flutter 動(dòng)手?jǐn)]一個(gè)城市選擇citypicker功能
城市選擇器在項(xiàng)目開發(fā)中一般都會(huì)用到,基于flutter版本的也有一個(gè)city_pickers但是已經(jīng)很久沒有人維護(hù)了,項(xiàng)目中之前也用的是這個(gè),最近升級(jí)到flutter1.17.x后,發(fā)現(xiàn)有一定的概率閃退,無奈之下,只能自動(dòng)動(dòng)手?jǐn)]一個(gè)了
demo下載地址:https://github.com/qqcc1388/city_picker
CityPickerView能夠?qū)崿F(xiàn)以下功能
- 顯示省市區(qū)地址,市或者區(qū)可以為空白數(shù)據(jù)
- 省市區(qū)數(shù)據(jù)支持自定義,但是格式要按照city.json中個(gè)格式來,如果需要外部傳入省市區(qū)數(shù)據(jù),直接使用params參數(shù)即可
- 支持選中之后回調(diào)選中內(nèi)容以及對(duì)于的省市區(qū)code碼
思路
利用cupertino.dart中的CupertinoPicker來實(shí)現(xiàn)單層數(shù)據(jù)顯示,通過row實(shí)現(xiàn)3層數(shù)據(jù)的滾動(dòng)顯示,然后讓3個(gè)CupertinoPicker實(shí)現(xiàn)聯(lián)動(dòng)即可實(shí)現(xiàn)地址選擇器,
代碼
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數(shù)據(jù)可以從外部傳入,如果外部有值,取外部值 final List params; // 結(jié)果返回 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 []; } // 保存選擇結(jié)果 _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數(shù)據(jù) 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默認(rèn)取第一組值 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 { /// 省市區(qū) String province = ''; String city = ''; String area = ''; /// 對(duì)應(yīng)的編碼 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
到此這篇關(guān)于flutter 動(dòng)手?jǐn)]一個(gè)城市選擇citypicker的文章就介紹到這了,更多相關(guān)flutter 城市選擇citypicker內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android getReadableDatabase() 和 getWritableDatabase()分析對(duì)比
這篇文章主要介紹了Android getReadableDatabase() 和 getWritableDatabase()分析對(duì)比的相關(guān)資料,需要的朋友可以參考下2017-06-06利用OPENCV為android開發(fā)畸變校正的JNI庫方法
今天小編就為大家分享一篇利用OPENCV為android開發(fā)畸變校正的JNI庫方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-08-08詳解Android實(shí)現(xiàn)購物車頁面及購物車效果(點(diǎn)擊動(dòng)畫)
本篇文章主要介紹了詳解Android實(shí)現(xiàn)購物車頁面及購物車效果(點(diǎn)擊動(dòng)畫),非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-08-08Android Handler內(nèi)存泄漏詳解及其解決方案
在android開發(fā)過程中,我們可能會(huì)遇到過令人奔潰的OOM異常,這篇文章主要介紹了Android Handler內(nèi)存泄漏詳解及其解決方案,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08Android編程開發(fā)實(shí)現(xiàn)TextView顯示表情圖像和文字的方法
這篇文章主要介紹了Android編程開發(fā)實(shí)現(xiàn)TextView顯示表情圖像和文字的方法,結(jié)合實(shí)例形式分析了Android中TextView的使用技巧,需要的朋友可以參考下2015-12-12Android手機(jī)開發(fā) 控件 TextView文字居中
本文主要介紹Android手機(jī)開發(fā)TextView居中的方法,希望能幫到大家。2016-05-05Android通過滑動(dòng)實(shí)現(xiàn)Activity跳轉(zhuǎn)(手勢(shì)識(shí)別器應(yīng)用)
這篇文章主要為大家詳細(xì)介紹了Android通過滑動(dòng)實(shí)現(xiàn)Activity跳轉(zhuǎn),,講解手勢(shì)識(shí)別器應(yīng)用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05詳解Android開啟OTG功能/USB?Host?API功能
這篇文章主要介紹了Android開啟OTG功能/USB?Host?API功能,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07