在 flutter 中使用 bloc pattern 时的建议

Recommendation when using bloc pattern in flutter

当使用 flutter bloc 时,建议是什么?是否建议每个页面都有自己的 bloc,或者我可以为多个页面重用一个块,如果可以的话如何?

对此没有硬性规定。这取决于你想要完成什么。

例如:如果每个页面彼此 "radically",那么是的,每页 BLoC 是有意义的。如果页面之间需要某种共享或交互,您仍然可以在这些页面之间共享 "application-wide" BLoC

总的来说,我注意到 BLoC "per page" 通常很有用,因为您在其 BLoC 中处理的每个页面总是有相关的特定内容。您可以使用通用 BLoC 在它们之间共享数据或其他一些常见的东西。

您可以将 BLoC 模式与 RxDart 结合使用,以处理 BLoC 和 UI 之间更复杂的交互场景。


共享 BLoC 相当简单,只需将它们嵌套或使用 MultiProvider(来自 provider package):

runApp(
    BlocProvider(
      builder: (_) => SettingsBloc(),
      child: BlocProvider(
        builder: (_) => ApplicationBloc(),
        child: MyApp()
      )
    )
);

然后您可以通过 Provider:

检索它们
class MyApp extends ... {
  @override
  Widget build(BuildContext context) {
    final settingsBloc = Provider.of<SettingsBloc>(context);
    final appBloc = Provider.of<ApplicationBloc>(context);

    // do something with the above BLoCs
  }
}

您可以使用 BlocProvider 在不同的页面中共享不同的 bloc。

让我们定义一些 RootWidget 来负责保存所有 Bloc。

class RootPage extends StatefulWidget {
  @override
  _RootPageState createState() => _RootPageState();
}

class _RootPageState extends State<RootPage> {
  NavigationBloc _navigationBloc;
  ProfileBloc _profileBloc;
  ThingBloc _thingBloc;

  @override
  void initState(){
    _navigationBloc = NavigationBloc();
    _thingBloc = ThingBloc();
    _profileBloc = ProfileBloc();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
        providers: [
          BlocProvider<NavigationBloc>(
              builder: (BuildContext context) => _navigationBloc
          ),
          BlocProvider<ProfileBloc>(
              builder: (BuildContext context) => _profileBloc
          ), 
          BlocProvider<ThingBloc>(
              builder: (BuildContext context) => _thingBloc
          ),
    ],
      child: BlocBuilder(
        bloc: _navigationBloc,
        builder: (context, state){
          if (state is DrawProfilePage){
            return ProfilePage();
          } else if (state is DrawThingsPage){
            return ThingsPage();
          } else {
            return null
          }
        }
      )
    )
  }
}

之后,我们可以使用来自父级的任何 bloc,所有小部件将共享相同的状态,并且可以在同一个 bloc 上调度事件

class ThingsPage extends StatefulWidget {
  @override
  _ThingsPageState createState() => _ThingsPageState();
}

class _ThingsPageState extends State<ThingsPage> {
  @override
  void initState(){
    _profileBloc = BlocProvider.of<ProfileBloc>(context);
    super.initState();
  }
  @override
  Widget build(BuildContext context) {

    return Container(
        child: BlocBuilder(
          bloc: _profileBloc,
          builder: (context, state){
            if (state is ThingsAreUpdated){
              return Container(
                  Text(state.count.toList())
              );
            } else {
              return Container()
            }
          }
      )
    );
  }
}

我认为最好的解决方案是每页有一个 BLoC。通过查看其 BLoC,它可以帮助您始终了解每个屏幕处于哪种状态。如果您想独立显示每个选项卡的状态,您应该为每个选项卡创建一个 BLoC,并创建一个将处理数据获取的存储库。但是,如果每个选项卡的状态都相同(例如,您只为所有屏幕获取一次数据,所以您不会在每个选项卡上显示加载屏幕)那么我认为您可以只为所有创建一个 BLoC此选项卡的数量。

还值得补充的是,BLoC 可以相互通信。因此,您可以从一个 BLoC 获取另一个 BLoC 的状态,或监听其状态变化。当您决定为选项卡创建单独的 BLoC 时,这可能会有所帮助。

我已经在 latest article 中讨论了这个主题。如果您想深入了解,可以查看一下。