共计 5810 个字符,预计需要花费 15 分钟才能阅读完成。
项目Github地址:a1203991686/CoolWeather_Flutter
在第六章中我们写了天气预报的页面, 但是你作为天气预报肯定能选择城市吧。所以我们现在来写选择省、市、区的界面。
我们使用的是郭霖大神在第一行代码最后面酷欧天气的API。
1. 实现界面
既然是一个选择省市区的界面,那么我们就用ListView。
首先看一下大致界面:

就直接使用ListView的builder()方法,忘了的同学可以看前面第5章。
省
在view文件夹下新建一个类provinces_page.dart,接下来就在这个文件里面写代码:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
class ProvincesPageWidget extends StatefulWidget { ProvincesPageWidget({Key key}) : super(key: key);
@override ProvincesPageStateWidget createState() => new ProvincesPageStateWidget(); }
class ProvincesPageStateWidget extends State<ProvincesPageWidget> { @override Widget build(BuildContext context) { return Scaffold( appBar: new AppBar( title: Text( "省份", style: TextStyle(fontSize: 25.0), ), ), body: ListView.builder( itemCount: 30, itemBuilder: (context, index) { return ListTile( title: Text("$index"), ); }, ), ); } }
|
市
和省一样。在view文件夹下新建一个类city_page.dart,接下来就在这个文件里面写代码。照着省的依葫芦画瓢,写一个ListView。
区
和省一样。在view文件夹下新建一个类counties_page.dart,接下来就在这个文件里面写代码。照着省的依葫芦画瓢,写一个ListView。
2. 网络请求
网络请求可以看下之前第7章的内容。主要是通过Dio进行网络请求。大家可以看下第7章网络请求的内容。
我们网络请求主要需要以下几个API:
- 请求省份列表:
http://guolin.tech/api/china
- 请求对应市列表:
http://guolin.tech/api/china/provinceID
- 请求对应区县列表:
http://guolin.tech/api/china/provinceID/cityID
由于我们在之前文章中使用的省作为例子,这块我们就用区县作为例子:
转为Dart类
首先使用网站https://caijinglong.github.io/json2dart/来讲Json数据转为Dart实体类:

然后在项目lib目录新建一个文件夹。名为bean。这个文件夹主要存放实体类的代码。然后在这个文件夹下面新建一个dart文件,命名为Counties。然后把生成的Dart代码复制粘贴进去。但是你会发现复制进去后会报错,这个不用急,接下来我们来处理错误。
生成.g.dart文件
接下来在项目根目录的pubspec.yaml文件的dependencies项下面添加依赖json_annotation、dev_dependencies项下面添加build_runner和json_serializable。
接着打开终端,定位到项目根目录,输入
flutter packages pub run build_runner build,运行即可。运行完毕你就会发现你项目的存实体类的lib/bean目录多了一个文件,名为Counties.g.dart。同时你Counties.dart里面的错误也没有了。
网络请求
最后你就可以通过Dio来进行网络请求了。
那么到这有的同学可能就会问了,你网址里面有provinceID和cityID,那这两个怎么获取,这个时候就得用上带值路由跳转了。
我们一般都是这样的一个逻辑:先选择省份、再选择城市、最后选择区和县。所以我们这个CountyPageWidget肯定是由CityPageWidget调用的。那么我们可以在CityPageWidget的ListView里面将Text通过GestureDetector包起来,然后将GestureDetector的onTap方法设置为跳转到CountyPageWidget。CityPageWidget的ListView代码如下:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
ListView.builder( itemCount: _cityList.cities.length, itemBuilder: (context, index) { return GestureDetector( child: ListTile( title: Text("${_cityList.cities[index].name}"), ), onTap: () { Navigator.push(context, MaterialPageRoute(builder: (context) { return CountiesPageWidget( provinceID: _provinceID, cityID: _cityList.cities[index].id, ); })); }, ); }, );
|
这样CountyPageWidget所需的provinceID和cityID都是由CityPageWidget传入的,那么我们怎么对他传入的值进行处理呢,怎么把它添加到网址里去?接下来大家看代码就行了:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
29 30 31 32 33 34 35 36 37 38 39 40 41
|
class CountyPageWidget extends StatefulWidget { final int provinceID; final int cityID;
CountyPageWidget({Key key, this.provinceID, this.cityID}) : super(key: key);
@override CountyPageWidgetState createState() => CountyPageWidgetState(); }
class CountyPageWidgetState extends State<CountyPageWidget> { int _provinceID; int _cityID; List<County> _county;
@override void initState() { _provinceID = widget.provinceID; _cityID = widget.cityID; super.initState(); }
@override Widget build(BuildContext context) { return Scaffold( appBar: new AppBar( title: Text( "区县", style: TextStyle(fontSize: 25.0), ), ), body: FutureBuilder( future: Dio().get("http://guolin.tech/api/china/$_provinceID/$_cityID"), builder: (BuildContext context, AsyncSnapshot snapshot) { ...省略后面所有代码 }
|
3. UI异步更新
与异步更新相关的知识大家也可以看我们之前的第7章博客,这块只讲应用。
省
将Scaffold的body参数设置为FutureBuilder,并在FutureBuilder里面调用ListView:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
29 30 31 32 33 34
|
FutureBuilder( future: Dio().get("http://guolin.tech/api/china"), builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done) { Response response = snapshot.data; if (snapshot.hasError) { return Text(snapshot.error.toString()); }
provinceList = getProvinceList(response.data); return ListView.builder( itemCount: provinceList.length, itemBuilder: (context, index) { return GestureDetector( child: ListTile( title: Text("${provinceList[index].name}"), ), onTap: () { Navigator.push(context, MaterialPageRoute(builder: (context) { return CityPageWidget( provinceID: provinceList[index].id, ); })); }, ); }, ); } return CircularProgressIndicator(); }, )
|
市
与省同理:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
29 30 31 32 33 34 35
|
FutureBuilder( future: Dio().get("http://guolin.tech/api/china/$_provinceID"), builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done) { Response response = snapshot.data; if (snapshot.hasError) { return Text(snapshot.error.toString()); }
_cityList = getCityList(response.data); return ListView.builder( itemCount: _cityList.length, itemBuilder: (context, index) { return GestureDetector( child: ListTile( title: Text("${_cityList[index].name}"), ), onTap: () { Navigator.push(context, MaterialPageRoute(builder: (context) { return CountiesPageWidget( provinceID: _provinceID, cityID: _cityList[index].id, ); })); }, ); }, ); } return CircularProgressIndicator(); }, )
|
区县
与省同理:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
29 30 31 32 33 34 35
|
FutureBuilder( future: Dio().get("http://guolin.tech/api/china/$_provinceID/$_cityID"), builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done) { Response response = snapshot.data; if (snapshot.hasError) { return Text(snapshot.error.toString()); }
_county = getCountyList(response.data); return ListView.builder( itemCount: _county.length, itemBuilder: (context, index) { return GestureDetector( child: ListTile( title: Text("${_county[index].name}"), ), onTap: () { _saveCityID(_county[index].weatherId); Navigator.push(context, MaterialPageRoute(builder: (context) { return MainPage( cityID: _county[index].weatherId, ); })); }, ); }, ); } return CircularProgressIndicator(); }, )
|