Flutter城市选择器插件luo_city_picker的使用

Flutter城市选择器插件luo_city_picker的使用

地址选择器

插件版本 最后提交时间

由于地址选择的数据来源会更新,为了统一,在后台配置一份城市数据,前端获取,否则各个平台都配置一份数据,维护会很麻烦,而且有可能每个平台城市的数据结构都不一样。
本库就是由此而来,数据从后台实时获取,只要解析成固定的数据结构就可以。

Demo

web 预览

下载APK

效果示例

导入方式

dependencies:
    luo_city_picker: ^0.0.1

使用方法

1. 简单使用

CityPicker.show(context: context, cityPickerListener: this);

2. 多配置的使用

CityPicker.show(
      context: context,
      // 动画控制器
      animController: _animationController,
      // 背景透明度
      opacity: 0.5,
      // 点击外部是否消失
      dismissible: true,
      // 高度
      height: 400,
      // 标题高度
      titleHeight: 50,
      // 顶部圆角
      corner: 20,
      // 背景颜色
      backgroundColor: Colors.white
      // 距离左边的间距
      paddingLeft: 15,
      // 标题组件
      titleWidget: Container(
        padding: EdgeInsets.only(left: 15),
        child: Text(
          '请选择地址',
          style: TextStyle(
            color: Colors.black54,
            fontSize: 18,
            fontWeight: FontWeight.bold,
          ),
        ),
      ),
      // 选择文字
      selectText: "请选择",
      // 关闭图标组件
      closeWidget: Icon(Icons.close),
      // tab 高度
      tabHeight: 40,
      // 是否显示指示器
      showTabIndicator: _showTabIndicator,
      // tab 间隔
      tabPadding: 15,
      // tab 指示器颜色
      tabIndicatorColor: Theme.of(context).primaryColor,
      // tab 指示器高度
      tabIndicatorHeight: 2,
      // tab 字体大小
      labelTextSize: 15,
      // tab 选中的字体颜色
      selectedLabelColor: Theme.of(context).primaryColor,
      // tab 未选中的字体颜色
      unselectedLabelColor: Colors.black54,
      // 列表 item 头部高度
      itemHeadHeight: 30,
      // 列表 item 头部背景颜色
      itemHeadBackgroundColor: Colors.white,
      // 列表 item 头部分割线颜色
      itemHeadLineColor: Colors.black,
      // 列表 item 头部分割线高度
      itemHeadLineHeight: 0.1,
      // 列表 item 头部文字样式
      itemHeadTextStyle: TextStyle(fontSize: 15, color: Colors.black),
      // 列表 item 高度
      itemHeight: 40,
      // 索引组件宽度
      indexBarWidth: 28,
      // 索引组件 item 高度
      indexBarItemHeight: 20,
      // 索引组件背景颜色
      indexBarBackgroundColor: Colors.black12,
      // 索引组件文字样式
      indexBarTextStyle: TextStyle(fontSize: 14, color: Colors.black54),
      // 列表选中的图标组件
      itemSelectedIconWidget:
          Icon(Icons.done, color: Theme.of(context).primaryColor, size: 16),
      // 列表选中的文字样式
      itemSelectedTextStyle: TextStyle(
          fontSize: 14,
          fontWeight: FontWeight.bold,
          color: Theme.of(context).primaryColor),
      // 列表未选中的文字样式
      itemUnSelectedTextStyle: TextStyle(fontSize: 14, color: Colors.black54),
      // 初始默认地址
      initialAddress: _selectedAddress,
      cityPickerListener: this,
    );

3. 监听事件

class HomeWidgetState extends State<HomeWidget> with SingleTickerProviderStateMixin implements CityPickerListener {
  // ... 其他代码 ...

  [@override](/user/override)
  Future<List<AddressNode>> onDataLoad(int index, String code, String name) async {
    debugPrint("onDataLoad --- $index $name");

    /// 从后台获取所有省市区三级数据,Map结构
    var allCityList = HttpUtils.allCityList;

    /// 用户选择的省,有带选择的城市列表,Map结构
    Map selectProvince = {};

    /// 用户选择的城市,有带选择的区列表,Map结构
    Map selectCity = {};
    if (index == 0) {
      return getTargetList(allCityList);
    } else if (index == 1) {
      for (Map province in allCityList) {
        if (province["code"] == code && province["name"] == name) {
          selectProvince = province;
          break;
        }
      }
      List cityList = selectProvince["children"] ?? [];
      return getTargetList(cityList);
    } else if (index == 2) {
      List cityList = selectProvince["children"] ?? [];
      for (Map city in cityList) {
        if (city["code"] == code && city["name"] == name) {
          selectCity = city;
          break;
        }
      }
      List districtList = selectCity["children"] ?? [];
      return getTargetList(districtList);
    } else {
      return [];
    }
  }

