bd_locations

pub package pub points License: MIT Dart 3

A fast, zero-dependency, 100% offline geographic dataset of Bangladesh in both Bengali and English. Built for Flutter and Dart applications requiring administrative hierarchy navigation, bilingual search, postal code validation, address formatting, and coordinate-based location lookup.


Dataset Summary

Administrative Unit Count Model Class Bilingual Names Coordinates Parent Reference
Divisions 8 Division English & Bengali Latitude & Longitude —
Districts 64 District English & Bengali Latitude & Longitude divisionId
Upazilas / Thanas 495 Upazila English & Bengali — districtId
Unions 4,540 Union English & Bengali — upazilaId
Post Offices / Postcodes 1,349 PostOffice English & Bengali digits — districtId, divisionId

Key Features

  • Zero Async Overhead: All records are compiled as const Dart objects. No asset bundling, file I/O, or Future/async initialization is required.
  • Zero External Dependencies: Pure Dart implementation compatible with Android, iOS, Web, macOS, Windows, Linux, and Dart server/CLI environments.
  • 4-Tier Relational Hierarchy: Traverse seamlessly between Divisions, Districts, Upazilas, Unions, and Post Offices using intuitive extension getters.
  • Bilingual & Legacy-Spelling Search: Query locations in Bengali (ঢাকা, চট্টগ্রাম, সাভার) or English—including automatic normalization for common legacy spellings (Chittagong -> Chattogram, Jessore -> Jashore, Comilla -> Cumilla, Bogra -> Bogura, Barisal -> Barishal).
  • Postal Code Lookup & Validation: Search and validate 4-digit Bangladesh postal codes in both English (1207) and Bengali (১২০৭) digits.
  • Bilingual Address Formatter: Generate formatted address strings in either Bengali or English with a single method call.
  • Geolocation Utilities: Compute great-circle distances and identify the nearest District or Division from any (latitude, longitude) pair using the Haversine formula.

Installation

Run the following command in your project terminal:

# For Flutter projects
flutter pub add bd_locations

# For Pure Dart projects
dart pub add bd_locations

Or add bd_locations manually to your pubspec.yaml:

dependencies:
  bd_locations: ^0.2.0

Then import the package:

import 'package:bd_locations/bd_locations.dart';

Usage Guide

1. Accessing Complete Datasets and Lookup by ID

All datasets are available synchronously as immutable lists:

final divisions = BdLocations.divisions;     // 8 Divisions
final districts = BdLocations.districts;     // 64 Districts
final upazilas = BdLocations.upazilas;       // 495 Upazilas
final unions = BdLocations.unions;           // 4,540 Unions
final postOffices = BdLocations.postOffices; // 1,349 Post Offices

// Direct lookup by ID
final dhakaDivision = BdLocations.getDivisionById(6);
final dhakaDistrict = BdLocations.getDistrictById(47);
final savarUpazila = BdLocations.getUpazilaById(365);
final firstUnion = BdLocations.getUnionById(1);

2. Relational Navigation (Top-Down and Bottom-Up)

Use built-in relational extensions to navigate down or up the administrative hierarchy without manual filtering:

// Top-down navigation: Division -> Districts -> Upazilas -> Unions
final sylhetDivision = BdLocations.getDivisionById(5)!;
final sylhetDistricts = sylhetDivision.districts;       // 4 Districts
final sylhetUpazilas = sylhetDivision.upazilas;         // All Upazilas in Sylhet Division
final sylhetPostOffices = sylhetDivision.postOffices;   // All Post Offices in Sylhet Division

final sylhetSadar = sylhetDistricts.first.upazilas.first;
final unionsInUpazila = sylhetSadar.unions;             // All Unions in this Upazila

// Bottom-up navigation: Union -> Upazila -> District -> Division
final union = unionsInUpazila.first;
print(union.nameEn);            // Union name
print(union.upazila.nameEn);    // Parent Upazila name
print(union.district.nameEn);   // Parent District name
print(union.division.nameEn);   // Parent Division name

Search across any administrative level using Bengali text, official English names, or legacy English spellings:

// Search by legacy English spelling
final jashore = BdLocations.searchDistricts('Jessore').first;
print('${jashore.nameEn} (${jashore.nameBn})'); // Jashore (যশোর)

final cumilla = BdLocations.searchDistricts('Comilla').first;
print('${cumilla.nameEn} (${cumilla.nameBn})'); // Cumilla (কুমিল্লা)

// Search in Bengali
final savar = BdLocations.searchUpazilas('সাভার').first;
print('${savar.nameEn} - ${savar.district.nameEn}'); // Savar - Dhaka

