Thai address autocomplete search screen Thai address cascading dropdown screen Thai administrative boundary map preview

SearchDropdownMap Preview

thai_address_plus

pub.dev License: MIT

Flutter SDK สำหรับ Thailand Geographic API — จังหวัด / อำเภอ / ตำบล / หมู่บ้าน / GeoJSON / Reverse-geocode / Autocomplete

ต้องการเรียก API โดยตรงนอก Flutter? นำเข้า Postman collection จาก docs/geo-api.postman_collection.json แล้วตั้งค่า base_url เป็น https://api.kidpech.app เพื่อเริ่มยิง request ได้ทันที


Features

หมวด รายละเอียด
Admin lookup Provinces, Districts, Sub-districts, Villages
Search Fuzzy full-text search + prefix autocomplete
Spatial Reverse geocode, batch reverse, within-radius, bounding box
GeoJSON ขอบเขตรายหน่วย, FeatureCollection, country boundary
Vector tiles URL template สำหรับ MapLibre / Mapbox
Network Gzip อัตโนมัติ, retry (429 + Retry-After / 5xx), in-memory cache
Widget ThaiAddressSearchField — debounced typeahead พร้อมใช้

Installation

dependencies:
  thai_address_plus: ^1.0.0
flutter pub get

Documentation & API Tools

thai_address_plus ครอบฝั่ง Flutter SDK และมีไฟล์สำหรับทีมที่ต้องการทดสอบ API ตรงผ่าน HTTP client ด้วย

  1. Import docs/geo-api.postman_collection.json เข้า Postman
  2. ตั้ง collection variable base_url เป็น https://api.kidpech.app
  3. เรียกใช้ Thailand Geographic API ได้ทันทีโดยไม่ต้องมี authentication

Resources เพิ่มเติม:


Quick Start

import 'package:thai_address_plus/thai_address_plus.dart';

final geo = ThaiGeoApi();

// ดึง 77 จังหวัด (cache 24h อัตโนมัติ)
final provinces = await geo.listProvinces();

// ดึงตำบล + หมู่บ้าน + GeoJSON ใน 1 คำสั่ง
final detail = await geo.subDistrictWithVillages('TH100101');
print(detail.subDistrict.nameTh);   // ตำบลพระบรมมหาราชวัง
print(detail.villages.length);      // จำนวนหมู่บ้าน

Usage

Provinces

// ทุกจังหวัด
final all = await geo.listProvinces();

// กรองตามภาค
final north = await geo.listProvinces(region: 'north');

// จังหวัดที่มีทะเล
final coastal = await geo.listProvinces(isCoastal: true);

// ดึงจังหวัดเดียว
final bkk = await geo.getProvince('TH10');

// จังหวัดเพื่อนบ้าน (depth 1 = ติดกัน, depth 2 = ห่าง 2 ชั้น)
final neighbors = await geo.listProvinceNeighbors('TH10', depth: 1);

// อำเภอในจังหวัด
final districts = await geo.listDistrictsOfProvince('TH10');

Districts & Sub-districts

// ข้อมูลอำเภอ
final district = await geo.getDistrict('TH1001');

// ตำบลในอำเภอ
final subDistricts = await geo.listSubDistrictsOfDistrict('TH1001');

// ตำบลเดียว
final tambon = await geo.getSubDistrict('TH100101');

// ตำบลแบบกรองหลายเงื่อนไข
final islands = await geo.listSubDistricts(isIsland: true, limit: 20);

// ตำบลตามรหัสไปรษณีย์
final byZip = await geo.listByZip('10200');

Villages

// หมู่บ้านในตำบล (ใช้กับ dropdown 4 ระดับ)
final villages = await geo.listVillagesOfSubDistrict('TH100101');

// หมู่บ้านเดียว
final village = await geo.getVillage(12345);

Reverse Geocode

// จุดเดียว
final result = await geo.reverse(13.7563, 100.5018);
print(result.subDistrictNameTh);

