Flutter異步操作實現(xiàn)流程詳解
在Flutter中,借助 FutureBuilder 組件和 StreamBuilder 組件,可以非常方便地完成異步操作。
一、FutureBuilder
在講解FutureBuilder之前,你首先要知道Future是什么,了解了這個,后面再了解該組件就輕松許多。
在不同的編程語言中會有不同的名詞來定義,在Dart語言中 選擇使用Future類型配合async、await關(guān)鍵字來實現(xiàn)異步支持。
Future 表示一個現(xiàn)在不確定,但以后應(yīng)該可以確定的值。這個值可以是任意類型,如 Future<int>表示一個未來獲取到的整型值,Future<String>表示一個未來獲取到的字符串。
我們通常會在定時器、網(wǎng)絡(luò)請求中使用Future,它會有三種狀態(tài):uncompleted(未完成)、completed with data(獲取到一個數(shù)據(jù))、completed with error(捕獲到一個錯誤),所以在實戰(zhàn)過程中,我們需要根據(jù)這三種狀態(tài)來判斷當(dāng)前界面應(yīng)該是怎樣的,加載中、數(shù)據(jù)正常顯示、提示錯誤 重新操作。
小示例:
定義一個getValue方法:
Future<String> getValue() async {
await Future.delayed(Duration(seconds: 3));
return "100";
}
結(jié)合FutureBuilder組件來調(diào)用方法:
FutureBuilder(
future: getValue(),
builder:
(BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
return Text(
"${snapshot.data}",
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
} else {
return Container(
height: 50,
width: 50,
margin: EdgeInsets.only(top: 10),
child: CircularProgressIndicator(
strokeWidth: 8.0,
)
);
}
}),

我們逐步來分解上面的示例代碼:
- 這里使用async關(guān)鍵字將函數(shù)標(biāo)為異步函數(shù),這樣函數(shù)的返回值就會被封裝為異步。
- FutureBuilder組件的future屬性是此組件的必傳參數(shù)。FutureBuilder組件得到future之后,便開始通過future的then等方法追蹤它(監(jiān)聽future執(zhí)行的結(jié)果),當(dāng)其狀態(tài)改變時自動調(diào)用builder函數(shù)重繪。
- builder函數(shù)。每次繪制時,F(xiàn)utureBuilder都會調(diào)用這里的builder回傳函數(shù),并提供BuildContext(上下文)和AsyncSnapshot<>(異步快照)。在這里AsyncSnapshot<> 封裝的類型就是future參數(shù)里的Future<> 所封裝的類型。像上例一樣,F(xiàn)uture返回一個String,那么對應(yīng)的AsyncSnapshot也是String類型。
- AsyncSnapshot 中含有 Future的最新狀態(tài)及被封裝的數(shù)據(jù)或異常。另外ConnectionState 屬性描述了 Future 的四種狀態(tài),其分別為 none、waiting、active、done。
- 大多數(shù)時間做異步操作都是為了獲取最終的數(shù)據(jù),那么這個數(shù)據(jù)的獲取即是在Future的done(完成)之后,所以我們的邏輯代碼可以這樣寫。
FutureBuilder(
future: getValue(),
builder:
(BuildContext context, AsyncSnapshot<String> snapshot) {
if(snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
return Text(
"${snapshot.data}",
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
);
} else {
return Text("${snapshot.error}");
}
}else {
return Container(
height: 50,
width: 50,
margin: const EdgeInsets.only(top: 10),
child: const CircularProgressIndicator(
strokeWidth: 8.0,
)
);
}
}),
首先判斷Future的狀態(tài)是否完成,未完成的情況下就加載動畫組件。加載完成的情況下再進(jìn)行判斷snapshot返回的是正確數(shù)據(jù)還是異常,此時data和error必有一個且只有一個不是空。
總結(jié)一下:在Future完成之前,data和error都為空,F(xiàn)uture完成之后,data和error有且僅有一個為空。所以這個時候我們可以不檢查Future的狀態(tài)是否完成,而是直接通過snapshot的hasData數(shù)據(jù)和hasError異常來判斷。如果既沒有數(shù)據(jù)又沒有異常,那就是當(dāng)前的Future還未完成,可直接返回加載動畫組件。這個時候的代碼就如最開始的那個示例一樣直接檢查hasData和hasError。
初始值 initialData
在Future完成之前,initialData屬性提供一個數(shù)據(jù)的初始值給FutureBuilder組件使用。在有初始值的情況下,F(xiàn)uture完成之前hasData會返回true,并且data中存儲著所設(shè)置的初始值。當(dāng)Future完成之后,F(xiàn)utureBuilder組件會自動切換到Future的真實值并重新渲染。
FutureBuilder(
future: getValue(),
initialData: "加載中...",
builder:
(BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
return Text(
"${snapshot.data}",
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
);
} else {
return Text("${snapshot.error}");
}
}),
二、StreamBuilder
StreamBuilder組件與FutureBuilder組件相同之外的不同之處在于它是一個可以自動跟蹤Stream的狀態(tài),并在Stream有變化時自動重繪的組件。
那么問題來了,什么是Stream?在這里,我們稱之為數(shù)據(jù)流或事件流。Data Stream、Event Stream。
顧名思義,既然稱之為“流”,那可以想象出這是一種不間斷的操作。
我們可以通過使用Stream.periodic構(gòu)造函數(shù),并借助其count參數(shù)(當(dāng)前Stream已被調(diào)用的次數(shù),從0開始遞增),制作一個一秒加一的計數(shù)器數(shù)據(jù)流。
方法:
Stream<int> counter() {
return Stream.periodic(
const Duration(seconds: 1),
(count) => count
);
}
在組件中使用:
StreamBuilder(
stream: counter(),
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
switch(snapshot.connectionState) {
case ConnectionState.none:
return Text("無數(shù)據(jù)流");
case ConnectionState.done:
return Text("數(shù)據(jù)流關(guān)閉");
case ConnectionState.waiting:
return Text("等待數(shù)據(jù)");
case ConnectionState.active:
if(snapshot.hasData) {
return Text("${snapshot.data}");
}else {
return Text("${snapshot.error}");
}
default:
throw "connect error";
}
})

