vn_provinces_api 1.0.1 copy "vn_provinces_api: ^1.0.1" to clipboard
vn_provinces_api: ^1.0.1 copied to clipboard

Flutter/Dart package để truy vấn dữ liệu địa chỉ tỉnh thành, phường xã Việt Nam qua provinces.open-api.vn.

example/lib/main.dart

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

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'VN Provinces Demo',
      theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.red), useMaterial3: true),
      home: const AddressPickerPage(),
    );
  }
}

/// Demo: Chọn địa chỉ 3 cấp (Tỉnh → Huyện → Xã)
class AddressPickerPage extends StatefulWidget {
  const AddressPickerPage({super.key});

  @override
  State<AddressPickerPage> createState() => _AddressPickerPageState();
}

class _AddressPickerPageState extends State<AddressPickerPage> {
  final _client = VnProvincesClient(); // v2 — sau sáp nhập 07/2025

  List<Province> _provinces = [];
  List<Ward> _wards = [];

  Province? _selectedProvince;
  Ward? _selectedWard;

  bool _loading = false;
  String? _error;

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

  @override
  void dispose() {
    _client.dispose();
    super.dispose();
  }

  Future<void> _loadProvinces() async {
    setState(() => _loading = true);
    try {
      final provinces = await _client.getProvinces();
      setState(() {
        _provinces = provinces;
        _error = null;
      });
    } on VnProvincesException catch (e) {
      setState(() => _error = e.message);
    } finally {
      setState(() => _loading = false);
    }
  }

  Future<void> _onProvinceChanged(Province? province) async {
    if (province == null) return;
    setState(() {
      _selectedProvince = province;
      _selectedWard = null;
      _wards = [];
      _loading = true;
    });
    try {
      final full = await _client.getProvince(province.code, depth: 2);
      setState(() {
        _wards = full.wards ?? [];
        _error = null;
      });
    } on VnProvincesException catch (e) {
      setState(() => _error = e.message);
    } finally {
      setState(() => _loading = false);
    }
  }

  String get _fullAddress {
    final parts = [
      if (_selectedWard != null) _selectedWard!.name,
      if (_selectedProvince != null) _selectedProvince!.name,
    ];
    return parts.join(', ');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Chọn địa chỉ Việt Nam'),
        backgroundColor: Theme.of(context).colorScheme.primary,
        foregroundColor: Colors.white,
      ),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            if (_loading) const LinearProgressIndicator(),
            if (_error != null)
              Padding(
                padding: const EdgeInsets.only(bottom: 8),
                child: Text(_error!, style: const TextStyle(color: Colors.red)),
              ),
            const SizedBox(height: 12),

            // Tỉnh/Thành phố
            _buildDropdown<Province>(
              label: 'Tỉnh / Thành phố',
              value: _selectedProvince,
              items: _provinces,
              itemLabel: (p) => p.name,
              onChanged: _onProvinceChanged,
            ),

            const SizedBox(height: 16),

            // Phường/Xã
            _buildDropdown<Ward>(
              label: 'Phường / Xã / Thị trấn',
              value: _selectedWard,
              items: _wards,
              itemLabel: (w) => w.name,
              onChanged: (ward) => setState(() => _selectedWard = ward),
              enabled: _selectedProvince != null,
            ),

            const SizedBox(height: 32),

            if (_fullAddress.isNotEmpty) ...[
              const Text('Địa chỉ đã chọn:', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
              const SizedBox(height: 8),
              Container(
                padding: const EdgeInsets.all(12),
                decoration: BoxDecoration(
                  color: Colors.red.shade50,
                  borderRadius: BorderRadius.circular(8),
                  border: Border.all(color: Colors.red.shade200),
                ),
                child: Text(_fullAddress, style: const TextStyle(fontSize: 15)),
              ),
            ],
          ],
        ),
      ),
    );
  }

  Widget _buildDropdown<T>({
    required String label,
    required T? value,
    required List<T> items,
    required String Function(T) itemLabel,
    required void Function(T?) onChanged,
    bool enabled = true,
  }) {
    return DropdownButtonFormField<T>(
      decoration: InputDecoration(labelText: label, border: const OutlineInputBorder(), enabled: enabled),
      initialValue: value,
      hint: Text('Chọn $label'),
      isExpanded: true,
      items: items
          .map(
            (item) => DropdownMenuItem<T>(
              value: item,
              child: Text(itemLabel(item), overflow: TextOverflow.ellipsis),
            ),
          )
          .toList(),
      onChanged: enabled ? onChanged : null,
    );
  }
}
1
likes
0
points
11
downloads

Publisher

unverified uploader

Weekly Downloads

Flutter/Dart package để truy vấn dữ liệu địa chỉ tỉnh thành, phường xã Việt Nam qua provinces.open-api.vn.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, http

More

Packages that depend on vn_provinces_api