A comprehensive Flutter library for Indonesia location data. Complete data for all 38 provinces, 514 cities/regencies, 7,265 districts (kecamatan), 83,202 villages (kelurahan/desa), and 83,762 postal codes.
- Complete Data: All Indonesian administrative regions from province to village level
- Offline: All data is embedded, no network required
- Fast Lookup: O(1) lookup by ID using HashMap indexes
- Smart Search: Typo-tolerant search using Levenshtein distance algorithm
- Type-Safe: Full Dart null safety and type checking
- Well-Tested: Comprehensive unit tests
- BPS Standard: Uses official Badan Pusat Statistik (BPS) codes
| Type | Count |
|---|---|
| Provinces | 38 |
| Cities/Regencies | 514 |
| Districts (Kecamatan) | 7,265 |
| Villages (Kelurahan/Desa) | 83,202 |
| Postal Codes | 83,762 |
Add this to your package's pubspec.yaml file:
dependencies:
nusantara_data: ^1.0.0Then run:
flutter pub getCall initialize() once at app startup:
import 'package:nusantara_data/nusantara_data.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize the library
await NusantaraData.initialize();
runApp(MyApp());
}// Get all provinces
final provinces = NusantaraData.getAllProvinces();
// Returns: [ProvinceSummary(id: "11", name: "Aceh"), ...]
// Get province by ID (BPS code)
final jakarta = NusantaraData.getProvinceById('31');
// Returns: Province(id: "31", name: "DKI Jakarta", cities: [...])
// Get province by name
final jawaBarat = NusantaraData.getProvinceByName('Jawa Barat');
// Search with typo tolerance
final results = NusantaraData.searchProvinces('Jakrta');
// Finds "DKI Jakarta" despite typo// Get all cities
final cities = NusantaraData.getAllCities();
// Get cities in a province
final jakartaCities = NusantaraData.getCitiesByProvinceId('31');
// Get city by ID
final jakartaPusat = NusantaraData.getCityById('3171');
// Search cities
final results = NusantaraData.searchCities('bandung');
// Search within a province
final results = NusantaraData.searchCities('jakarta', provinceId: '31');// Get all districts
final districts = NusantaraData.getAllDistricts();
// Get districts in a city
final jakpusDistricts = NusantaraData.getDistrictsByCityId('3171');
// Get district by ID
final gambir = NusantaraData.getDistrictById('317101');
// Search districts
final results = NusantaraData.searchDistricts('menteng');// Get villages in a district
final villages = NusantaraData.getVillagesByDistrictId('317101');
// Get village by ID
final village = NusantaraData.getVillageById('3171012001');
// Search villages (districtId required for performance)
final results = NusantaraData.searchVillages('cideng', districtId: '317101');// Get postal codes for a village
final codes = NusantaraData.getPostalCodesByVillageId('3171012001');
// Returns: ['10110']
// Get postal codes for a district
final codes = NusantaraData.getPostalCodesByDistrictId('317101');
// Reverse lookup - find location by postal code
final locations = NusantaraData.getLocationByPostalCode('10110');
for (final location in locations) {
print(location.getFullAddress());
// "Gambir, Gambir, Jakarta Pusat, DKI Jakarta 10110"
}// Get version
final version = NusantaraData.getVersion();
// Returns: "1.0.0"
// Get last updated date
final date = NusantaraData.getLastUpdated();
// Returns: "2026-01-29"
// Get statistics
final stats = NusantaraData.getStatistics();
print('Provinces: ${stats.provinceCount}'); // 38
print('Cities: ${stats.cityCount}'); // 514
print('Districts: ${stats.districtCount}'); // 7,265
print('Villages: ${stats.villageCount}'); // 83,202
print('Postal Codes: ${stats.postalCodeCount}'); // 83,762All IDs follow the BPS (Badan Pusat Statistik) standard:
| Level | Digits | Example | Description |
|---|---|---|---|
| Province | 2 | 31 |
DKI Jakarta |
| City | 4 | 3171 |
Kota Jakarta Pusat |
| District | 6 | 317101 |
Gambir |
| Village | 10 | 3171012001 |
Kelurahan Gambir |
| Postal Code | 5 | 10110 |
Postal code |
class Province {
final String id; // BPS code (2 digits)
final String name; // e.g., "DKI Jakarta"
final List<City> cities;
}
class ProvinceSummary {
final String id;
final String name;
final int cityCount;
}enum CityType { kota, kabupaten }
class City {
final String id; // BPS code (4 digits)
final String provinceId;
final String name; // e.g., "Kota Jakarta Pusat"
final CityType type;
final List<District> districts;
}class District {
final String id; // BPS code (6 digits)
final String cityId;
final String name; // e.g., "Gambir"
final List<Village> villages;
}class Village {
final String id; // BPS code (10 digits)
final String districtId;
final String name;
final List<String> postalCodes;
}class PostalCodeDetail {
final String postalCode;
final String villageId;
final String villageName;
final String districtId;
final String districtName;
final String cityId;
final String cityName;
final CityType cityType;
final String provinceId;
final String provinceName;
String getFullAddress(); // Returns formatted address
}The library includes typo-tolerant search using the Levenshtein distance algorithm:
// Typo in "Jakarta" -> "Jakrta"
final results = NusantaraData.searchProvinces('Jakrta');
// Finds "DKI Jakarta"
// Typo in "Bandung" -> "Bandug"
final results = NusantaraData.searchCities('Bandug');
// Finds "Kota Bandung", "Kabupaten Bandung", etc.- Exact Match: Highest priority for exact substring matches
- Word-Start Match: High priority for matches at word boundaries
- Fuzzy Match: Uses Levenshtein distance for typo tolerance
- Adaptive Tolerance: Shorter queries get less tolerance
import 'package:nusantara_data/nusantara_data.dart';
// Calculate Levenshtein distance
final distance = FuzzySearch.levenshteinDistance('hello', 'hallo');
// Returns: 1
// Calculate similarity ratio (0.0 to 1.0)
final ratio = FuzzySearch.similarityRatio('hello', 'hello');
// Returns: 1.0
// Rank items by similarity
final items = ['Jakarta', 'Bandung', 'Surabaya'];
final ranked = FuzzySearch.rankBySimilarity(
query: 'jakrta',
items: items,
selector: (item) => item,
minSimilarity: 0.6,
);
// Returns: ['Jakarta']import 'package:nusantara_data/nusantara_data.dart';
// After initialization
final repository = NusantaraData.repository; // Access internal repository- Kepmendagri No 300.2.2-2138 Tahun 2025 - Official Indonesian government regulation
- Badan Pusat Statistik (BPS) - Official Statistics Indonesia codes
- cahyadsn/wilayah - GitHub Repository
- cahyadsn/wilayah_kodepos - Postal Code Data
- O(1) Lookup: ID lookups use HashMap for constant-time access
- Lazy Loading: Data loaded on first
initialize()call - Cached: Subsequent access uses cached data
- Indexed: Pre-built indexes for fast hierarchical queries
- Flutter SDK >= 3.10.0
- Dart SDK >= 3.0.0
Copyright 2026 Naufal Prakoso
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Contributions are welcome! Please read our Contributing Guide first.
See CHANGELOG.md for version history.