  List<AddressNode> getTargetList(List<dynamic> list) {
    return list
        .map((item) => AddressNode(
              name: item['name'],
              code: item['code'],
              letter: PinyinHelper.getFirstWordPinyin(item['name']).substring(0, 1).toUpperCase(),
            ))
        .toList();
  }

  [@override](/user/override)
  void onFinish(List<AddressNode> data) {
    String newCity = "";
    for (int i = 0; i < data.length; i++) {
      if (i < data.length - 1) {
        newCity += "${data[i].name},";
      } else {
        newCity += "${data[i].name}";
      }
    }
    _addressArea = newCity;
    _selectData = data;
    setState(() {});
  }
}

完整示例Demo

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:luo_city_picker/city_picker.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:lpinyin/lpinyin.dart';
import 'package:provider/provider.dart';

import 'http/http_util.dart';
import 'provider/theme_provider.dart';
import 'view/item_text.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider.value(value: ThemeProvider()),
      ],
      child: Consumer<ThemeProvider>(
        builder: (context, themeProvider, _) {
          Color color = themeProvider.themeColor;
          return MaterialApp(
            title: 'CityPickerExample',
            debugShowCheckedModeBanner: false,
            theme: ThemeData(
              primaryColor: color,
              dialogBackgroundColor: Colors.white,
              bottomSheetTheme: BottomSheetThemeData(
                  constraints: BoxConstraints(
                maxWidth: MediaQuery.of(context).size.width,
              )),
            ),
            home: HomeWidget(),
          );
        },
      ),
    );
  }
}

class HomeWidget extends StatefulWidget {
  [@override](/user/override)
  HomeWidgetState createState() => HomeWidgetState();
}

class HomeWidgetState extends State<HomeWidget> with SingleTickerProviderStateMixin implements CityPickerListener {
  AnimationController? _animationController;
  String _addressArea = "请选择地区";
  Color _themeColor = Colors.blue;
  Color _backgroundColor = Colors.white;
  double _height = 500.0;
  double _opacity = 0.5;
  double _corner = 20;
  bool _dismissible = true;
  bool _showTabIndicator = true;
  List<AddressNode> _selectData = [];

  // List<AddressNode> _selectCity = [];
  // List<AddressNode> _selectArea = [];
  // List<AddressNode> _selectStreet = [];

  /// 0: 省
  /// 1: 市
  /// 2: 地区
  /// 3: 街道
  int _currentType = 0;

