在 Flutter 中探索 StreamBuilder

會煮咖啡的貓發表於2021-09-01

原文

medium.com/flutterdevs…

正文

非同步互動可能需要一個理想的機會來進行總結。偶爾,在週期結束之前可能會發出一些值。在 Dart 中,您可以建立一個返回 Stream 的容量,該容量可以在非同步程式處於活動狀態時發射一些值。假設您需要根據一個 Stream 的快照在 Flutter 中構造一個小部件,那麼有一個名為 StreamBuilder 的小部件。

在這個部落格中,我們將探索 Flutter 中的 StreamBuilder。我們還將實現一個演示程式,並向您展示如何在您的 Flutter 應用程式中使用 StreamBuilder。

介紹:

StreamBuilder 可以監聽公開的流,並返回小部件和捕獲獲得的流資訊的快照。造溪者提出了兩個論點。

A stream

構建器,它可以將流中的多個元件更改為小部件

Stream 像一條線。當您從一端輸入值而從另一端輸入偵聽器時,偵聽器將獲得該值。一個流可以有多個偵聽器,這些偵聽器的負載可以獲得流水線,流水線將獲得等價值。如何在流上放置值是通過使用流控制器實現的。流構建器是一個小部件,它可以將使用者定義的物件更改為流。

建造者:

要使用 StreamBuilder,需要呼叫下面的建構函式:

const StreamBuilder({
Key? key,
Stream<T>? stream,
T? initialData,
required AsyncWidgetBuilder<T> builder,
})
複製程式碼

實際上,您需要建立一個 Stream 並將其作為流爭用傳遞。然後,在這一點上,您需要傳遞一個 AsyncWidgetBuilder,該 AsyncWidgetBuilder 可用於構造依賴於 Stream 快照的小部件。

引數:

下面是 StreamBuilderare 的一些引數:

  • Key? key: 小部件的鍵,用於控制小部件如何被另一個小部件取代
  • Stream<T>? stream: 一個流,其快照可以通過生成器函式獲得
  • T? initialData: 將利用這些資料製作初始快照
  • required AsyncWidgetBuilder<T> builder: 生成過程由此生成器使用

如何實現 dart 檔案中的程式碼:

你需要分別在你的程式碼中實現它:

讓我們建立一個流:

下面的函式返回一個每秒生成一個數字的 Stream。你需要使用 async * 關鍵字來建立一個流。若要發出值,可以使用 yield 關鍵字後跟要發出的值。

Stream<int> generateNumbers = (() async* {
  await Future<void>.delayed(Duration(seconds: 2));

  for (int i = 1; i <= 10; i++) {
    await Future<void>.delayed(Duration(seconds: 1));
    yield i;
  }
})();
複製程式碼

From that point onward, pass it as the stream argument

從那一點開始,把它作為流引數傳遞下去

StreamBuilder<int>(
stream: generateNumbers,
// other arguments
)
複製程式碼

讓我們建立一個 AsyncWidgetBuilder

建構函式期望您傳遞一個型別為 AsyncWidgetBuilder 的命名爭用構建器。這是一個有兩個引數的函式,它們的型別都是 BuildContext 和 AsyncSnapshot < t > 。後續的邊界(包含當前快照)可以用來確定應該呈現的內容。

要建立這個函式,首先需要了解 AsyncSnapshot。AsyncSnapshot 是使用非同步計算的最新通訊的不變描述。在這種獨特的情況下,它解決了與 Stream 的最新通訊。可以通過 AsyncSnapshot 屬性獲取流的最新快照。您可能需要使用的屬性之一是 connectionState,這個列舉將當前關聯狀態轉換為非同步計算,在這種特殊情況下,這種非同步計算就是 Steam。

StreamBuilder<int>(
  stream: generateNumbers,
  builder: (
      BuildContext context,
      AsyncSnapshot<int> snapshot,
      ) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return CircularProgressIndicator();
    } else if (snapshot.connectionState == ConnectionState.active
        || snapshot.connectionState == ConnectionState.done) {
      if (snapshot.hasError) {
        return const Text('Error');
      } else if (snapshot.hasData) {
        return Text(
            snapshot.data.toString(),
            style: const TextStyle(color: Colors._red_, fontSize: 40)
        );
      } else {
        return const Text('Empty data');
      }
    } else {
      return Text('State: ${snapshot.connectionState}');
    }
  },
),
複製程式碼

AsyncSnapshot 還有一個名為 hasError 的屬性,可用於檢查快照是否包含非空錯誤值。如果非同步活動的最新結果失敗,hasError 值將有效。為了獲取資訊,首先,您可以通過獲取其 hasData 屬性來檢查快照是否包含資訊,如果 Stream 有效地釋放了任何非空值,那麼 hasData 屬性將是有效的。然後,在這一點上,您可以從 AsyncSnapshot 的資料屬性獲取資訊。

