項目Github地址:a1203991686/CoolWeather_Flutter
在第六章中我們寫了天氣預報的頁面, 但是你作爲天氣預報肯定能選擇城市吧。所以我們現在來寫選擇省、市、區的界面。
我們使用的是郭霖大神在第一行代碼最後面酷歐天氣的API。
1. 實現界面
既然是一個選擇省市區的界面,那麼我們就用ListView
。
首先看一下大致界面:
就直接使用ListView
的builder()
方法,忘了的同學可以看前面第5章。
省
在view
文件夾下新建一個類provinces_page.dart
,接下來就在這個文件裏面寫代碼:
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來進行網絡請求了。
Dio().get(http://guolin.tech/api/china/$_provinceID/$_cityID);
那麼到這有的同學可能就會問了,你網址裏面有provinceID
和cityID
,那這兩個怎麼獲取,這個時候就得用上帶值路由跳轉了。
我們一般都是這樣的一個邏輯:先選擇省份、再選擇城市、最後選擇區和縣。所以我們這個CountyPageWidget
肯定是由CityPageWidget
調用的。那麼我們可以在CityPageWidget
的ListView
裏面將Text
通過GestureDetector
包起來,然後將GestureDetector
的onTap
方法設置爲跳轉到CountyPageWidget
。CityPageWidget
的ListView
代碼如下:
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, //由CityWidget的上一級ProvinceWidget傳過來,
cityID: _cityList.cities[index].id, // 由網絡請求回來的數據傳入
);
}));
},
);
},
);
這樣CountyPageWidget
所需的provinceID
和cityID
都是由CityPageWidget
傳入的,那麼我們怎麼對他傳入的值進行處理呢,怎麼把它添加到網址裏去?接下來大家看代碼就行了:
class CountyPageWidget extends StatefulWidget {
//在Widget中定義兩個,方便由其他Widget傳入
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> {
// 在State中定義兩個變量
int _provinceID;
int _cityID;
List<County> _county;
@override
void initState() {
// 在此將Widget中的兩個變量賦值給State中的變量
_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( // UI異步更新組件
// 這塊就可以調用了
future: Dio().get("http://guolin.tech/api/china/$_provinceID/$_cityID"),
builder: (BuildContext context, AsyncSnapshot snapshot) {
...省略後面所有代碼
}
3. UI異步更新
與異步更新相關的知識大家也可以看我們之前的第7章博客,這塊只講應用。
省
將Scaffold的body參數設置爲FutureBuilder,並在FutureBuilder裏面調用ListView:
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);
//請求成功,通過項目信息構建用於顯示項目名稱的ListView
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,
);
}));
},
);
},
);
}
// 請求未完成時彈出loading
return CircularProgressIndicator();
},
)
市
與省同理:
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);
//請求成功,通過項目信息構建用於顯示項目名稱的ListView
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,
);
}));
},
);
},
);
}
// 請求未完成時彈出loading
return CircularProgressIndicator();
},
)
區縣
與省同理:
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);
//請求成功,通過項目信息構建用於顯示項目名稱的ListView
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,
);
}));
},
);
},
);
}
// 請求未完成時彈出loading
return CircularProgressIndicator();
},
)