// Batch (≤ 1 000 จุด/request, rate limit 10/min)
final results = await geo.reverseBatch([
  (lat: 13.7563, lng: 100.5018),
  (lat: 18.7883, lng: 98.9853),
]);

Search & Autocomplete

// Fuzzy full-text search (ต้องการ ≥ 2 ตัวอักษร)
final hits = await geo.search('สีลม', level: 'sub_district');

// Prefix autocomplete (เร็วกว่า, แนะนำ debounce 200-300ms)
final suggestions = await geo.autocomplete('สีล', limit: 8);
for (final h in suggestions) {
  print('${h.pcode}  ${h.displayTh}');
}

Spatial Queries

// หน่วยการปกครองในรัศมี 5 กม.
final nearby = await geo.within(
  lat: 13.7563, lng: 100.5018,
  radiusKm: 5.0,
  level: 'sub_district',
);

// Bounding box
final inBox = await geo.bbox(
  minLng: 100.4, minLat: 13.6,
  maxLng: 100.6, maxLat: 13.9,
  level: 'district',
);

GeoJSON & Map Tiles

// ขอบเขต GeoJSON ของตำบล
final feature = await geo.geometryOf(GeoLevel.subDistrict, 'TH100101');

// FeatureCollection ทุกจังหวัด
final fc = await geo.featureCollection(GeoLevel.province);

// ขอบเขตประเทศไทย
final country = await geo.country(simplified: true);

// Vector tile URL สำหรับ MapLibre
final tileUrl = geo.tilesUrlTemplate(GeoLevel.subDistrict);
// → "https://api.kidpech.app/api/v1/geo/tiles/sub_districts/{z}/{x}/{y}.mvt"

ThaiAddressSearchField Widget

Widget สำเร็จรูปพร้อม debounce + cancel token — วางได้ทันทีในฟอร์ม:

ThaiAddressSearchField(
  api: ThaiGeoApi(),
  level: 'sub_district',  // 'province' | 'district' | 'sub_district' | null (all)
  lang: 'th',
  limit: 8,
  minLength: 2,
  debounce: Duration(milliseconds: 250),
  decoration: InputDecoration(
    labelText: 'ค้นหาตำบล / อำเภอ / จังหวัด',
    prefixIcon: Icon(Icons.search),
  ),
  onSelected: (hit) {
    debugPrint('${hit.pcode}  ${hit.displayTh}');
  },
  onError: (e) => debugPrint('Error: $e'),
)

Custom Configuration

final geo = ThaiGeoApi(
  config: ThaiGeoConfig(
    baseUrl: 'https://api.kidpech.app/api/v1/geo',
    connectTimeout: Duration(seconds: 10),
    maxRetries: 5,
    enableLogging: true,              // print request/response
    defaultStaleTime: Duration(hours: 12),
    autocompleteStaleTime: Duration(minutes: 3),
  ),
);

Custom Cache Store

class MyHiveCache implements CacheStore {
  // implement get / set / remove / clear
}

final geo = ThaiGeoApi(cacheStore: MyHiveCache());

Inject Mock Dio (for testing)

final mockDio = MockDio();
final client = ThaiGeoClient(dio: mockDio);
final geo = ThaiGeoApi(client: client);

Error Handling

try {
  final provinces = await geo.listProvinces();
} on GeoApiException catch (e) {
  switch (e.code) {
    case GeoErrorCode.network:
      print('No internet: ${e.message}');
    case GeoErrorCode.rateLimited:
      print('Rate limited, retry after: ${e.retryAfter}');
    case GeoErrorCode.notFound:
      print('Not found: ${e.requestPath}');
    default:
      print('Error ${e.statusCode}: ${e.message}');
  }
}

API Reference


License

MIT © 2026 kidpech.app

Libraries

thai_address_plus
thai_address_plus — Production-ready Flutter SDK สำหรับ Thailand Geographic API (provinces / districts / sub-districts / villages / GeoJSON / reverse-geocode / autocomplete).