flutter 動手擼一個城市選擇citypicker功能
城市選擇器在項目開發中一般都會用到,基於flutter版本的也有一個city_pickers但是已經很久沒有人維護瞭,項目中之前也用的是這個,最近升級到flutter1.17.x後,發現有一定的概率閃退,無奈之下,隻能自動動手擼一個瞭
demo下載地址:https://github.com/qqcc1388/city_picker
CityPickerView能夠實現以下功能
- 顯示省市區地址,市或者區可以為空白數據
- 省市區數據支持自定義,但是格式要按照city.json中個格式來,如果需要外部傳入省市區數據,直接使用params參數即可
- 支持選中之後回調選中內容以及對於的省市區code碼
思路
利用cupertino.dart中的CupertinoPicker來實現單層數據顯示,通過row實現3層數據的滾動顯示,然後讓3個CupertinoPicker實現聯動即可實現地址選擇器,
代碼
import 'dart:convert'; import 'package:city_picker/city_result.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; typedef ResultBlock = void Function(CityResult result); class CityPickerView extends StatefulWidget { // json數據可以從外部傳入,如果外部有值,取外部值 final List params; // 結果返回 final ResultBlock onResult; CityPickerView({this.onResult, this.params}); @override _CityPickerViewState createState() => _CityPickerViewState(); } class _CityPickerViewState extends State<CityPickerView> { List datas = []; int provinceIndex; int cityIndex; int areaIndex; FixedExtentScrollController provinceScrollController; FixedExtentScrollController cityScrollController; FixedExtentScrollController areaScrollController; CityResult result = CityResult(); bool isShow = false; List get provinces { if (datas.length > 0) { if (provinceIndex == null) { provinceIndex = 0; result.province = provinces[provinceIndex]['label']; result.provinceCode = provinces[provinceIndex]['value'].toString(); } return datas; } return []; } List get citys { if (provinces.length > 0) { return provinces[provinceIndex]['children'] ?? []; } return []; } List get areas { if (citys.length > 0) { if (cityIndex == null) { cityIndex = 0; result.city = citys[cityIndex]['label']; result.cityCode = citys[cityIndex]['value'].toString(); } List list = citys[cityIndex]['children'] ?? []; if (list.length > 0) { if (areaIndex == null) { areaIndex = 0; result.area = list[areaIndex]['label']; result.areaCode = list[areaIndex]['value'].toString(); } } return list; } return []; } // 保存選擇結果 _saveInfoData() { var prs = provinces; var cts = citys; var ars = areas; if (provinceIndex != null && prs.length > 0) { result.province = prs[provinceIndex]['label']; result.provinceCode = prs[provinceIndex]['value'].toString(); } else { result.province = ''; result.provinceCode = ''; } if (cityIndex != null && cts.length > 0) { result.city = cts[cityIndex]['label']; result.cityCode = cts[cityIndex]['value'].toString(); } else { result.city = ''; result.cityCode = ''; } if (areaIndex != null && ars.length > 0) { result.area = ars[areaIndex]['label']; result.areaCode = ars[areaIndex]['value'].toString(); } else { result.area = ''; result.areaCode = ''; } } @override void dispose() { provinceScrollController.dispose(); cityScrollController.dispose(); areaScrollController.dispose(); super.dispose(); } @override void initState() { super.initState(); //初始化控制器 provinceScrollController = FixedExtentScrollController(); cityScrollController = FixedExtentScrollController(); areaScrollController = FixedExtentScrollController(); //讀取city.json數據 if (widget.params == null) { _loadCitys().then((value) { setState(() { isShow = true; }); }); } else { datas = widget.params; setState(() { isShow = true; }); } } Future _loadCitys() async { var cityStr = await rootBundle.loadString('assets/city.json'); datas = json.decode(cityStr) as List; //result默認取第一組值 return Future.value(true); } @override Widget build(BuildContext context) { return Material( child: Container( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: <Widget>[ _firstView(), _contentView(), ], ), ), ); } Widget _firstView() { return Container( height: 44, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[ FlatButton( child: Text('取消'), onPressed: () { Navigator.pop(context); }, ), FlatButton( child: Text('確定'), onPressed: () { if (widget.onResult != null) { widget.onResult(result); } Navigator.pop(context); }, ), ]), decoration: BoxDecoration( border: Border( bottom: BorderSide(color: Colors.grey.withOpacity(0.1), width: 1)), ), ); } Widget _contentView() { return Container( // color: Colors.orange, height: 200, child: isShow ? Row( children: <Widget>[ Expanded(child: _provincePickerView()), Expanded(child: _cityPickerView()), Expanded(child: _areaPickerView()), ], ) : Center( child: CupertinoActivityIndicator( animating: true, ), ), ); } Widget _provincePickerView() { return Container( child: CupertinoPicker( scrollController: provinceScrollController, children: provinces.map((item) { return Center( child: Text( item['label'], style: TextStyle(color: Colors.black87, fontSize: 16), maxLines: 1, ), ); }).toList(), onSelectedItemChanged: (index) { provinceIndex = index; if (cityIndex != null) { cityIndex = 0; if (cityScrollController.positions.length > 0) { cityScrollController.jumpTo(0); } } if (areaIndex != null) { areaIndex = 0; if (areaScrollController.positions.length > 0) { areaScrollController.jumpTo(0); } } _saveInfoData(); setState(() {}); }, itemExtent: 36, ), ); } Widget _cityPickerView() { return Container( child: citys.length == 0 ? Container() : CupertinoPicker( scrollController: cityScrollController, children: citys.map((item) { return Center( child: Text( item['label'], style: TextStyle(color: Colors.black87, fontSize: 16), maxLines: 1, ), ); }).toList(), onSelectedItemChanged: (index) { cityIndex = index; if (areaIndex != null) { areaIndex = 0; if (areaScrollController.positions.length > 0) { areaScrollController.jumpTo(0); } } _saveInfoData(); setState(() {}); }, itemExtent: 36, ), ); } Widget _areaPickerView() { return Container( width: double.infinity, child: areas.length == 0 ? Container() : CupertinoPicker( scrollController: areaScrollController, children: areas.map((item) { return Center( child: Text( item['label'], style: TextStyle(color: Colors.black87, fontSize: 16), maxLines: 1, ), ); }).toList(), onSelectedItemChanged: (index) { areaIndex = index; _saveInfoData(); setState(() {}); }, itemExtent: 36, ), ); } } class CityResult { /// 省市區 String province = ''; String city = ''; String area = ''; /// 對應的編碼 String provinceCode = ''; String cityCode = ''; String areaCode = ''; CityResult({ this.province, this.city, this.area, this.provinceCode, this.cityCode, this.areaCode, }); CityResult.fromJson(Map<String, dynamic> json) { province = json['province']; city = json['city']; area = json['area']; provinceCode = json['provinceCode']; cityCode = json['cityCode']; areaCode = json['areaCode']; } Map<String, dynamic> toJson() { final Map<String, dynamic> datas = new Map<String, dynamic>(); datas['province'] = this.province; datas['city'] = this.city; datas['area'] = this.area; datas['provinceCode'] = this.provinceCode; datas['cityCode'] = this.cityCode; datas['areaCode'] = this.areaCode; return datas; } }
用法
// var cityStr = await rootBundle.loadString('assets/city.json'); // List datas = json.decode(cityStr) as List; showModalBottomSheet( context: context, isScrollControlled: true, builder: (ctx) { return CityPickerView( // params: datas, onResult: (res) { print(res.toJson()); setState(() { citySelect = res.toJson().toString(); }); }, ); }, );
demo下載地址:https://github.com/qqcc1388/city_picker
到此這篇關於flutter 動手擼一個城市選擇citypicker的文章就介紹到這瞭,更多相關flutter 城市選擇citypicker內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 使用微信小程序制作核酸檢測點查詢工具
- Mybatis + js 實現下拉列表二級聯動效果
- C#根據IP地址查詢所屬地區實例詳解
- json如何解析混合數組對象到實體類的list集合裡去
- 微信小程序自定義地址組件