如何使用 Provider 为 PageView() 提供一个块,而无需每次切换页面时孩子都重新订阅?

Posted

技术标签:

【中文标题】如何使用 Provider 为 PageView() 提供一个块,而无需每次切换页面时孩子都重新订阅?【英文标题】:How can I use Provider to provide a bloc to a PageView() without the child resubscribing everytime I switch page? 【发布时间】:2020-08-11 18:01:55 【问题描述】:

我正在使用 Provider 通过静态创建方法将我的块提供给名为 TheGroupPage 的小部件

  static Widget create(BuildContext context, GroupModel group) 
    final database = Provider.of<DatabaseService>(context);
    return Provider(
      create: (_) => GroupMembersBloc(database, group),
      child: TheGroupPage(group),
      dispose: (BuildContext context, GroupMembersBloc bloc) => bloc.dispose(),
    );
  

那个小部件有一个包含 3 页的 PageView()

  PageView(children: [
    TheGroupNotificationsView(),
    TheGroupMembersView(group),
    TheGroupSettingsView(group),
  ])

组成员视图查找 GroupMembersBloc

GroupMembersBloc bloc = Provider.of<GroupMembersBloc>(context);

我也尝试将 listen 设置为 false,但这没有用。我希望小部件能够监听任何变化。该页面使用该 bloc 的流来绘制组成员列表

class GroupMembersBloc

  StreamController<List<UserModel>> _controller = StreamController<List<UserModel>>();
  Stream<List<UserModel>> get stream => _controller.stream;

  GroupMembersBloc(DatabaseService database, GroupModel group)
  
   _controller.addStream(database.groupMembersStream(group));
  

  void dispose()
    _controller.close();
  

问题是当我在 PageView() 内切换页面时,在页面第一次显示后出现错误。错误提示 Bad state: Stream has already been listened to. 我该如何解决?

【问题讨论】:

【参考方案1】:

这是因为流控制器只允许 1 个订阅(或 1 个侦听器),您可以使用 [StreamController&lt;List&lt;UserModel&gt;&gt;.broadcast()][1] 构造函数代替 StreamController>()。

【讨论】:

是的,但我只有 1 个页面可以收听流。那么为什么要让它成为广播流呢?我会有多个不必要的听众。使用 PageView() 切换页面时没有办法停止监听吗?【参考方案2】:

我最终将 StreamBuilder 移动到了解决问题的 PageView() 上方的父小部件。

【讨论】:

以上是关于如何使用 Provider 为 PageView() 提供一个块,而无需每次切换页面时孩子都重新订阅?的主要内容,如果未能解决你的问题,请参考以下文章

PageView:禁用默认滚动并将其替换为 Tap 事件

如何去除材质 PageView 组件中的飞溅?

如何在颤动的pageView中使用AnimatedCrossFade多个小部件?

如何在 PageView 中拥有不同大小的页面?

Flutter中如何在PageView中正确使用GoogleMap

Flutter - 如何制作 PageView 和 ListView?