這種用法有點像js中的 setInterval() 一樣。
StreamBuilder組件與FutureBuilder組件用法類似,只是在這里所傳的參數(shù)不是future,而是stream,同時可以通過builder回傳函數(shù) 獲取Stream的4中狀態(tài),編寫業(yè)務(wù)邏輯。用法與FutureBuilder組件中的狀態(tài)類似,這里不過多重復(fù)。
上面這種普通數(shù)據(jù)流的寫法只監(jiān)聽count()方法,如果需要支持多個監(jiān)聽者同時監(jiān)聽,則可以通過控制器的StreamController.broadcast構(gòu)造函數(shù)創(chuàng)建一個廣播數(shù)據(jù)流。實現(xiàn)界面多處位置監(jiān)聽并重繪StreamBuilder組件。
例如:
final _stream = StreamController<int>();
StreamBuilder(
stream: _stream.stream.map((event) => "value:${event}"),
builder: (context, snapshot) {
if(snapshot.connectionState == ConnectionState.done) {
return Text("close stream");
}
if(snapshot.hasData) return Text("${snapshot.data}");
if(snapshot.hasError) return Text("${snapshot.error}");
return CircularProgressIndicator();
}),
Row(
children: [
ElevatedButton(onPressed: () => _stream.add(666), child: Text("添加666")),
SizedBox(width: 20,),
ElevatedButton(onPressed: () => _stream.add(888), child: Text("添加888")),
SizedBox(width: 20,),
ElevatedButton(onPressed: () => _stream.close(), child: Text("關(guān)閉數(shù)據(jù)流")),
],
)
在最開始,Stream沒有開始釋放任何事件,這時StreamBuilder會先渲染一個加載組件,當(dāng)點擊第一個按鈕,界面將會顯示 value:666 字樣。只要不關(guān)閉數(shù)據(jù)流,StreamBuilder就會一直監(jiān)聽,任何一處controller的變化。
到此這篇關(guān)于Flutter異步操作實現(xiàn)流程詳解的文章就介紹到這了,更多相關(guān)Flutter異步操作內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
android基于ListView和CheckBox實現(xiàn)多選和全選記錄的功能
本篇文章主要介紹了android基于ListView和CheckBox實現(xiàn)多選和全選記錄的功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2016-11-11
android串口開發(fā)入門之搭建ndk開發(fā)環(huán)境及第一個jni調(diào)用程序
這篇文章主要給大家介紹了關(guān)于android串口開發(fā)入門之搭建ndk開發(fā)環(huán)境及第一個jni調(diào)用程序的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-01-01
Android TabHost如何實現(xiàn)頂部選項卡
這篇文章主要介紹了Android TabHost如何實現(xiàn)頂部選項卡,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-09-09
Android基于ViewPager實現(xiàn)類似微信頁面切換效果
這篇文章主要介紹了Android基于ViewPager實現(xiàn)類似微信頁面切換效果,通過Fragment適配器實現(xiàn)頁面切換效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-05-05
Android DaggerActivityComponent錯誤解決辦法詳解
這篇文章主要介紹了Android DaggerActivityComponent錯誤解決的相關(guān)資料,需要的朋友可以參考下2017-05-05
Android 自定義ContentProvider簡單實例
這篇文章主要介紹了Android 自定義ContentProvider簡單實例的相關(guān)資料,需要的朋友可以參考下2017-06-06