// Scoped search (e.g., search Upazilas within a specific District)
final dhakaUpazilas = BdLocations.searchUpazilas('Dhamrai', districtId: 47);

// Search Unions
final unions = BdLocations.searchUnions('তেঁতুলঝোড়া');

Supported Legacy Spelling Aliases

Legacy / Alternative Query Resolved Official Name
Chittagong, Chattagram Chattogram
Comilla Cumilla
Bogra Bogura
Jessore Jashore
Barisal Barishal
Coxs Bazar, Coxsbazar Cox's Bazar
Chapai Nawabganj, Nawabganj Chapainawabganj
Jhalokati, Jhalokathi Jhalakathi
Netrakona Netrokona
Khagrachari Khagrachhari
Laxmipur, Laksmipur Lakshmipur
Maulvibazar Moulvibazar
Bbaria Brahmanbaria

4. Postal Code Lookup, Search, and Form Validation

Look up or validate 4-digit postal codes using either English (1207) or Bengali (১২০৭) numerals:

// Validate postal codes in forms
print(BdLocations.isValidPostcode('1207')); // true
print(BdLocations.isValidPostcode('১২০৭')); // true
print(BdLocations.isValidPostcode('9999')); // false

// Find post offices by postal code
final offices = BdLocations.findByPostcode('১২০৭');
for (final po in offices) {
  print('${po.subOfficeEn} (${po.postcode}) - ${po.district.nameEn}');
  // Mohammadpur Housing (1207) - Dhaka
}

// Search post offices by sub-office name, thana, or code
final bananiOffices = BdLocations.searchPostOffices('Banani');
print(bananiOffices.first.postcode);   // 1213
print(bananiOffices.first.postcodeBn); // ১২১৩

5. Bilingual Address Formatting

Generate human-readable addresses from District, Upazila, Union, or PostOffice instances:

final savar = BdLocations.searchUpazilas('Savar').first;
print(savar.formatAddress(inBangla: false));
// Output: Savar, Dhaka, Dhaka Division

print(savar.formatAddress(inBangla: true));
// Output: সাভার, ঢাকা, ঢাকা বিভাগ

final union = savar.unions.first;
print(union.formatAddress(inBangla: true));
// Output: সাভার, সাভার, ঢাকা

final postOffice = BdLocations.findByPostcode('1207').first;
print(postOffice.formatAddress(inBangla: false));
// Output: Mohammadpur Housing (1207), Mohammadpur, Dhaka

6. Geolocation and Distance Calculation

Find the closest District or Division to a user's GPS coordinates or calculate the distance in kilometers between two coordinates:

// Find nearest District and Division
final nearestDistrict = BdLocations.findNearestDistrict(21.4272, 92.0058);
print(nearestDistrict.nameEn); // Cox's Bazar

final nearestDivision = BdLocations.findNearestDivision(24.8949, 91.8687);
print(nearestDivision.nameEn); // Sylhet

// Calculate distance between Dhaka and Chattogram in kilometers
final distanceKm = BdLocations.distanceInKm(
  23.8103, 90.4125, // Dhaka
  22.3569, 91.7832, // Chattogram
);
print('${distanceKm.toStringAsFixed(1)} km'); // ~214.2 km

7. JSON Serialization

Every model (Division, District, Upazila, Union, and PostOffice) supports toJson() and fromJson() for local storage or API payloads:

final district = BdLocations.getDistrictById(47)!;
final Map<String, dynamic> jsonMap = district.toJson();
final District restored = District.fromJson(jsonMap);

Flutter 4-Level Cascading Dropdown Example

Below is a complete Flutter widget demonstrating a cascading Division -> District -> Upazila -> Union selector:

import 'package:bd_locations/bd_locations.dart';
import 'package:flutter/material.dart';

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

  @override
  State<BdAddressPicker> createState() => _BdAddressPickerState();
}

class _BdAddressPickerState extends State<BdAddressPicker> {
  Division? selectedDivision;
  District? selectedDistrict;
  Upazila? selectedUpazila;
  Union? selectedUnion;

