我正在尝试使用StreamProvider(来自this令人敬畏的软件包),但是我一直在努力使特定的流工作。

我创建了一个StreamController,用于通过其Stream将数据添加到其Sink中。所有这些似乎都工作正常。但是,当将此StreamStreamProvider一起使用时,小部件树不会反射(reflect)Stream的更改。但是,使用StreamBuilder确实可以正常工作。

使用StreamProvider的代码:

class TestPage extends StatelessWidget {
  final Mockup exchange = ExchangeApi.mockup;

  @override
  Widget build(BuildContext context) {
    return StreamProvider<List<Data>>(
      builder: (BuildContext context) => Wrapper.mockup.stream,
      initialData: null,
      catchError: (BuildContext context, e) {
        print("Error: $e");
        return null;
      },
      child: TestPageBody(),
    );
  }
}

class TestPageBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    List<Data> dataList = Provider.of<List<Data>>(context);
    return ListView.builder(
      itemCount: dataList?.length ?? 0,
      itemBuilder: (BuildContext context, int index) {
        return Text(dataList[index].name);
      },
    );
  }
}

我一直在寻找为什么这行不通,但是还没有找到答案。但是,我确实找到了一些东西:
  • 使用Flutter Desktop Embedding,当窗口调整大小时(因此强制重建),UI确实反射(reflect)了流中的更改。使用热刷新时,可以看到相同的效果。
  • 流正在不断添加新数据,我通过调试进行了测试,并简单地打印出了数据

  • 任何帮助将不胜感激!

    最佳答案

    大多数提供程序(不包括ChangeNotifierProvider)的默认行为是假定传递的值是不可变的。

    因此,如果您的流发出的值与先前发出的值相同,则不会重建依赖项。

    有两种解决方案:

  • 使您的流发出的值不可变,以便正确执行previousValue == newValue
  • 覆盖updateShouldNotify,以便在不更改值的情况下不对其进行过滤。

    一个简单的updateShouldNotify: (_, __) => true就可以了。


  • 在理想世界中,更喜欢不变性。

    这可以通过在将对象发送到streamController.add(value)之前制作对象的副本来完成:

    List<T> value;
    streamController.add(List.from(value));
    

    第二个(可选)步骤是重写updateShouldNotify(或对象上的operator==)以通知提供者该值没有更改。

    使用List,可以使用ListEquality中的collection/collection.dart来完成:

    import 'package:provider/provider.dart';
    import 'package:collection/collection.dart';
    
    StreamProvider<List<int>>(
      builder: (_) => stream,
      updateShouldNotify: const ListEquality<int>().equals,
    );
    

    10-08 03:15