nepali_address_plus

Nepal's complete administrative division data — Province, District, Local Level and Ward, plus the legacy Zone hierarchy — with synchronous lookup utilities and zero dependencies.

 Province
      └── District
            └── Local Level (Metropolitan / Sub-Metro / Municipality / Rural Municipality)
                    └── Ward
  • Zero dependencies. Pure Dart, dart:core only.
  • Synchronous. Every lookup is an in-memory call over const data — no Future, no asset loading, no FutureBuilder in your widget tree.
  • Runs everywhere. Flutter (all platforms), Dart CLI tools, Dart servers.
  • Bilingual. English and Devanagari (नेपाली) names.
  • Legacy-aware. Resolves pre-2015 zone records onto today's structure.

Installation

dart pub add nepali_address_plus

or for Flutter:

flutter pub add nepali_address_plus

Then:

import 'package:nepali_address_plus/nepali_address_plus.dart';

Quick start

import 'package:nepali_address_plus/nepali_address_plus.dart';

void main() {
  // Walk the hierarchy downward.
  final bagmati = NepaliAddress.province(3)!;
  print('${bagmati.nameEn} (${bagmati.nameNp}) — capital ${bagmati.capital}');

  final districts = NepaliAddress.districtsByProvince(bagmati.id);
  print('${districts.length} districts');

  final kathmandu = NepaliAddress.district(28)!;
  final levels = NepaliAddress.localLevelsByDistrict(kathmandu.id);
  print('${kathmandu.nameEn}: ${levels.length} local levels');

  // Resolve a legacy zone record forward onto today's districts.
  final old = NepaliAddress.districtsByZone(5); // Bagmati Zone
  print(old.map((d) => d.nameEn).join(', '));
}

Building an address form? See Example app below for a complete cascading picker.

API

Everything hangs off NepaliAddress, a static-only class. No instantiation, no setup, no initialization call.

Member Returns Description
provinces List<Province> All 7 provinces
province(int id) Province? Province by id (1–7), or null
districts List<District> All 77 districts
district(int id) District? District by id (1–77), or null
districtsByProvince(int provinceId) List<District> Districts in a province
zones List<Zone> All 14 legacy zones
zone(int id) Zone? Zone by id (1–14), or null
districtsByZone(int zoneId) List<District> Districts that belonged to a zone
localLevels List<LocalLevel> All 755 local levels
localLevel(int id) LocalLevel? Local level by id, or null
localLevelsByDistrict(int districtId) List<LocalLevel> Local levels in a district
wardsByLocalLevel(int localLevelId) List<Ward> Wards, derived from wardCount

Models

Type Fields
Province id, nameEn, nameNp, capital, districtIds
District id, nameEn, nameNp, provinceId, zoneId, headquarter
Zone id, nameEn, nameNp, districtIds
LocalLevel id, nameEn, nameNp?, type, districtId, wardCount?, wardNumbers
LocalLevelType enum: metropolitanCity, subMetropolitanCity, municipality, ruralMunicipality — each with labelEn / labelNp
Ward number, localLevelId

Store ids, display names

Persist districtId: 28, not "Kathmandu". Ids are stable across releases; a spelling correction in a future version won't invalidate saved records.

Data status

Tier Count Status
Zone 14 / 14 Complete, cross-checked for id coverage
Province 7 / 7 Complete
District 77 / 77 Complete, cross-checked for province + zone consistency
Local Level 755 / 755 Name, type and district assignment complete and verified
Local Level — Devanagari names 0 / 755 Not yet sourcednameNp is null
Ward counts 0 / 755 Not yet sourcedwardCount is null
Ward derived Available once wardCount is populated

Read this before calling wardsByLocalLevel.

nameNp and wardCount on LocalLevel are null for every entry today, so wardsByLocalLevel() currently returns an empty list. That is not a bug and it does not mean the place has no wards — it means the value hasn't been verified against an official source yet.

These fields are deliberately left null rather than guessed. Check for null and show your own fallback:

final level = NepaliAddress.localLevel(322)!;
if (level.wardCount == null) {
  // Not yet sourced — let the user type a ward number instead.
} else {
  final wards = NepaliAddress.wardsByLocalLevel(level.id);
}

Local level name/type/district data was derived from official administrative boundary data and cross-matched against this package's 77-district list with zero unmatched rows and zero districts left without a local level. Type counts (6 metropolitan / 11 sub-metropolitan / 277 municipality / 461 rural municipality = 755) match the source's published statistics exactly.

Devanagari names and ward counts still need a verification pass against MoFAGA and the Election Commission of Nepal.

Why "Zone" is included

Zones (अञ्चल) were abolished in 2015, but older citizenship certificates, land records and legacy databases still reference them. NepaliAddress.districtsByZone(id) resolves those old records forward onto the current province/district structure, so you don't have to maintain a second lookup table yourself.

Example app

A Flutter demo lives in example/ — a cascading Province → District → Local Level → Ward picker, a legacy-zone migration lookup, and a dataset coverage screen.

cd example
flutter pub get
flutter run                    # or: flutter run -d chrome
dart run bin/cli_demo.dart     # pure Dart, no Flutter needed

Contributing

Found an inaccurate name, headquarters or mapping? Please open an issue — correctness here matters more than completeness speed.

The dataset is authored as JSON and the Dart is generated from it. See CONTRIBUTING.md for the data format and the workflow for submitting a fix.

License

MIT — see LICENSE.

Libraries

nepali_address_plus
Comprehensive, zero-dependency Nepal administrative division data: Zone (legacy) → Province → District → Local Level → Ward.