写点什么

BlocProvider 为什么感觉和 Provider 很相似?

作者:岛上码农
  • 2022 年 7 月 01 日
  • 本文字数:2063 字

    阅读完需:约 7 分钟

BlocProvider 为什么感觉和 Provider 很相似?

前言

上一篇不到 40 行代码手撸一个 BlocProvider,我们只用了不到 40 行代码手撸了一个 SimpleBlocProvider,用起来感觉也还可以,这就是 Bloc 的优势之一,你可以基于 Bloc 这种模式自己做扩展,形成自己的状态管理组件。我们这一篇来看看官方 flutter_bloc 插件提供的 BlocProvider 怎么样。

Provider 的模仿者?

我们先来看 BlocProvider 实现的计数器示例代码:


class BlocCounterWrapper extends StatelessWidget {  @override  Widget build(BuildContext context) {    return BlocProvider(      create: (_) => CounterCubit(),      child: BlocCounterPage(),    );  }}
class BlocCounterPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Bloc 计数器'), ), body: Center( child: BlocBuilder<CounterCubit, int>( builder: (context, count) => Text( '$count', style: TextStyle( fontSize: 32, color: Colors.blue, ), ), ), ), floatingActionButton: FloatingActionButton( onPressed: () { context.read<CounterCubit>().increment(); }, tooltip: '点击增加', child: Icon(Icons.add), ), ); }}
复制代码


这代码是不是十分熟悉?如果回顾我们之前的 Provider 状态管理,感觉是十分相似啊!比如:


Widget build(BuildContext context) {  return BlocProvider(    create: (_) => CounterCubit(),    child: BlocCounterPage(),  );}
复制代码


Provider 里实现就是下面这样。


Widget build(BuildContext context) {  return Provider(    create: (_) => CounterCubit(),    child: BlocCounterPage(),  );}
复制代码


而在 FloatingActionButton 里的 context.read 用法也和 Provider 里的一样。所以,如果要从 Provider 迁移到 Bloc 也是十分简单的。实际从源码逐层向上,你会发现里面就有 provider 的代码。


library flutter_bloc;
export 'package:bloc/bloc.dart';export 'package:provider/provider.dart' show ProviderNotFoundException, ReadContext, SelectContext, WatchContext;
复制代码


而从 Providerbloc 的 GitHub 源码贡献者中我们也发现 Provider 的代码贡献者 Felix Angelov 就是 bloc 代码的发起者。。




所以这就不奇怪 Bloc 中使用 Provider 了。

与 Provider 的异同

BlocProviderProvider一样,通过组件树为子组件提供状态数据。也就是我们可以像使用 Provider 一样使用 BlocProvider,比如使用已有的状态对象时的哟用法和 Provider 一样使用 value 参数赋值:


final counter =  CounterCubit();//...BlocProvider.value(  value: counter,  child: SomeWidget(),);
复制代码


在比如可以在子组件中使用 context.readcontext.watch。以及使用 select 方法监听状态数据的部分变化。


final isPositive = context.select((CounterBloc b) => b.state >= 0);
复制代码


还有 MultiBlocProvider来向子组件树传递多个 Bloc 状态对象。


MultiBlocProvider(  providers: [    BlocProvider<BlocA>(      create: (BuildContext context) => BlocA(),    ),    BlocProvider<BlocB>(      create: (BuildContext context) => BlocB(),    ),    BlocProvider<BlocC>(      create: (BuildContext context) => BlocC(),    ),  ],  child: ChildA(),)
复制代码


可以说,如果之前你用 Provider 状态管理的话,可以无缝迁移到 BlocProvider。当然,状态对象类还是需要改一下的。这也是 BlocProviderProvider 的区别。比如状态对象不需要实现 ChangeNotifier 接口,在状态数据改变的时候也无需调用 notifyListeners 来通知刷新。实际我们从 BlocProvider 的源码扒出了一段监听状态对象实现的方法,这里其实也是使用我们在 SimpleBlocProvider 中使用的 Stream.listen 方法来实现的。


static VoidCallback _startListening(    InheritedContext<BlocBase?> e,    BlocBase value,  ) {    final subscription = value.stream.listen(      (dynamic _) => e.markNeedsNotifyDependents(),    );    return subscription.cancel;  }
复制代码


而其他部分的代码基本就是复用 Provider 的代码。所以 BlocProvider 可以看成是使用了 Stream.listen 监听状态数据的方式替换 ChangeNotiferProvider 的变种。因此,如果想从 Provider 切换到 BlocProvider,可以大胆地切换,代码修改工作量很低。而且无需在状态对象里反复调用 notifyListeners来通知界面刷新了。

总结

本篇介绍了 BlocProvider 的使用,从写法上来看,感觉不到太多的优势,个人估计这是作者想让 Provider 的用户可以直接切换到 BlocProvider 才提供了这么一个 Provider的替代品。不过,这只是冰山一脚,实际上,flutter_bloc 还提供了很多其他的选择,我们接下来会一一介绍。


发布于: 刚刚阅读数: 3
用户头像

岛上码农

关注

用代码连接孤岛,公众号@岛上码农 2022.03.03 加入

从南漂到北,从北漂到南的业余码农

评论

发布
暂无评论
BlocProvider 为什么感觉和 Provider 很相似?_flutter_岛上码农_InfoQ写作社区