  @override
  Widget build(BuildContext context) {
    final districts = selectedDivision?.districts ?? const [];
    final upazilas = selectedDistrict?.upazilas ?? const [];
    final unions = selectedUpazila?.unions ?? const [];

    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        DropdownButtonFormField<Division>(
          value: selectedDivision,
          hint: const Text('Select Division (বিভাগ নির্বাচন করুন)'),
          items: BdLocations.divisions.map((div) {
            return DropdownMenuItem(
              value: div,
              child: Text('${div.nameBn} (${div.nameEn})'),
            );
          }).toList(),
          onChanged: (division) {
            setState(() {
              selectedDivision = division;
              selectedDistrict = null;
              selectedUpazila = null;
              selectedUnion = null;
            });
          },
        ),
        const SizedBox(height: 12),
        DropdownButtonFormField<District>(
          value: selectedDistrict,
          hint: const Text('Select District (জেলা নির্বাচন করুন)'),
          items: districts.map((dist) {
            return DropdownMenuItem(
              value: dist,
              child: Text('${dist.nameBn} (${dist.nameEn})'),
            );
          }).toList(),
          onChanged: (district) {
            setState(() {
              selectedDistrict = district;
              selectedUpazila = null;
              selectedUnion = null;
            });
          },
        ),
        const SizedBox(height: 12),
        DropdownButtonFormField<Upazila>(
          value: selectedUpazila,
          hint: const Text('Select Upazila (উপজেলা নির্বাচন করুন)'),
          items: upazilas.map((upa) {
            return DropdownMenuItem(
              value: upa,
              child: Text('${upa.nameBn} (${upa.nameEn})'),
            );
          }).toList(),
          onChanged: (upazila) {
            setState(() {
              selectedUpazila = upazila;
              selectedUnion = null;
            });
          },
        ),
        const SizedBox(height: 12),
        DropdownButtonFormField<Union>(
          value: selectedUnion,
          hint: const Text('Select Union (ইউনিয়ন নির্বাচন করুন)'),
          items: unions.map((uni) {
            return DropdownMenuItem(
              value: uni,
              child: Text('${uni.nameBn} (${uni.nameEn})'),
            );
          }).toList(),
          onChanged: (union) {
            setState(() => selectedUnion = union);
          },
        ),
      ],
    );
  }
}

API Reference

BdLocations Static Methods and Getters

Member Return Type Description
BdLocations.divisions List<Division> Returns all 8 divisions of Bangladesh.
BdLocations.districts List<District> Returns all 64 districts of Bangladesh.
BdLocations.upazilas List<Upazila> Returns all 495 upazilas of Bangladesh.
BdLocations.unions List<Union> Returns all 4,540 unions of Bangladesh.
BdLocations.postOffices List<PostOffice> Returns all 1,349 post offices and postcodes.
BdLocations.getDivisionById(id) Division? Finds a division by its ID (1..8).
BdLocations.getDistrictById(id) District? Finds a district by its ID (1..64).
BdLocations.getUpazilaById(id) Upazila? Finds an upazila by its ID (1..495).
BdLocations.getUnionById(id) Union? Finds a union by its ID.
BdLocations.getDistrictsByDivision(divisionId) List<District> Returns all districts in a division.
BdLocations.getUpazilasByDistrict(districtId) List<Upazila> Returns all upazilas in a district.
BdLocations.getUpazilasByDivision(divisionId) List<Upazila> Returns all upazilas in a division.
BdLocations.getUnionsByUpazila(upazilaId) List<Union> Returns all unions in an upazila.
BdLocations.getUnionsByDistrict(districtId) List<Union> Returns all unions in a district.
BdLocations.getPostOfficesByDistrict(districtId) List<PostOffice> Returns all post offices in a district.
BdLocations.getPostOfficesByDivision(divisionId) List<PostOffice> Returns all post offices in a division.
BdLocations.findByPostcode(postcode) List<PostOffice> Finds post offices by English or Bengali 4-digit code.
BdLocations.isValidPostcode(postcode) bool Checks if a 4-digit postcode exists in Bangladesh.
BdLocations.searchDivisions(query) List<Division> Searches divisions in Bengali or English.
BdLocations.searchDistricts(query, {divisionId}) List<District> Searches districts in Bengali or English.
BdLocations.searchUpazilas(query, {districtId}) List<Upazila> Searches upazilas in Bengali or English.
BdLocations.searchUnions(query, {upazilaId}) List<Union> Searches unions in Bengali or English.
BdLocations.searchPostOffices(query, {districtId}) List<PostOffice> Searches post offices by code, sub-office, or upazila.
BdLocations.findNearestDistrict(lat, lng) District Finds the nearest district to (lat, lng).
BdLocations.findNearestDivision(lat, lng) Division Finds the nearest division to (lat, lng).
BdLocations.distanceInKm(lat1, lng1, lat2, lng2) double Calculates Haversine distance in kilometers.

Contributing

Contributions, bug reports, and dataset improvements are welcome! Please open an issue or submit a pull request on the GitHub Repository.

License

This project is licensed under the MIT License.

Libraries

bd_locations
Complete offline geographic dataset of Bangladesh (Divisions, Districts, Upazilas, Unions, and Post Offices/Postcodes) in both Bengali and English with fast search and geolocation utilities.