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)文章
Gradle?Build?Cache引發(fā)的Task緩存編譯問題
這篇文章主要為大家介紹了Gradle?Build?Cache引發(fā)的Task緩存編譯問題,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
Android基于Fresco實(shí)現(xiàn)圓角和圓形圖片
這篇文章主要為大家詳細(xì)介紹了Android基于Fresco實(shí)現(xiàn)圓角和圓形圖片,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
android?原生安全音量配置邏輯設(shè)計(jì)詳解
這篇文章主要為大家介紹了android?原生安全音量配置邏輯設(shè)計(jì)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
Android 后臺(tái)發(fā)送郵件示例 (收集應(yīng)用異常信息+Demo代碼)
今天介紹個(gè)更簡單的方法,我們把異常信息收集后,通過后臺(tái)發(fā)送郵件方法,把相關(guān)異常信息發(fā)送到我們指定的郵箱里面2013-07-07
SwipeRefreshLayout+RecyclerView實(shí)現(xiàn)上拉刷新和下拉刷新功能
這篇文章主要介紹了SwipeRefreshLayout+RecyclerView實(shí)現(xiàn)上拉刷新和下拉刷新功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01
Android入門之使用eclipse進(jìn)行源碼開發(fā)的方法
這篇文章主要介紹了Android入門之使用eclipse進(jìn)行源碼開發(fā)的方法,較為詳細(xì)的分析了使用eclipse進(jìn)行Android源碼開發(fā)的具體步驟與相關(guān)注意事項(xiàng),需要的朋友可以參考下2016-02-02
Android Studio創(chuàng)建AIDL文件并實(shí)現(xiàn)進(jìn)程間通訊實(shí)例
本篇文章主要介紹了Android Studio創(chuàng)建AIDL文件并實(shí)現(xiàn)進(jìn)程間通訊實(shí)例,具有一定的參考價(jià)值,有興趣可以了解一下。2017-04-04