由於上面屬性的值,您可以計算出應該在螢幕上呈現什麼。在下面的程式碼中,當 connectionState 值正在等待時,將顯示一個 CircularProgressIndicator。當 connectionState 更改為 active 或 done 時,可以檢查快照是否有錯誤或資訊。建造函式稱為 Flutter 管道的檢測。因此,它將獲得一個與時間相關的快照子組。這意味著,如果在實際上相似的時間裡,Stream 發出了一些值,那麼一部分值可能沒有傳遞給構建器。

列舉有一些可能的值:

  • > none: 無: 不與任何非同步計算關聯。如果流為空,則可能發生
  • > waiting: 等待: 與非同步計算關聯並等待協作。在這個上下文中,它暗示流還沒有完成
  • > active: 活躍的: 與活動的非同步計算相關聯。例如,如果一個 Stream 已經返回了任何值,但此時還沒有結束
  • > done: > 完成: 與結束的非同步計算相關聯。在這個上下文中,它暗示流已經完成

設定初始資料:

您可以選擇傳遞一個 worth 作為 initialData 引數,這個引數將被利用,直到 Stream 發出 a。如果傳遞的值不為空,那麼當 connectionState 在等待時,hasData 屬性在任何事件中首先都將為 true

StreamBuilder<int>(
initialData: 0,
// other arguments
)
複製程式碼

要在 connectionState 等待時顯示初始資料,應該調整 if snapshot.connectionState = = connectionState.waiting,然後調整上面程式碼中的塊。

if (snapshot.connectionState == ConnectionState.waiting) {
  return Column(
    crossAxisAlignment: CrossAxisAlignment.center,
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      CircularProgressIndicator(),
      Visibility(
        visible: snapshot.hasData,
        child: Text(
          snapshot.data.toString(),
          style: const TextStyle(color: Colors._black_, fontSize: 24),
        ),
      ),
    ],
  );
}
複製程式碼

當我們執行應用程式,我們應該得到螢幕的輸出像下面的螢幕視訊。

Code File:

密碼檔案:

import 'package:flutter/material.dart';
import 'package:flutter_steambuilder_demo/splash_screen.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Splash(),
      debugShowCheckedModeBanner: false,
    );
  }
}

Stream<int> generateNumbers = (() async* {
  await Future<void>.delayed(Duration(seconds: 2));

  for (int i = 1; i <= 10; i++) {
    await Future<void>.delayed(Duration(seconds: 1));
    yield i;
  }
})();

class StreamBuilderDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _StreamBuilderDemoState ();
  }
}

class _StreamBuilderDemoState extends State<StreamBuilderDemo> {

  @override
  initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        automaticallyImplyLeading: false,
        title: const Text('Flutter StreamBuilder Demo'),
      ),
      body: SizedBox(
        width: double._infinity_,
        child: Center(
          child: StreamBuilder<int>(
            stream: generateNumbers,
            initialData: 0,
            builder: (
                BuildContext context,
                AsyncSnapshot<int> snapshot,
                ) {
              if (snapshot.connectionState == ConnectionState.waiting) {
                return Column(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    CircularProgressIndicator(),
                    Visibility(
                      visible: snapshot.hasData,
                      child: Text(
                        snapshot.data.toString(),
                        style: const TextStyle(color: Colors._black_, fontSize: 24),
                      ),
                    ),
                  ],
                );
              } else if (snapshot.connectionState == ConnectionState.active
                  || snapshot.connectionState == ConnectionState.done) {
                if (snapshot.hasError) {
                  return const Text('Error');
                } else if (snapshot.hasData) {
                  return Text(
                      snapshot.data.toString(),
                      style: const TextStyle(color: Colors._red_, fontSize: 40)
                  );
                } else {
                  return const Text('Empty data');
                }
              } else {
                return Text('State: ${snapshot.connectionState}');
              }
            },
          ),
        ),
      ),
    );
  }
}
複製程式碼

結語:

在本文中,我已經簡單介紹了 StreamBuilder 的基本結構; 您可以根據自己的選擇修改這段程式碼。這是我對 StreamBuilder On User Interaction 的一個小小介紹,它正在使用 Flutter 工作。


© 貓哥

往期

開源

GetX Quick Start

github.com/ducafecat/g…

新聞客戶端

github.com/ducafecat/f…

strapi 手冊譯文

getstrapi.cn

微信討論群 ducafecat

系列集合

譯文

ducafecat.tech/categories/…

開源專案

ducafecat.tech/categories/…

Dart 程式語言基礎

space.bilibili.com/404904528/c…

Flutter 零基礎入門

space.bilibili.com/404904528/c…

Flutter 實戰從零開始 新聞客戶端

space.bilibili.com/404904528/c…

Flutter 元件開發

space.bilibili.com/404904528/c…

Flutter Bloc

space.bilibili.com/404904528/c…

Flutter Getx4

space.bilibili.com/404904528/c…

Docker Yapi

space.bilibili.com/404904528/c…

相關文章