  [@override](/user/override)
  void initState() {
    super.initState();

    _animationController = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 200),
    );
  }

  void show(List<AddressNode> initData) {
    CityPicker.show(
      context: context,
      animController: _animationController,
      opacity: _opacity,
      dismissible: _dismissible,
      height: _height,
      titleHeight: 50,
      corner: _corner,
      backgroundColor: _backgroundColor,
      paddingLeft: 15,
      titleWidget: Text(
        '请选择地址',
        style: TextStyle(
          color: Colors.black54,
          fontSize: 18,
          fontWeight: FontWeight.bold,
        ),
      ),
      selectText: "请选择",
      closeWidget: Icon(Icons.close),
      tabHeight: 40,
      showTabIndicator: _showTabIndicator,
      tabPadding: 15,
      tabIndicatorColor: Theme.of(context).primaryColor,
      tabIndicatorHeight: 2,
      labelTextSize: 15,
      selectedLabelColor: Theme.of(context).primaryColor,
      unselectedLabelColor: Colors.black54,
      itemHeadHeight: 30,
      itemHeadBackgroundColor: _backgroundColor,
      itemHeadLineColor: Colors.black,
      itemHeadLineHeight: 0.1,
      itemHeadTextStyle: TextStyle(fontSize: 15, color: Colors.black),
      itemHeight: 40,
      indexBarWidth: 28,
      indexBarItemHeight: 20,
      indexBarBackgroundColor: Colors.black12,
      indexBarTextStyle: TextStyle(fontSize: 14, color: Colors.black54),
      itemSelectedIconWidget:
          Icon(Icons.done, color: Theme.of(context).primaryColor, size: 16),
      itemSelectedTextStyle: TextStyle(
          fontSize: 14,
          fontWeight: FontWeight.bold,
          color: Theme.of(context).primaryColor),
      itemUnSelectedTextStyle: TextStyle(fontSize: 14, color: Colors.black54),
      initialAddress: initData,
      cityPickerListener: this,
    );
  }

  Widget _buildTheme() {
    return InkWell(
      onTap: () {
        showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: Text('选择颜色'),
              content: SingleChildScrollView(
                child: BlockPicker(
                  pickerColor: _themeColor,
                  onColorChanged: (color) {
                    setState(() {
                      _themeColor = color;
                    });
                    Provider.of<ThemeProvider>(context, listen: false).setTheme(color);
                  },
                ),
              ),
            );
          },
        );
      },
      child: Container(
        color: _themeColor,
        width: double.infinity,
        padding: EdgeInsets.all(10),
        margin: EdgeInsets.fromLTRB(80, 10, 80, 10),
      ),
    );
  }

  Widget _buildColor() {
    return InkWell(
      onTap: () {
        showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: Text('选择颜色'),
              content: SingleChildScrollView(
                child: BlockPicker(
                  pickerColor: _backgroundColor,
                  onColorChanged: (color) {
                    setState(() {
                      _backgroundColor = color;
                    });
                  },
                ),
              ),
            );
          },
        );
      },
      child: Container(
        color: _backgroundColor,
        width: double.infinity,
        padding: EdgeInsets.all(10),
        margin: EdgeInsets.fromLTRB(80, 10, 80, 10),
      ),
    );
  }

  Widget _buildOpacity() {
    return Row(
      children: <Widget>[
        Expanded(
          flex: 1,
          child: Slider(
            value: _opacity,
            min: 0.01,
            max: 1.0,
            divisions: 100,
            activeColor: Theme.of(context).primaryColor,
            inactiveColor: Colors.grey,
            onChanged: (double) {
              setState(() {
                _opacity = double.toDouble();
              });
            },
          ),
        ),
        Text("${_opacity.toStringAsFixed(2)}")
      ],
    );
  }

  Widget _buildDismissible() {
    return Container(
        alignment: Alignment.centerRight,
        child: Switch(
          value: _dismissible,
          activeColor: Theme.of(context).primaryColor,
          onChanged: (bool val) {
            setState(() {
              _dismissible = !_dismissible;
            });
          },
        ));
  }

  Widget _buildHeight() {
    return Row(
      children: <Widget>[
        Expanded(
          flex: 1,
          child: Slider(
            value: _height,
            min: 200,
            max: 600,
            divisions: 100,
            activeColor: Theme.of(context).primaryColor,
            inactiveColor: Colors.grey,
            onChanged: (double) {
              setState(() {
                _height = double.toDouble();
              });
            },
          ),
        ),
        Text("${_height.toStringAsFixed(2)}")
      ],
    );
  }

  Widget _buildCorner() {
    return Row(
      children: <Widget>[
        Expanded(
          flex: 1,
          child: Slider(
            value: _corner,
            min: 0.0,
            max: 30,
            divisions: 100,
            activeColor: Theme.of(context).primaryColor,
            inactiveColor: Colors.grey,
            onChanged: (double) {
              setState(() {
                _corner = double.toDouble();
              });
            },
          ),
        ),
        Text("${_corner.toStringAsFixed(2)}")
      ],
    );
  }

  Widget _buildIndicator() {
    return Container(
        alignment: Alignment.centerRight,
        child: Switch(
          value: _showTabIndicator,
          activeColor: Theme.of(context).primaryColor,
          onChanged: (bool val) {
            setState(() {
              _showTabIndicator = !_showTabIndicator;
            });
          },
        ));
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('城市选择器'),
        backgroundColor: Theme.of(context).primaryColor,
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            ItemTextWidget(
              title: '选择地区',
              subWidget: InkWell(
                onTap: () {
                  _currentType = 0;
                  setState(() {});
                  show(_selectData);
                },
                child: Padding(
                  padding: EdgeInsets.fromLTRB(0, 15, 0, 15),
                  child: Text(_addressArea),
                ),
              ),
            ),
            ItemTextWidget(title: '主题颜色', subWidget: _buildTheme()),
            ItemTextWidget(title: '透明度', subWidget: _buildOpacity()),
            ItemTextWidget(title: '外部点击消失', subWidget: _buildDismissible()),
            ItemTextWidget(title: '弹窗背景颜色', subWidget: _buildColor()),
            ItemTextWidget(title: '弹窗高度', subWidget: _buildHeight()),
            ItemTextWidget(title: '顶部圆角', subWidget: _buildCorner()),
            ItemTextWidget(title: '显示 Indicator', subWidget: _buildIndicator()),
          ],
        ),
      ),
    );
  }

  [@override](/user/override)
  Future<List<AddressNode>> onDataLoad(int index, String code, String name) async {
    debugPrint("onDataLoad --- $index $name");

    /// 从后台获取所有省市区三级数据,Map结构
    var allCityList = HttpUtils.allCityList;

    /// 用户选择的省,有带选择的城市列表,Map结构
    Map selectProvince = {};

    /// 用户选择的城市,有带选择的区列表,Map结构
    Map selectCity = {};
    if (index == 0) {
      return getTargetList(allCityList);
    } else if (index == 1) {
      for (Map province in allCityList) {
        if (province["code"] == code && province["name"] == name) {
          selectProvince = province;
          break;
        }
      }
      List cityList = selectProvince["children"] ?? [];
      return getTargetList(cityList);
    } else if (index == 2) {
      List cityList = selectProvince["children"] ?? [];
      for (Map city in cityList) {
        if (city["code"] == code && city["name"] == name) {
          selectCity = city;
          break;
        }
      }
      List districtList = selectCity["children"] ?? [];
      return getTargetList(districtList);
    } else {
      return [];
    }
  }

  List<AddressNode> getTargetList(List<dynamic> list) {
    return list
        .map((item) => AddressNode(
              name: item['name'],
              code: item['code'],
              letter: PinyinHelper.getFirstWordPinyin(item['name']).substring(0, 1).toUpperCase(),
            ))
        .toList();
  }

  [@override](/user/override)
  void onFinish(List<AddressNode> data) {
    String newCity = "";
    for (int i = 0; i < data.length; i++) {
      if (i < data.length - 1) {
        newCity += "${data[i].name},";
      } else {
        newCity += "${data[i].name}";
      }
    }
    _addressArea = newCity;
    _selectData = data;
    setState(() {});
  }
}

