Skip to main content

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

A fast, offline Flutter/Dart library for Uganda's administrative-unit hierarchy - village, parish, subcounty, constituency, and district lookup with fuzzy search, backed by a bundled SQLite database.

example/lib/main.dart

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:ug_locations/ug_locations.dart';

void main() {
  // Desktop platforms need the FFI-based sqflite implementation; Android/iOS
  // work out of the box with the default sqflite plugin.
  if (Platform.isLinux || Platform.isWindows || Platform.isMacOS) {
    sqfliteFfiInit();
    databaseFactory = databaseFactoryFfi;
  }
  runApp(const UgLocationsExampleApp());
}

class UgLocationsExampleApp extends StatelessWidget {
  const UgLocationsExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ug_locations example',
      theme: ThemeData(colorSchemeSeed: Colors.green, useMaterial3: true),
      home: const HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  UgandaLocations? _ug;
  List<UgandaLocation> _searchResults = <UgandaLocation>[];

  List<String> _districts = <String>[];
  List<String> _subcounties = <String>[];
  List<String> _parishes = <String>[];
  List<String> _villages = <String>[];

  String? _selectedDistrict;
  String? _selectedSubcounty;
  String? _selectedParish;

  @override
  void initState() {
    super.initState();
    _init();
  }

  Future<void> _init() async {
    final ug = await UgandaLocations.getInstance();
    final districts = await ug.getDistricts();
    setState(() {
      _ug = ug;
      _districts = districts;
    });
  }

  Future<void> _onSearchChanged(String query) async {
    final ug = _ug;
    if (ug == null || query.trim().isEmpty) {
      setState(() => _searchResults = <UgandaLocation>[]);
      return;
    }
    final results = await ug.search(query, limit: 20);
    setState(() => _searchResults = results);
  }

  Future<void> _onDistrictSelected(String? district) async {
    final ug = _ug;
    setState(() {
      _selectedDistrict = district;
      _selectedSubcounty = null;
      _selectedParish = null;
      _subcounties = <String>[];
      _parishes = <String>[];
      _villages = <String>[];
    });
    if (ug == null || district == null) return;
    final subcounties = await ug.getSubcountiesInDistrict(district);
    setState(() => _subcounties = subcounties);
  }

  Future<void> _onSubcountySelected(String? subcounty) async {
    final ug = _ug;
    setState(() {
      _selectedSubcounty = subcounty;
      _selectedParish = null;
      _parishes = <String>[];
      _villages = <String>[];
    });
    if (ug == null || subcounty == null || _selectedDistrict == null) return;
    final parishes = await ug.getParishesInSubcounty(_selectedDistrict!, subcounty);
    setState(() => _parishes = parishes);
  }

  Future<void> _onParishSelected(String? parish) async {
    final ug = _ug;
    setState(() {
      _selectedParish = parish;
      _villages = <String>[];
    });
    if (ug == null ||
        parish == null ||
        _selectedDistrict == null ||
        _selectedSubcounty == null) {
      return;
    }
    final villages = await ug.getVillagesInParish(
      _selectedDistrict!,
      _selectedSubcounty!,
      parish,
    );
    setState(() => _villages = villages);
  }

  @override
  Widget build(BuildContext context) {
    if (_ug == null) {
      return const Scaffold(body: Center(child: CircularProgressIndicator()));
    }

    return Scaffold(
      appBar: AppBar(title: const Text('ug_locations example')),
      body: DefaultTabController(
        length: 2,
        child: Column(
          children: [
            const TabBar(tabs: [Tab(text: 'Search'), Tab(text: 'Cascading selector')]),
            Expanded(
              child: TabBarView(
                children: [_buildSearchTab(), _buildSelectorTab()],
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildSearchTab() {
    return Padding(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          TextField(
            decoration: const InputDecoration(
              labelText: 'Search village / parish / subcounty / district',
              border: OutlineInputBorder(),
            ),
            onChanged: _onSearchChanged,
          ),
          const SizedBox(height: 16),
          Expanded(
            child: ListView.builder(
              itemCount: _searchResults.length,
              itemBuilder: (context, index) {
                final loc = _searchResults[index];
                return ListTile(
                  title: Text(loc.village),
                  subtitle: Text('${loc.parish} → ${loc.subcounty} → ${loc.district}'),
                );
              },
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildSelectorTab() {
    return Padding(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          DropdownButtonFormField<String>(
            initialValue: _selectedDistrict,
            decoration: const InputDecoration(labelText: 'District'),
            items: [
              for (final d in _districts.toSet())
                DropdownMenuItem(value: d, child: Text(d)),
            ],
            onChanged: _onDistrictSelected,
          ),
          DropdownButtonFormField<String>(
            initialValue: _selectedSubcounty,
            decoration: const InputDecoration(labelText: 'Subcounty'),
            items: [
              for (final s in _subcounties.toSet())
                DropdownMenuItem(value: s, child: Text(s)),
            ],
            onChanged: _subcounties.isEmpty ? null : _onSubcountySelected,
          ),
          DropdownButtonFormField<String>(
            initialValue: _selectedParish,
            decoration: const InputDecoration(labelText: 'Parish'),
            items: [
              // Source data can list the same parish name more than once
              // within a subcounty (e.g. ARUA / ARIVU has two entries) -
              // dedupe since dropdown items must have unique values.
              for (final p in _parishes.toSet())
                DropdownMenuItem(value: p, child: Text(p)),
            ],
            onChanged: _parishes.isEmpty ? null : _onParishSelected,
          ),
          const SizedBox(height: 16),
          Text('Villages (${_villages.length})', style: Theme.of(context).textTheme.titleMedium),
          Expanded(
            child: ListView.builder(
              itemCount: _villages.length,
              itemBuilder: (context, index) => ListTile(title: Text(_villages[index])),
            ),
          ),
        ],
      ),
    );
  }
}
7
likes
0
points
215
downloads

Publisher

unverified uploader

Weekly Downloads

A fast, offline Flutter/Dart library for Uganda's administrative-unit hierarchy - village, parish, subcounty, constituency, and district lookup with fuzzy search, backed by a bundled SQLite database.

Repository (GitHub)
View/report issues

Topics

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

License

unknown (license)

Dependencies

flutter, path, path_provider, sqflite

More

Packages that depend on ug_locations