flutter 動(dòng)手?jǐn)]一個(gè)城市選擇citypicker功能
城市選擇器在項(xiàng)目開(kāi)發(fā)中一般都會(huì)用到,基于flutter版本的也有一個(gè)city_pickers但是已經(jīng)很久沒(méi)有人維護(hù)了,項(xiàng)目中之前也用的是這個(gè),最近升級(jí)到flutter1.17.x后,發(fā)現(xiàn)有一定的概率閃退,無(wú)奈之下,只能自動(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è)格式來(lái),如果需要外部傳入省市區(qū)數(shù)據(jù),直接使用params參數(shù)即可
- 支持選中之后回調(diào)選中內(nèi)容以及對(duì)于的省市區(qū)code碼
思路
利用cupertino.dart中的CupertinoPicker來(lái)實(shí)現(xiàn)單層數(shù)據(jù)顯示,通過(guò)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開(kāi)發(fā)畸變校正的JNI庫(kù)方法
今天小編就為大家分享一篇利用OPENCV為android開(kāi)發(fā)畸變校正的JNI庫(kù)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
詳解Android實(shí)現(xiàn)購(gòu)物車頁(yè)面及購(gòu)物車效果(點(diǎn)擊動(dòng)畫)
本篇文章主要介紹了詳解Android實(shí)現(xiàn)購(gòu)物車頁(yè)面及購(gòu)物車效果(點(diǎn)擊動(dòng)畫),非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-08-08
Android Handler內(nèi)存泄漏詳解及其解決方案
在android開(kāi)發(fā)過(guò)程中,我們可能會(huì)遇到過(guò)令人奔潰的OOM異常,這篇文章主要介紹了Android Handler內(nèi)存泄漏詳解及其解決方案,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08
Android編程開(kāi)發(fā)實(shí)現(xiàn)TextView顯示表情圖像和文字的方法
這篇文章主要介紹了Android編程開(kāi)發(fā)實(shí)現(xiàn)TextView顯示表情圖像和文字的方法,結(jié)合實(shí)例形式分析了Android中TextView的使用技巧,需要的朋友可以參考下2015-12-12
Android手機(jī)開(kāi)發(fā) 控件 TextView文字居中
本文主要介紹Android手機(jī)開(kāi)發(fā)TextView居中的方法,希望能幫到大家。2016-05-05
Android通過(guò)滑動(dòng)實(shí)現(xiàn)Activity跳轉(zhuǎn)(手勢(shì)識(shí)別器應(yīng)用)
這篇文章主要為大家詳細(xì)介紹了Android通過(guò)滑動(dòng)實(shí)現(xiàn)Activity跳轉(zhuǎn),,講解手勢(shì)識(shí)別器應(yīng)用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05
詳解Android開(kāi)啟OTG功能/USB?Host?API功能
這篇文章主要介紹了Android開(kāi)啟OTG功能/USB?Host?API功能,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07

