Flutter實戰(zhàn) 異步UI更新(FutureBuilder、StreamBuilder)

2022-05-10 11:05 更新

很多時候我們會依賴一些異步數(shù)據(jù)來動態(tài)更新 UI,比如在打開一個頁面時我們需要先從互聯(lián)網(wǎng)上獲取數(shù)據(jù),在獲取數(shù)據(jù)的過程中我們顯示一個加載框,等獲取到數(shù)據(jù)時我們再渲染頁面;又比如我們想展示 Stream(比如文件流、互聯(lián)網(wǎng)數(shù)據(jù)接收流)的進度。當然,通過 StatefulWidget 我們完全可以實現(xiàn)上述這些功能。但由于在實際開發(fā)中依賴異步數(shù)據(jù)更新 UI 的這種場景非常常見,因此 Flutter 專門提供了FutureBuilderStreamBuilder兩個組件來快速實現(xiàn)這種功能。

#7.5.1 FutureBuilder

FutureBuilder會依賴一個Future,它會根據(jù)所依賴的Future的狀態(tài)來動態(tài)構建自身。我們看一下FutureBuilder構造函數(shù):

  1. FutureBuilder({
  2. this.future,
  3. this.initialData,
  4. @required this.builder,
  5. })

  • futureFutureBuilder依賴的Future,通常是一個異步耗時任務。

  • initialData:初始數(shù)據(jù),用戶設置默認數(shù)據(jù)。

  • builder:Widget 構建器;該構建器會在Future執(zhí)行的不同階段被多次調用,構建器簽名如下:

  1. Function (BuildContext context, AsyncSnapshot snapshot)

snapshot會包含當前異步任務的狀態(tài)信息及結果信息 ,比如我們可以通過snapshot.connectionState獲取異步任務的狀態(tài)信息、通過snapshot.hasError判斷異步任務是否有錯誤等等,完整的定義讀者可以查看AsyncSnapshot類定義。

另外,FutureBuilderbuilder函數(shù)簽名和StreamBuilderbuilder是相同的。

#示例

我們實現(xiàn)一個路由,當該路由打開時我們從網(wǎng)上獲取數(shù)據(jù),獲取數(shù)據(jù)時彈一個加載框;獲取結束時,如果成功則顯示獲取到的數(shù)據(jù),如果失敗則顯示錯誤。由于我們還沒有介紹在 flutter 中如何發(fā)起網(wǎng)絡請求,所以在這里我們不真正去網(wǎng)絡請求數(shù)據(jù),而是模擬一下這個過程,隔3秒后返回一個字符串:

  1. Future<String> mockNetworkData() async {
  2. return Future.delayed(Duration(seconds: 2), () => "我是從互聯(lián)網(wǎng)上獲取的數(shù)據(jù)");
  3. }

FutureBuilder使用代碼如下:

  1. ...
  2. Widget build(BuildContext context) {
  3. return Center(
  4. child: FutureBuilder<String>(
  5. future: mockNetworkData(),
  6. builder: (BuildContext context, AsyncSnapshot snapshot) {
  7. // 請求已結束
  8. if (snapshot.connectionState == ConnectionState.done) {
  9. if (snapshot.hasError) {
  10. // 請求失敗,顯示錯誤
  11. return Text("Error: ${snapshot.error}");
  12. } else {
  13. // 請求成功,顯示數(shù)據(jù)
  14. return Text("Contents: ${snapshot.data}");
  15. }
  16. } else {
  17. // 請求未結束,顯示loading
  18. return CircularProgressIndicator();
  19. }
  20. },
  21. ),
  22. );
  23. }

運行結果如圖7-8、7-9所示:

圖7-8圖7-9

上面代碼中我們在builder中根據(jù)當前異步任務狀態(tài)ConnectionState來返回不同的 widget。ConnectionState是一個枚舉類,定義如下:

  1. enum ConnectionState {
  2. /// 當前沒有異步任務,比如[FutureBuilder]的[future]為null時
  3. none,
  4. /// 異步任務處于等待狀態(tài)
  5. waiting,
  6. /// Stream處于激活狀態(tài)(流上已經(jīng)有數(shù)據(jù)傳遞了),對于FutureBuilder沒有該狀態(tài)。
  7. active,
  8. /// 異步任務已經(jīng)終止.
  9. done,
  10. }

注意,ConnectionState.active只在StreamBuilder中才會出現(xiàn)。

#7.5.2 StreamBuilder

我們知道,在 Dart 中Stream 也是用于接收異步事件數(shù)據(jù),和Future 不同的是,它可以接收多個異步操作的結果,它常用于會多次讀取數(shù)據(jù)的異步任務場景,如網(wǎng)絡內容下載、文件讀寫等。StreamBuilder正是用于配合Stream來展示流上事件(數(shù)據(jù))變化的 UI 組件。下面看一下StreamBuilder的默認構造函數(shù):

  1. StreamBuilder({
  2. Key key,
  3. this.initialData,
  4. Stream<T> stream,
  5. @required this.builder,
  6. })

可以看到和FutureBuilder的構造函數(shù)只有一點不同:前者需要一個future,而后者需要一個stream。

#示例

我們創(chuàng)建一個計時器的示例:每隔1秒,計數(shù)加1。這里,我們使用Stream來實現(xiàn)每隔一秒生成一個數(shù)字:

  1. Stream<int> counter() {
  2. return Stream.periodic(Duration(seconds: 1), (i) {
  3. return i;
  4. });
  5. }

StreamBuilder使用代碼如下:

  1. Widget build(BuildContext context) {
  2. return StreamBuilder<int>(
  3. stream: counter(), //
  4. //initialData: ,// a Stream<int> or null
  5. builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
  6. if (snapshot.hasError)
  7. return Text('Error: ${snapshot.error}');
  8. switch (snapshot.connectionState) {
  9. case ConnectionState.none:
  10. return Text('沒有Stream');
  11. case ConnectionState.waiting:
  12. return Text('等待數(shù)據(jù)...');
  13. case ConnectionState.active:
  14. return Text('active: ${snapshot.data}');
  15. case ConnectionState.done:
  16. return Text('Stream已關閉');
  17. }
  18. return null; // unreachable
  19. },
  20. );
  21. }

讀者可以自己運行本示例查看運行結果。注意,本示例只是為了演示StreamBuilder的使用,在實戰(zhàn)中,凡是 UI 會依賴多個異步數(shù)據(jù)而發(fā)生變化的場景都可以使用StreamBuilder。

以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號