作者 | 弗拉德
來源 | 弗拉德(公眾號:fulade_me)
異步請求
在移動開發(fā)過程中很多時候我們都需要依賴異步請求數(shù)據(jù)然后再來刷新UI呵燕。在用戶打開界面的時候,先給出一個Loading提示,等數(shù)據(jù)請求完成后偶垮,我們再把數(shù)據(jù)展示在頁面上结蟋,這是很常見的操作招刹。
異步請求的好處就是不會阻塞主線程,用戶雖然在“等”审丘,但是頁面不會卡死吏够。
同步請求不適應(yīng)于這種情況,同步請求會出現(xiàn)頁面卡死現(xiàn)象滩报,此時用戶不能點擊(即使點擊也沒有效果)锅知,體驗非常不好。
所以大多數(shù)時候我們都是使用異步請求來獲取數(shù)據(jù)
http 庫
在Flutter中脓钾,我們可以用http庫來做網(wǎng)絡(luò)請求售睹,它支持異步請求,并且有良好的API接口
使用http庫的步驟:
- 在項目中可训,打開
pubspec.yaml
文件 - 找到
dependencies
字段昌妹,在下面添加http: ^0.12.2
,其中0.12.2
是版本號 - 然后保存
pubspec.yaml
并執(zhí)行pub get
命令把我們要使用的第三方庫下載下來
具體
pub get
命令的使用在之前的文章有介紹過
然后在要使用http庫的文件里面引入頭文件
import 'package:http/http.dart' as http;
發(fā)送http請求的代碼也比較簡單握截,我們這里以get
請求為例
http.get("https://cdn.jsdelivr.net/gh/johnson8888/blog_pages/images/request_demo_test.json");
只要傳入要請求的地址即可飞崖,這里的URL地址是我自己上傳的測試文件。
http 異步請求返回結(jié)果
前面我知道http庫發(fā)送請求是支持異步的谨胞,那么異步請求的返回結(jié)果我們該如何接收呢固歪?
- 通過
then
函數(shù)獲取,在get
請求之后我們可以直接跟上then
函數(shù)來作為回調(diào)胯努,在回調(diào)內(nèi)部可以獲取到請求的結(jié)果
http.get(getURL).then((value) {
print(value);
});
這樣寫確實很方便牢裳,但當我們的網(wǎng)絡(luò)請求很多,并且一個網(wǎng)絡(luò)請求依賴另外一個網(wǎng)絡(luò)請求的時候叶沛,這個時候就會多個回調(diào)函數(shù)嵌套在一起(又稱為回調(diào)地獄
)蒲讯,代碼就會顯得很凌亂,很不適合Debug恬汁。
- 使用
await
來接收異步操作的結(jié)果
var data = await http.get(getURL);
這樣寫代碼就比上面的代碼清爽多了
但是需要注意的是伶椿,如果函數(shù)內(nèi)部有被await
修飾的方法,那么函數(shù)應(yīng)該被async
來修飾氓侧,并且返回值需要被Future
修飾脊另,Future
是一個延時計算的對象,在被await
修飾的函數(shù)返回的時候才能拿到Future
的具體值约巷。
示例入下:
Future<Map> getData() async {
var data = await http.get(getURL);
return data.body;
}
刷新頁面
我們前面已經(jīng)知道:調(diào)用setState()
函數(shù)可以刷新頁面偎痛,所以在http請求之后我們調(diào)用setState()
函數(shù)即可刷新頁面
http.get(getURL).then((value) {
print(value);
var data = jsonDecode(data.body);
setState(() {
/// 此處執(zhí)行刷新頁面的代碼
});
});
使用FutureBuilder來刷新頁面
setState()
固然是可以刷新頁面,但是當我們頁面內(nèi)有多個網(wǎng)絡(luò)請求的時候独郎,就會不停的調(diào)用setState()
來全量刷新頁面踩麦,顯然這就有點冗余枚赡。
Flutter為我們提供了更好的方式來實現(xiàn)獲取數(shù)據(jù)并且刷新UI的操作,那就是FutureBuilder
來看它的初始化方法
const FutureBuilder({
/// key
Key key,
/// 異步的操作
this.future,
/// 初始化數(shù)據(jù)
this.initialData,
/// 構(gòu)建UI的函數(shù)
@required this.builder,
})
由構(gòu)造函數(shù)可見谓谦,我們需要傳入future
參數(shù)贫橙,也就是我們的耗時操作函數(shù),還需要傳入builder
函數(shù)
在builder
方法里可以捕捉到兩個參數(shù)BuildContext context
和AsyncSnapshot snap
其中snap
的屬性會攜帶future
的耗時函數(shù)的返回值反粥,也就是說:在耗時操作函數(shù)返回結(jié)果之后卢肃,我們可以在builder
方法內(nèi)獲取到這一返回值。
所以上面的請求我們也可以這么來實現(xiàn):
FutureBuilder(
future: getData(),
builder: (BuildContext context, AsyncSnapshot snap) {
/// 如果沒有數(shù)據(jù) 我們就顯示loading頁面
if (snap.hasData == false) {
return CircularProgressIndicator();
} else {
/// 如果獲取到了數(shù)據(jù) 我們就初始化一個 ListView來展示獲取到的數(shù)據(jù)
var dataSource = snap.data["tracks"];
return ListView.builder(
itemCount: dataSource.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(dataSource[index]["title"]),
subtitle: Text(dataSource[index]["cover"]),
);
},
);
}
},
),
在獲取到數(shù)據(jù)之后才顿,我們通過ListView.builder
來構(gòu)建一個ListView
并返回莫湘,此時就完成了刷新UI的工作
我這里寫的比較簡單,只是用hasData
來做為判斷的依據(jù)
其實還有更優(yōu)雅的做法:使用snap
的另一個屬性connectionState
enum ConnectionState {
/// 沒有異步任務(wù)郑气,
none,
/// 異步任務(wù)正在等待
waiting,
/// 異步任務(wù)正在執(zhí)行 或者 數(shù)據(jù)正在傳輸
active,
/// 異步任務(wù)已經(jīng)終止.
done,
}
我們也可以在connectionState
是done
的時候在來判斷是否存在數(shù)據(jù)
如果存在就展示數(shù)據(jù)幅垮!
想體驗以上的示例的運行效果,可以到我的Github倉庫項目flutter_app
->lib
->routes
->http_page.dart
查看尾组,并且可以下載下來運行并體驗忙芒。