Flutter - 如何在流构建器中使用 await?

Posted

技术标签:

【中文标题】Flutter - 如何在流构建器中使用 await?【英文标题】:Flutter - How do I use await inside the streambuilder? 【发布时间】:2020-08-12 11:58:57 【问题描述】:

我想在 streambuilder 中使用 await。但是,如果你在里面使用 async,你会得到一个错误。在下面的代码上!!!!!!!!!这就是我要解决的部分。如果我能告诉你怎么做,非常感谢。

class _MemoStreamState extends State<MemoStream> 
final _fireStore = Firestore.instance;

@override
Widget build(BuildContext context) 
return StreamBuilder<QuerySnapshot>(
  stream: _fireStore
      .collection(widget.logInUsrEmail)
      .orderBy('id', descending: false)
      .snapshots(),
  builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) 
    if (!snapshot.hasData) return LinearProgressIndicator();

    final memos = snapshot.data.documents;
    List<MemoMaterial> memoList = [];

    for (var memo in memos) 
      final memoDocumentID = memo.documentID;
      final memoTitle = await PlatformStringCryptor().decrypt(memo.data['title'], _key);  !!!!!!!!!!
      final memoUsrID = memo.data['usrID'];
      final memoUsrPW = memo.data['usrPW'];
      final memoText = memo.data['text'];
      final memoCreateTime = memo.data['createTime'];

      final memoMaterial = MemoMaterial(
        logInUsrEmail: widget.logInUsrEmail,
        doc: memoDocumentID,
        title: memoTitle,
        usrID: memoUsrID,
        usrPW: memoUsrPW,
        text: memoText,
        createTime: memoCreateTime,
      );
      memoList.add(memoMaterial);
    

    return Expanded(
      child: new ListView.builder(

【问题讨论】:

您不能使用异步构建方法。在定义 memoTitle 时,您应该编辑您的流并产生结果。有关处理流的帮助,请参阅 the docs。 要编辑流的部分是"stream: _fireStore .collection (widget.logInUsrEmail) .orderBy('id', descending: false) .snapshots()(),"你的意思是?跨度> 【参考方案1】:

你应该这样做:

  Stream<List<MemoMaterial>> memosStream;

  Future<MemoMaterial> generateMemoMaterial(Memo memo) async 
    final memoTitle =
        await PlatformStringCryptor().decrypt(memo.data['title'], _key);

    return MemoMaterial(
      logInUsrEmail: widget.logInUsrEmail,
      doc: memo.documentID,
      title: memoTitle,
      usrID: memo.data['usrID'],
      usrPW: memo.data['usrPW'],
      text: memo.data['text'];,
      createTime: memo.data['createTime'],
    );
  

  @override
  void initState() 
    memosStream = _fireStore
            .collection(widget.logInUsrEmail)
            .orderBy('id', descending: false)
            .snapshots()
            .asyncMap((memos) => Future.wait([for (var memo in memos) generateMemoMaterial(memo)]));
    super.initState();
  
  @override
  Widget build(BuildContext context) 
  return StreamBuilder<List<MemoMaterial>>(
    stream: memosStream // Use memostream here

asyncMap() 会将每组新文档“转换”为一个 MemoMaterial 列表,并在执行操作时将该列表发送到流中。

Future.wait() 允许同时执行多个异步请求。

【讨论】:

感谢您的良好回复。这很有帮助。但是还有一个问题。即使您使用清单定义 memoStream,它仍然会在列表中显示为 null。有什么问题? 这不应该是.asyncMap((memos) =&gt; Future.wait([for (var memo in memos)...(备忘录而不是文档)吗? @AugustinR 我在“for (var memo in memos)”语句中收到memos 的错误。错误是“'for' 循环中使用的类型 'QuerySnapshot>' 必须实现 Iterable。”请提出问题是什么以及我们如何解决它。谢谢。 我们必须将它从“[for (var memo in memos)”更改为“[for (var memo in memos.docs)”来解决这个问题。谢谢。【参考方案2】:

您可以通过以下方式在 StreamBuilder 中使用 FutureBuilder 来完成。

Stream<List<int>> callme() async* 
    yield [1, 2, 3, 4, 5, 6];
  

  buildwidget() async 
    await Future.delayed(Duration(seconds: 1));
    return 1;
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      body: Container(
        child: StreamBuilder(
            stream: callme(),
            builder: (_, sna) 
              if (sna.hasData) 
                return FutureBuilder(
                  future: buildwidget(),
                  builder: (_, snap) 
                    if (snap.hasData) 
                      return ListView.builder(
                        itemCount: sna.data.length,
                        itemBuilder: (_, index) 
                          return Text("$sna.data[index] and $snap.data");
                        ,
                      );
                     else 
                      return CircularProgressIndicator();
                    
                  ,
                );
               else 
                return CircularProgressIndicator();
              
            ),
      ),
    );
  

【讨论】:

【参考方案3】:

如果 UI 依赖于异步函数,我更喜欢使用 GetxProvider 状态管理来处理 UI。 假设您想使用 StreamBuilder() 从 firebase 获取数据,它返回一些包含图像链接的文档,然后您想下载这些图像并从存储中显示。显然,下载图像是异步工作。然后,如果您显示带有直接从StreamBuilder() 获得的链接的图像,则会出现错误。

您可以做的是在getx 或provider 中设置一个变量来显示或隐藏图像Widget。如果图像正在下载或未下载,则将变量设置为在异步类型的函数完成时隐藏/显示图像。

【讨论】:

以上是关于Flutter - 如何在流构建器中使用 await?的主要内容,如果未能解决你的问题,请参考以下文章

Flutter/Firestore - 在流构建器中的动态列表视图上实现“分页”

Flutter 流构建器概念

Flutter StreamBuilder ListView在流数据更改时不会重新加载

Flutter BLoC 模式 - 如何在流事件后导航到另一个屏幕?

在 Flutter 的流构建器中使用 Future 构建器是正确的

Flutter:当我抛出异常时,构建器在未来的构建器中调用了两次