ug_locations 0.3.0 copy "ug_locations: ^0.3.0" to clipboard
ug_locations: ^0.3.0 copied to clipboard

Offline Flutter/Dart library for Uganda's administrative-unit hierarchy: village, parish, subcounty, and district lookup with fuzzy search, backed by SQLite.

ug_locations #

A fast, offline Flutter/Dart library for Uganda's administrative hierarchy, forked from ug_locations, with data sourced from the uganda npm package by kakandemanwell (browsable at uganda-omega.vercel.app).

Search villages, get complete administrative paths, and traverse village → parish → subcounty → county → district — fully offline via a bundled SQLite database.

pub package License: MIT

Features #

  • Fully offline — bundled SQLite database, no network calls
  • Smart search — substring/prefix matching with relevance ranking
  • Complete hierarchy traversal across all administrative levels
  • Ready-made widgets — LocationPicker (cascading selector) and LocationSearchField (autocomplete) — no UI to hand-roll
  • Full null-safety, typed models

Installation #

flutter pub add ug_locations

Migrating from 0.1.x: UgandaLocation.constituency was renamed to county in 0.2.0 and now comes from a different data source (a county, not an electoral constituency) — update field access, there's no compatibility shim.

Quick Start #

import 'package:ug_locations/ug_locations.dart';

Future<void> main() async {
  final ug = await UgandaLocations.getInstance();

  final location = await ug.getLocationByVillage('KASAMBYA I');
  // UgandaLocation(village: KASAMBYA I, parish: KATEREIGA,
  //   subcounty: BUHANIKA, county: BUGAHYA COUNTY, district: HOIMA,
  //   region: WESTERN, subRegion: BUNYORO)

  print(await ug.getPath('KASAMBYA I'));
  // "HOIMA → BUHANIKA → KATEREIGA → KASAMBYA I"
}

Platform setup #

  • Android / iOS: works out of the box.

  • Desktop (Linux/macOS/Windows) or dart test: sqflite needs the FFI implementation. Initialize once before calling any method:

    import 'package:sqflite_common_ffi/sqflite_ffi.dart';
    
    void main() {
      sqfliteFfiInit();
      databaseFactory = databaseFactoryFfi;
      runApp(const MyApp());
    }
    
  • Web: works out of the box — no setup needed. sqflite has no built-in web backend, so on web the package transparently switches to a sqlite3-wasm backend instead, loading the bundled database into an in-memory filesystem on each page load.

Usage #

// Districts and hierarchy traversal
final districts = await ug.getDistricts();
final subcounties = await ug.getSubcountiesInDistrict('HOIMA');
final parishes = await ug.getParishesInSubcounty('HOIMA', 'BUHANIKA');
final villages = await ug.getVillagesInParish('HOIMA', 'BUHANIKA', 'KATEREIGA');
final parent = await ug.getParent('KASAMBYA I');
// UgandaLocationParent(parish: KATEREIGA, subcounty: BUHANIKA, district: HOIMA)

// Search — ranked, exact/prefix matches first
final results = await ug.search('KABANDA', limit: 5);
for (final loc in results) {
  print('${loc.village} (${loc.district})');
}

Common patterns this API supports: cascading district → subcounty → parish → village selectors, and village-name input validation via getLocationByVillage(name) != null. See example/lib/main.dart for a full working Flutter app — it has a "Bundled widgets" tab demonstrating both widgets below, alongside hand-rolled equivalents built directly on the API.

Region and sub-region lookups #

Every UgandaLocation also carries region and subRegion, and three lookup methods let you drive a region-first cascade if you want one:

final regions = await ug.getRegions(); // WESTERN, CENTRAL, EASTERN, NORTHERN
final subRegions = await ug.getSubRegionsInRegion('WESTERN'); // BUNYORO, ANKOLE, ...
final districts = await ug.getDistrictsInSubRegion('BUNYORO'); // HOIMA, ...

Widgets #

Two ready-made Flutter widgets ship alongside the lookup API, so you don't have to hand-roll cascading dropdowns or an autocomplete field yourself. Both are exported from the same package:ug_locations/ug_locations.dart import used above — no separate import needed. Both open the shared UgandaLocations instance automatically (via UgandaLocations.getInstance()); pass ug: someInstance to inject a specific instance instead, e.g. in tests.

LocationPicker #

A cascading District → Subcounty → Parish → Village selector. Four DropdownButtonFormFields, each populated from the previous selection; onSelected fires once a village is chosen, with the full resolved UgandaLocation.

LocationPicker(
  onSelected: (location) => print(location.village),
)

