網站首頁 編程語言 正文
在Flutter中,借助 FutureBuilder
組件和 StreamBuilder
組件,可以非常方便地完成異步操作。
一、FutureBuilder
在講解FutureBuilder之前,你首先要知道Future
是什么,了解了這個,后面再了解該組件就輕松許多。
在不同的編程語言中會有不同的名詞來定義,在Dart語言中 選擇使用Future類型配合async、await關鍵字來實現異步支持。
Future 表示一個現在不確定,但以后應該可以確定的值。這個值可以是任意類型,如 Future<int>
表示一個未來獲取到的整型值,Future<String>
表示一個未來獲取到的字符串。
我們通常會在定時器、網絡請求中使用Future,它會有三種狀態:uncompleted(未完成)、completed with data(獲取到一個數據)、completed with error(捕獲到一個錯誤)
,所以在實戰過程中,我們需要根據這三種狀態來判斷當前界面應該是怎樣的,加載中、數據正常顯示、提示錯誤 重新操作。
小示例:
定義一個getValue方法:
Future<String> getValue() async { await Future.delayed(Duration(seconds: 3)); return "100"; }
結合FutureBuilder組件來調用方法:
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關鍵字將函數標為異步函數,這樣函數的返回值就會被封裝為異步。
- FutureBuilder組件的future屬性是此組件的必傳參數。FutureBuilder組件得到future之后,便開始通過future的then等方法追蹤它(監聽future執行的結果),當其狀態改變時自動調用builder函數重繪。
- builder函數。每次繪制時,FutureBuilder都會調用這里的builder回傳函數,并提供BuildContext(上下文)和AsyncSnapshot<>(異步快照)。在這里AsyncSnapshot<> 封裝的類型就是future參數里的Future<> 所封裝的類型。像上例一樣,Future返回一個String,那么對應的AsyncSnapshot也是String類型。
- AsyncSnapshot 中含有 Future的最新狀態及被封裝的數據或異常。另外ConnectionState 屬性描述了 Future 的四種狀態,其分別為 none、waiting、active、done。
- 大多數時間做異步操作都是為了獲取最終的數據,那么這個數據的獲取即是在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的狀態是否完成,未完成的情況下就加載動畫組件。加載完成的情況下再進行判斷snapshot返回的是正確數據還是異常,此時data和error必有一個且只有一個不是空。
總結一下:在Future完成之前,data和error都為空,Future完成之后,data和error有且僅有一個為空。所以這個時候我們可以不檢查Future的狀態是否完成,而是直接通過snapshot的hasData
數據和hasError
異常來判斷。如果既沒有數據又沒有異常,那就是當前的Future還未完成,可直接返回加載動畫組件。這個時候的代碼就如最開始的那個示例一樣直接檢查hasData和hasError。
初始值 initialData
在Future完成之前,initialData屬性提供一個數據的初始值給FutureBuilder組件使用。在有初始值的情況下,Future完成之前hasData會返回true,并且data中存儲著所設置的初始值。當Future完成之后,FutureBuilder組件會自動切換到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的狀態,并在Stream有變化時自動重繪的組件。
那么問題來了,什么是Stream?在這里,我們稱之為數據流或事件流。Data Stream、Event Stream。
顧名思義,既然稱之為“流”,那可以想象出這是一種不間斷的操作。
我們可以通過使用Stream.periodic構造函數
,并借助其count
參數(當前Stream已被調用的次數,從0開始遞增),制作一個一秒加一的計數器數據流。
方法:
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("無數據流"); case ConnectionState.done: return Text("數據流關閉"); case ConnectionState.waiting: return Text("等待數據"); case ConnectionState.active: if(snapshot.hasData) { return Text("${snapshot.data}"); }else { return Text("${snapshot.error}"); } default: throw "connect error"; } })
這種用法有點像js中的 setInterval() 一樣。
StreamBuilder組件與FutureBuilder組件用法類似,只是在這里所傳的參數不是future,而是stream
,同時可以通過builder回傳函數 獲取Stream的4中狀態,編寫業務邏輯。用法與FutureBuilder組件中的狀態類似,這里不過多重復。
上面這種普通數據流的寫法只監聽count()方法,如果需要支持多個監聽者同時監聽,則可以通過控制器的StreamController.broadcast
構造函數創建一個廣播數據流。實現界面多處位置監聽并重繪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("關閉數據流")), ], )
在最開始,Stream沒有開始釋放任何事件,這時StreamBuilder會先渲染一個加載組件,當點擊第一個按鈕,界面將會顯示 value:666 字樣。只要不關閉數據流,StreamBuilder就會一直監聽,任何一處controller的變化。
原文鏈接:https://honker.blog.csdn.net/article/details/124785128
相關推薦
- 2022-05-20 ElasticSearch 7.X系列之:查詢分析索引磁盤使用空間_disk_usage
- 2022-08-07 python利用pd.cut()和pd.qcut()對數據進行分箱操作_python
- 2023-02-26 React?Fiber原理深入分析_React
- 2023-08-01 前端傳遞對象數組,后端使用list接收并解析
- 2022-12-06 python中isdigit()?isalpha()用于判斷字符串的類型問題_python
- 2022-04-14 Python之OptionParser模塊使用詳解_python
- 2022-07-18 CSS基礎語法和盒模型
- 2023-12-15 IDEA去掉activate-power-mode右上角圖標和Power Mode II 進度條10
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支