flutter使用CupertinoPicker绘制一个传入数据源的省市区选择器
import 'package:atui/jade/bean/PCADataBean.dart';
import 'package:atui/jade/utils/JadeColors.dart';
import 'package:atui/main.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
/*
* 传入城市数据源的省市区选择器
* 待扩展:把传入的对象类型数据源优化传入json数据,
* 并在项目中存放本地的城市json数据,当传入数据源为空时读取本地json
* */
typedef ResultBlock = void Function(CityResult result);class CityPickerView extends StatefulWidget {final List<PcaDataBean> params;// 结果返回final ResultBlock onResult;CityPickerView({required this.onResult, required this.params}); _CityPickerViewState createState() => _CityPickerViewState();
}class _CityPickerViewState extends State<CityPickerView> {List<PcaDataBean> _cpaDatas = [];int? provinceIndex;int? cityIndex;int? areaIndex;late FixedExtentScrollController _provinceScrollController;late FixedExtentScrollController _cityScrollController;late FixedExtentScrollController _areaScrollController;CityResult result = CityResult();bool isShow = false;List get provinces {if (_cpaDatas.length > 0) {if (provinceIndex == null) {provinceIndex = 0;result.province = provinces[provinceIndex!].name;}return _cpaDatas;}return [];}List get citys {if (provinces.length > 0) {return provinces[provinceIndex!].cityVOList ?? [];}return [];}List get areas {if (citys.length > 0) {if (cityIndex == null) {cityIndex = 0;result.city = citys[cityIndex!].name;}List list = citys[cityIndex!].districtVOList ?? [];if (list.length > 0) {if (areaIndex == null) {areaIndex = 0;result.area = list[areaIndex!].name;}}return list;}return [];}// 保存选择结果_saveInfoData() {var prs = provinces;var cts = citys;var ars = areas;if (provinceIndex != null && prs.length > 0) {result.province = prs[provinceIndex!].name;} else {result.province = '';}if (cityIndex != null && cts.length > 0) {result.city = cts[cityIndex!].name;} else {result.city = '';}if (areaIndex != null && ars.length > 0) {result.area = ars[areaIndex!].name;} else {result.area = '';}}void dispose() {_provinceScrollController.dispose();_cityScrollController.dispose();_areaScrollController.dispose();super.dispose();}void initState() {super.initState();//初始化控制器_provinceScrollController = FixedExtentScrollController();_cityScrollController = FixedExtentScrollController();_areaScrollController = FixedExtentScrollController();if (widget.params == null) {//读取city.json数据_loadCitys().then((value) {setState(() {isShow = true;});});} else {_cpaDatas = widget.params;setState(() {isShow = true;});}}Future _loadCitys() async {// var cityStr = await rootBundle.loadString('assets/city.json');// _cpaDatas = json.decode(cityStr) as List;//result默认取第一组值return Future.value(true);} 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>[TextButton(child: Text('取消',style: TextStyle(color: JadeColors.grey)),onPressed: () {Navigator.pop(context);},),TextButton(child: Text('确定'),onPressed: () {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.name,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.name,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.name,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 = '';CityResult({this.province,this.city,this.area,});CityResult.fromJson(Map<String, dynamic> json) {province = json['province'];city = json['city'];area = json['area'];}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;return datas;}
}class CustomCityPicker {static cityPicker(BuildContext? context,provinceList,{required ResultBlock onResult}){showModalBottomSheet(context: context ?? navigatorKey.currentContext!,isScrollControlled: true,builder: (ctx) {return CityPickerView(params: provinceList,onResult: (res) {onResult(res);});},);}
}
省市区接口数据源实体类
/// name : "安徽省"
/// cityVOList : [{"name":"安庆市","districtVOList":[{"name":"大观区"},{"name":"怀宁县"},{"name":"潜山市"},{"name":"宿松县"},{"name":"桐城市"},{"name":"太湖县"},{"name":"望江县"},{"name":"迎江区"},{"name":"宜秀区"},{"name":"岳西县"}]},{"name":"蚌埠市","districtVOList":[{"name":"蚌山区"},{"name":"固镇县"},{"name":"淮上区"},{"name":"怀远县"},{"name":"龙子湖区"},{"name":"五河县"},{"name":"禹会区"}]},{"name":"亳州市","districtVOList":[{"name":"利辛县"},{"name":"蒙城县"},{"name":"谯城区"},{"name":"涡阳县"}]},{"name":"滁州市","districtVOList":[{"name":"定远县"},{"name":"凤阳县"},{"name":"来安县"},{"name":"琅琊区"},{"name":"明光市"},{"name":"南谯区"},{"name":"全椒县"},{"name":"天长市"}]},{"name":"池州市","districtVOList":[{"name":"东至县"},{"name":"贵池区"},{"name":"青阳县"},{"name":"石台县"}]},{"name":"阜阳市","districtVOList":[{"name":"阜南县"},{"name":"界首市"},{"name":"临泉县"},{"name":"太和县"},{"name":"颍东区"},{"name":"颍泉区"},{"name":"颍上县"},{"name":"颍州区"}]},{"name":"淮北市","districtVOList":[{"name":"杜集区"},{"name":"烈山区"},{"name":"濉溪县"},{"name":"相山区"}]},{"name":"合肥市","districtVOList":[{"name":"包河区"},{"name":"巢湖市"},{"name":"肥东县"},{"name":"肥西县"},{"name":"庐江县"},{"name":"庐阳区"},{"name":"蜀山区"},{"name":"瑶海区"},{"name":"长丰县"}]},{"name":"淮南市","districtVOList":[{"name":"八公山区"},{"name":"大通区"},{"name":"凤台县"},{"name":"潘集区"},{"name":"寿县"},{"name":"田家庵区"},{"name":"谢家集区"}]},{"name":"黄山市","districtVOList":[{"name":"黄山区"},{"name":"徽州区"},{"name":"祁门县"},{"name":"歙县"},{"name":"屯溪区"},{"name":"休宁县"},{"name":"黟县"}]},{"name":"六安市","districtVOList":[{"name":"霍邱县"},{"name":"霍山县"},{"name":"金安区"},{"name":"金寨县"},{"name":"舒城县"},{"name":"裕安区"},{"name":"叶集区"}]},{"name":"马鞍山市","districtVOList":[{"name":"博望区"},{"name":"当涂县"},{"name":"花山区"},{"name":"含山县"},{"name":"和县"},{"name":"雨山区"}]},{"name":"宿州市","districtVOList":[{"name":"砀山县"},{"name":"灵璧县"},{"name":"泗县"},{"name":"萧县"},{"name":"埇桥区"}]},{"name":"铜陵市","districtVOList":[{"name":"郊区"},{"name":"铜官区"},{"name":"义安区"},{"name":"枞阳县"}]},{"name":"芜湖市","districtVOList":[{"name":"繁昌区"},{"name":"镜湖区"},{"name":"鸠江区"},{"name":"南陵县"},{"name":"无为市"},{"name":"湾沚区"},{"name":"弋江区"}]},{"name":"宣城市","districtVOList":[{"name":"广德市"},{"name":"旌德县"},{"name":"泾县"},{"name":"绩溪县"},{"name":"郎溪县"},{"name":"宁国市"},{"name":"宣州区"}]}]class PcaDataBean {String? name;List<CityVoList>? cityVOList;PcaDataBean({this.name, this.cityVOList,});PcaDataBean.fromJson(dynamic json) {name = json['name'];if (json['cityVOList'] != null) {cityVOList = [];json['cityVOList'].forEach((v) {cityVOList?.add(CityVoList.fromJson(v));});}}PcaDataBean copyWith({ String? name,List<CityVoList>? cityVOList,
}) => PcaDataBean( name: name ?? this.name,cityVOList: cityVOList ?? this.cityVOList,
);Map<String, dynamic> toJson() {final map = <String, dynamic>{};map['name'] = name;if (cityVOList != null) {map['cityVOList'] = cityVOList?.map((v) => v.toJson()).toList();}return map;}}/// name : "安庆市"
/// districtVOList : [{"name":"大观区"},{"name":"怀宁县"},{"name":"潜山市"},{"name":"宿松县"},{"name":"桐城市"},{"name":"太湖县"},{"name":"望江县"},{"name":"迎江区"},{"name":"宜秀区"},{"name":"岳西县"}]class CityVoList {String? name;List<DistrictVoList>? districtVOList;CityVoList({this.name, this.districtVOList,});CityVoList.fromJson(dynamic json) {name = json['name'];if (json['districtVOList'] != null) {districtVOList = [];json['districtVOList'].forEach((v) {districtVOList?.add(DistrictVoList.fromJson(v));});}}
CityVoList copyWith({ String? name,List<DistrictVoList>? districtVOList,
}) => CityVoList( name: name ?? this.name,districtVOList: districtVOList ?? this.districtVOList,
);Map<String, dynamic> toJson() {final map = <String, dynamic>{};map['name'] = name;if (districtVOList != null) {map['districtVOList'] = districtVOList?.map((v) => v.toJson()).toList();}return map;}}/// name : "大观区"class DistrictVoList {DistrictVoList({this.name,});DistrictVoList.fromJson(dynamic json) {name = json['name'];}String? name;
DistrictVoList copyWith({ String? name,
}) => DistrictVoList( name: name ?? this.name,
);Map<String, dynamic> toJson() {final map = <String, dynamic>{};map['name'] = name;return map;}}
调用
_showCityPicker(){CustomCityPicker.cityPicker(context, _provinceVOList,onResult: (result) {setState(() {_provinceName = result.province;_cityName = result.city;_districtName = result.area;_showFullDistrict = '${result.province}${result.city}${result.area}';});});}