更多关于Flutter城市选择器插件luo_city_picker的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter城市选择器插件luo_city_picker的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


luo_city_picker 是一个用于 Flutter 的城市选择器插件,允许用户从列表中选择省、市、区。它提供了一个简单易用的界面,适用于需要用户选择地址信息的应用程序。

安装

首先,你需要在 pubspec.yaml 文件中添加依赖:

dependencies:
  flutter:
    sdk: flutter
  luo_city_picker: ^版本号 # 请替换为最新版本

然后运行 flutter pub get 来安装依赖。

基本用法

  1. 导入包

    在你的 Dart 文件中导入 luo_city_picker

    import 'package:luo_city_picker/luo_city_picker.dart';
    
  2. 使用城市选择器

    你可以通过调用 showCityPicker 方法来显示城市选择器。这个方法会返回一个 Future,用户选择城市后会返回选择的省、市、区信息。

    ElevatedButton(
      onPressed: () async {
        CityResult? result = await showCityPicker(
          context: context,
        );
        
        if (result != null) {
          print("省: ${result.province}");
          print("市: ${result.city}");
          print("区: ${result.district}");
        }
      },
      child: Text('选择城市'),
    );
    
  3. 自定义配置

    showCityPicker 方法允许你通过参数自定义选择器的行为。例如:

    CityResult? result = await showCityPicker(
      context: context,
      title: '请选择城市', // 标题
      confirmText: '确定', // 确认按钮文本
      cancelText: '取消', // 取消按钮文本
      // 其他配置
    );
    

返回值

showCityPicker 返回一个 CityResult 对象,包含以下属性:

  • province: 选择的省
  • city: 选择的市
  • district: 选择的区

如果用户取消了选择,返回值为 null

示例代码

以下是一个完整的示例代码,展示如何使用 luo_city_picker

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

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CityPickerDemo(),
    );
  }
}

class CityPickerDemo extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('城市选择器示例'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () async {
            CityResult? result = await showCityPicker(
              context: context,
              title: '请选择城市',
              confirmText: '确定',
              cancelText: '取消',
            );
            
            if (result != null) {
              print("省: ${result.province}");
              print("市: ${result.city}");
              print("区: ${result.district}");
            }
          },
          child: Text('选择城市'),
        ),
      ),
    );
  }
}
回到顶部