LocationPicker, default mode

Pass includeRegionHierarchy: true to prepend Region and Sub-region dropdowns above District, narrowing the District list to the chosen sub-region:

LocationPicker(
  includeRegionHierarchy: true,
  onSelected: (location) => print(location.village),
)

LocationPicker, with region hierarchy

Pass initialLocation to pre-select every dropdown, e.g. when editing a record that already has a saved location. Each level's options are loaded so the seeded value is valid; onSelected is not called for it, since the caller already has the value:

LocationPicker(
  onSelected: (location) => print(location.village),
  initialLocation: existingLocation, // a UgandaLocation
)

LocationSearchField #

A text field that searches villages, parishes, subcounties, and districts as the user types (via UgandaLocations.search), showing a ranked suggestions list to pick from.

LocationSearchField(
  onSelected: (location) => print(location.village),
  limit: 5, // suggestions fetched per keystroke, default 3
)

LocationSearchField suggestions

Additional params for common cases:

LocationSearchField(
  onSelected: (location) => print(location.village),

  // Seed the field with a saved value, e.g. when editing an existing record.
  initialValue: const TextEditingValue(text: 'KASAMBYA I'),

  // Manual fallback for free text that doesn't match any suggestion
  // (real gap for rural/offline data not yet in the dataset).
  onTextChanged: (text) => print('typed: $text'),

  // Delay searches until typing pauses, instead of querying on every
  // keystroke. Defaults to null (no debounce).
  debounceDuration: const Duration(milliseconds: 300),
)

Migrating from earlier versions: on both widgets, the locations: Future<UgandaLocations>? param is deprecated in favor of ug: UgandaLocations?, which takes an already-resolved instance instead of a future — a more conventional shape for dependency injection. locations still works but will be removed in a future release.

API Reference #

Method Returns Description
UgandaLocations.getInstance() Future<UgandaLocations> Opens (and caches) the shared database instance
getDistricts() Future<List<String>> Returns all 146 districts
getRegions() Future<List<String>> Returns all 4 regions
getSubRegionsInRegion(region) Future<List<String>> Sub-regions in a region
getDistrictsInSubRegion(subRegion) Future<List<String>> Districts in a sub-region
getLocationByVillage(village) Future<UgandaLocation?> Full hierarchy for a village
getPath(village) Future<String?> Formatted path: "District → Subcounty → Parish → Village"
search(query, {limit = 50}) Future<List<UgandaLocation>> Search across all levels, ranked by relevance
getSubcountiesInDistrict(district) Future<List<String>> Subcounties in a district
getParishesInSubcounty(district, subcounty) Future<List<String>> Parishes in a subcounty
getVillagesInParish(district, subcounty, parish) Future<List<String>> Villages in a parish
getParent(village) Future<UgandaLocationParent?> Parent parish/subcounty/district of a village

Types #

class UgandaLocation {
  final String village;
  final String parish;
  final String subcounty;
  final String? county;
  final String district;
  final String? region;
  final String? subRegion;
}

class UgandaLocationParent {
  final String parish;
  final String subcounty;
  final String district;
}

Data #

146 districts, 52,000+ unique villages across 71,000+ administrative records, covering the full village → parish → subcounty → county → district → sub-region → region hierarchy. Sourced from the uganda npm package's dataset (also browsable at uganda-omega.vercel.app).

Data freshness: this replaces the package's original static snapshot of the July 2022 Electoral Commission list (145 districts, no region/sub-region data). Regenerating the bundled database from a newer source is a two-step, manual process — see skills/ug_locations/SKILL.md — there is still no automatic update mechanism. If you need data fresher than what's bundled here, check whether uganda/uganda-omega.vercel.app has since published an update.

Acknowledgments #

  • Dart/Flutter port of the ug-locations npm package by Natumanya Guy, reimplemented with a SQLite-backed storage layer.
  • Administrative-unit data from the uganda npm package by kakandemanwell (uganda-omega.vercel.app) — also a good source to check for more current data than what's bundled here.

Contributing #

Contributions welcome — please open a Pull Request.

License #

MIT

7
likes
160
points
308
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Offline Flutter/Dart library for Uganda's administrative-unit hierarchy: village, parish, subcounty, and district lookup with fuzzy search, backed by SQLite.

Repository (GitHub)
View/report issues

Topics

#uganda #locations #administrative-units #sqlite #offline

License

MIT (license)

Dependencies

flutter, path, path_provider, sqflite, sqlite3, typed_data

More

Packages that depend on ug_locations