首页 > 解决方案 > Flutter StreamBuilder 从先前的生成器流生成快照

问题描述

我有一个 StreamBuilder 包裹在另一个 StreamBuilder 中。内部 StreamBuilder 将分页异步查询的结果加载到 ListView 中。外部 StreamBuilder 使用用户指定的搜索文本发出一个新查询,并构建一个新的内部 StreamBuilder 来监听新查询。该查询使用生成器函数生成流。

我注意到一些非常奇怪的行为:当发出一个新查询并使用新 Stream 重建内部 StreamBuilder 时,它最初从前一个 Stream 接收数据。这在新 Stream 为空时尤其明显(例如,用户的查询没有产生任何结果)。如果新的 Stream 是空的,它会从ConnectionState.waitingto 开始ConnectionState.done,并且两个事件(我认为是错误的)都填充了来自前一个生成器 Stream 的数据。

这是我编写的一些代码,以尽可能孤立的方式重现这一点。为简单起见,我用 FutureBuilder 替换了外部 StreamBuilder,尽管行为是相同的。

import 'dart:async';

import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final Future<bool> oneSecondFuture = Future.delayed(Duration(seconds: 2), () {
    print('future completed!');
    return true;
  });

  @override
  Widget build(BuildContext context) {
    Stream<int> intStream(bool returnValues) async* {
      var lst = [1, 2, 3];
      var i = 0;
      while (returnValues) {
        if (i == lst.length) break;
        yield lst[i++];
        await Future.delayed(Duration(milliseconds: 40));
      }
    }

    print('building');
    var futureBuilder = FutureBuilder(
      future: oneSecondFuture,
      builder: (_, boolSnap) {
        // If the future isn't complete yet, return a StreamBuilder with a nonempty stream
        if (!boolSnap.hasData) return StreamBuilder(
            stream: intStream(true),
            builder: (_, intSnap) {
              print('Nonempty steambuilder: ' + intSnap.connectionState.toString() + '; ' + intSnap.data.toString());
              return Text(intSnap.data.toString());
            }
        );

        // If the future is complete, return a StreamBuilder with an empty stream
        return StreamBuilder(
            stream: intStream(false),
            builder: (_, intSnap) {
              print('Empty steambuilder: ' + intSnap.connectionState.toString() + '; ' + intSnap.data.toString());
              return Text(intSnap.data.toString());
            }
        );
      }
    );

    return MaterialApp(
      title: 'Test StreamBuilder',
      home: futureBuilder,
    );
  }
}

上面的代码产生以下打印输出:

I/flutter (20225): building
I/flutter (20225): Nonempty streambuilder: ConnectionState.waiting; null
I/flutter (20225): Nonempty streambuilder: ConnectionState.active; 1
I/flutter (20225): Nonempty streambuilder: ConnectionState.active; 2
I/flutter (20225): Nonempty streambuilder: ConnectionState.active; 3
I/flutter (20225): Nonempty streambuilder: ConnectionState.done; 3
I/flutter (20225): building
I/flutter (20225): Nonempty streambuilder: ConnectionState.waiting; 3
I/flutter (20225): Nonempty streambuilder: ConnectionState.active; 1
I/flutter (20225): Nonempty streambuilder: ConnectionState.active; 2
I/flutter (20225): Nonempty streambuilder: ConnectionState.active; 3
I/flutter (20225): Nonempty streambuilder: ConnectionState.done; 3
I/flutter (20225): future completed!
I/flutter (20225): Empty streambuilder: ConnectionState.waiting; 3
I/flutter (20225): Empty streambuilder: ConnectionState.done; 3

请注意,在等待连接时,第二个构建从最后一个流的值“3”开始。空流为等待和完成事件产生一个值 3。

如果我创建第二个相同的 intStream 函数(即 intStream 和 intStreamTwo)并在未来完成后调用它,我会按预期获得空数据值,因此问题似乎源于重复调用单个生成器函数以获取不同的流:

I/flutter (20225): building
I/flutter (20225): Nonempty streambuilder: ConnectionState.waiting; null
I/flutter (20225): Nonempty streambuilder: ConnectionState.active; 1
I/flutter (20225): Nonempty streambuilder: ConnectionState.active; 2
I/flutter (20225): Nonempty streambuilder: ConnectionState.active; 3
I/flutter (20225): Nonempty streambuilder: ConnectionState.done; 3
I/flutter (20225): building
I/flutter (20225): Nonempty streambuilder: ConnectionState.waiting; 3
I/flutter (20225): Nonempty streambuilder: ConnectionState.active; 1
I/flutter (20225): Nonempty streambuilder: ConnectionState.active; 2
I/flutter (20225): Nonempty streambuilder: ConnectionState.active; 3
I/flutter (20225): Nonempty streambuilder: ConnectionState.done; 3
I/flutter (20225): future completed!
I/flutter (20225): using a different generator function:
I/flutter (20225): Empty streambuilder: ConnectionState.waiting; null
I/flutter (20225): Empty streambuilder: ConnectionState.done; null

这是预期的行为吗?有没有一种理智的方法可以让 StreamBuilder 不从以前的生成器 Stream 接收数据?我想我可以创建两个相同的生成器函数并在它们之间交替,但我真的想要一个不那么老套的解决方案。

标签: flutterdartgeneratorstream-builderdart-stream

解决方案


我通过更仔细地阅读文档回答了我自己的问题。这似乎是(令人难以置信的混乱)预期行为。请参阅文档

但是,我仍然不清楚为什么使用来自同一生成器函数的新流会触发此行为,而使用来自不同生成器函数的新流不会。

无论哪种方式,对于面临同样问题的其他人,我的预期工作是在 ConnectionState 为“等待”或“完成”时忽略错误的数据值,只要流为空(例如,“完成”快照未收到有效事件值的情况下收到)。


推荐阅读