文章目錄
相關Demo源碼可見a1203991686/CoolWeather_Flutter
1. Flutter Http 網絡請求
Flutter網絡請求可分爲兩種方式,一種爲Dart:IO
庫中爲我們提供的HttpClient
,另一種爲Dart第三方庫Dio
。
1.1 HttpClient
HttpClient
是Dart:IO
庫自帶的一個類,通過他來實現網絡請求最底層、也可以完完全全的自定義設置,但是相對於其他人封裝好的第三方庫來說顯得複雜。
引入
導入Dart:IO
庫:
import 'dart:io';
創建一個HttpClient:
HttpClient httpClient = new HttpClient();
創建一個Uri
如果你是Http
請求:
var uri = new Uri.http(
host,
queryParameters: {
"xx":"xx",
"yy":"dd"
}
);
如果你是Https
請求:
var uri = new Uri.https(
host,
queryParameters: {
"xx":"xx",
"yy":"dd"
}
);
根據uri獲取返回數據
HttpClientRequest request = await httpClient.getUrl(uri);
HttpClientResponse response = await request.close();
讀取內容
進行這一步得導入dart:convert
庫:
import 'dart:convert';
... //省略中間代碼
String responseBody = await response.transform(Utf8Decoder()).join();
print(respinseBody)
最後關閉Client
httpClient.close();
1.2 Dio
Dio
是Dart社區上別人上傳的第三方庫,他封裝了HttpClient
,相較來說更爲簡單、方便。
引入
在項目的pubspec.yaml
文件的dependencies
中導入Dio
:
dependencies:
dio:
// 如果冒號後面不帶具體版本信息則表示自動下載最新版
接着在需要使用的代碼文件裏面,import
導入他:
import 'package:dio/dio.dart';
接着我們就可以使用Dio
了。
示例
首先得創建一個Dio
對象。
Dio dio = Dio();
發起GET
請求:
Response response;
response=await dio.get(uri)
print(response.data.toString());
發起POST
請求:
response=await dio.post(uri);
2. Json轉Dart
手動生成Dart實體類
首先得大家安利一個網站,因爲Dart實體類比Java實體類多了幾個方法,所以相對來說麻煩,通過這個網站就可以自動生成json數據對應的實體類:
https://caijinglong.github.io/json2dart/
比方說我們使用這個鏈接(http://guolin.tech/api/china),並讓他自動轉dart:
接下來只需要創建dart文件,然後再把代碼複製進去就行了。
不出意外,代碼肯定會報錯。
報錯信息爲:
error: Target of URI hasn't been generated: 'province.g.dart'. (uri_has_not_been_generated at [cool_weather] lib/bean/province.dart:3)
error: The method '_$ProvinceFromJson' isn't defined for the class 'Province'. (undefined_method at [cool_weather] lib/bean/province.dart:31)
error: The method '_$ProvinceToJson' isn't defined for the class 'Province'. (undefined_method at [cool_weather] lib/bean/province.dart:33)
這是因爲我們項目下還沒有province.g.dart
這個文件,這個文件是根據dart實體類自動生成的,那麼我們該怎麼生成他呢?
這個時候我們需要在pubspec.yaml
文件中導入三個依賴包:
我們重點只需要管三個寫着需要導入的。輸入之後點擊pubspec.yaml
文件右上角的packages get
,就會自動下載包了。
然後在終端中,轉到項目根目錄下,輸入
flutter packages pub run build_runner build
接着你就會驚喜的發現,在你的dart實體類下面多了一個文件,也就是你所缺失的~.g.dart文件,並且實體類中那些報錯也都沒了。
將請求回來的Response轉爲Dart實體類
接着HttpClient返回來的responseBody或者Dio返回的response
List<Province> provinceList;
provinceList = ProvinceList.getProvinceList(jsonResponse);
provinceList就是返回來的數據轉成的實體類的對象了。
3. 異步更新UI
在此處介紹兩種方法,第一種是我自己想出來的沙雕方法,第二種是通過Flutter提供的專門用於異步更新UI的組件FutureBuilder。
沙雕方法
在此說一下我這個方法的想法。
大家可以回顧下我之前講Widget的時候說過,Widget有一個方法initState可以用來加載UI,以及一個setState方法可用來提醒Flutter重新加載UI。
說到這很多同學一定想到了,我們可以在獲取到數據之後通過setState方法來更新UI。
獲取到數據
這個不同多說,上面講的全都是獲取數據。
設置一個空的實體類
此處我還是拿上面的Province.dart來做例子。我們正常情況下獲取到的數據轉成的實體類的對象是List<Province> provinceList
。所以我們此處先設置一個空的對象:List<Province> _provinceList
。
接着在initState裏面調用網絡請求的方法:
@override
void initState() {
getProvince();
super.initState();
}
然後在getProvince方法裏面獲取數據,並將數據provinceList傳給_provinceList:
void getProvince() async {
var response = await Dio().get("http://guolin.tech/api/china");
provinceList = ProvinceList.getProvinceList(response);
setState(() {
_provinces = provinceList;
});
}
這樣就可以當獲取到數據的時候就通知Flutter更新狀態了。這個時候有的同學可能會問我,你這個也只是獲取到數據的時候才更新啊,那當還沒獲取到數據的時候呢?
這塊就體現到了爲什麼我們要設置一個_provinces了。
我們完全可以在build方法設置下,判斷下_provinces爲不爲空:如果爲空,就證明沒數據,就加載其它頁面;如果不爲空,就證明有數據了,那就加載數據。
body: _provinces == null
? new Text("正在請求")
: new ListView.builder(
... //省去代碼,總的來說就是將_provinces的數據加載ListView裏面,一個ListView的基本構造方法
)
FutureBuilder
迴歸正題,我上面那個方法真的是有夠沙雕的。
那我們來看看這個名正言順的Flutter親生兒子FutureBuilder。
首先我們看下定義:
FutureBuilder({
this.future,
this.initialData,
@required this.builder,
})
- future:異步任務;
- initialData:初始化數據;
- builder:builder方法。
示例
body: FutureBuilder(
future: Dio().get("http://guolin.tech/api/china"),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Response response = snapshot.data;
print(response.toString());
//發生錯誤
if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
provinceList = ProvinceList.fromJson(response.data);
//請求成功,通過項目信息構建用於顯示項目名稱的ListView
return ListView.builder(
itemCount: provinceList.provinces.length,
itemBuilder: (context, index) {
return GestureDetector(
child: ListTile(
title: Text("${provinceList.provinces[index].name}"),
),
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) {
return CityPageWidget(
cityID: provinceList.provinces[index].id);
}));
},
);
},
);
}
// 請求未完成時彈出loading
return CircularProgressIndicator();
},
)