写点什么

不到 40 行代码手撸一个 BlocProvider

作者:岛上码农
  • 2022 年 6 月 26 日
  • 本文字数:3202 字

    阅读完需:约 11 分钟

不到40行代码手撸一个BlocProvider

前言

上一篇我们对 BLoC 做了整体的介绍,了解了 BLoC 的使用后,心里不禁有点痒痒,写了好几个状态管理插件了,能不能利用 BLoC 自己也撸一个 Flutter 状态管理组件,用来构建基于 BLoC 的响应式 Flutter 页面。说干就干,我们给这个状态管理组件命名为 SimpleBlocProvider

SimpleBlocProvider定义

对于这个简单的状态管理组件 SimpleBlocProvider,因为需要放置到组件树中,因此肯定是一个 Widget,由于内部还需要维护数据,因此我们使用 StatefulWidget。等等!不是不推荐使用 StatefulWidget 吗?需要注意,如果这个组件自己维护自身状态,不影响外部组件那是没问题的。比如说,一个按钮点击后会有点击效果,这个其实也需要使用 StatefulWidget 实现,但是这个行为只会引起自身刷新,那是没问题的。



其次,我们要把原先 UI 组件的构建放到SimpleBlocProvider中来,那就需要定义一个 构建组件的 builder 参数,所有原先 UI 组件的构建都由这个 builder 来完成,同时这个 builder 应该携带最新的状态数据,以便更新 UI 组件。而且,状态数据是确定不了类型的,因此这个 builder 应该是返回 Widget 的泛型函数,定义如下。


typedef StateBuilder<T> = Widget Function(T state);
复制代码


比如显示计数器的 Text,我们可以这么写:


SimpleBlocProvider<int> (  builder: (count) => Text('$count'),)
复制代码


光有 builder 还不够,我们需要 Bloc 逻辑组件,以便从逻辑组件里获取最新的状态数据,因此需要将 Bloc 逻辑组件也作为参数给 SimpleBlocProvider。于是我们就得到了SimpleBlocProvider的基本定义了。


class SimpleBlocProvider<T> extends StatefulWidget {  final StateBuilder<T> builder;  final BlocBase<T> bloc;  const SimpleBlocProvider(      {Key? key, required this.builder, required this.bloc})      : super(key: key);
@override _SimpleBlocProviderState<T> createState() => _SimpleBlocProviderState<T>();}
复制代码

BLoC 刷新

要实现 BLoC 刷新,我们需要监听 BLoC 状态数据的变化,从上一篇我们知道 BLoC 是基于 Stream 实现的,对于 Stream,可以使用其 listen 方法来监听 Stream 流数据的变化。listen 方法定义如下:


 StreamSubscription<T> listen(void onData(T event)?,      {Function? onError, void onDone()?, bool? cancelOnError});
复制代码


因此,我们可以在 listenonData 中调用 setState 就可以做到刷新界面了。我们组件销毁的时候需要取消监听,因此我们在_SimpleBlocProviderState 中定义一个属性_streamSubscription存储 listen 方法的返回值,并在 dispose 中取消监听。


_streamSubscription = widget.bloc.stream.listen((data) {  setState(() {    _state = data;  });});
//
@overridevoid dispose() { _streamSubscription.cancel(); super.dispose();}
复制代码


接下来就比较简单了,在_SimpleBlocProviderStatebuild 方法中直接返回 builder 携带状态数据_state构建组件即可。


@overrideWidget build(BuildContext context) {  return widget.builder(_state);}
复制代码


这样,只要 BLoC 的状态数据发生了改变,就会通过 listen监听更新SimpleBlocProvider_state,并刷新SimpleBlocProvider组件,从而更新了 builder 构建的组件。完整代码如下:


typedef StateBuilder<T> = Widget Function(T state);
class SimpleBlocProvider<T> extends StatefulWidget { final StateBuilder<T> builder; final BlocBase<T> bloc; const SimpleBlocProvider( {Key? key, required this.builder, required this.bloc}) : super(key: key);
@override _SimpleBlocProviderState<T> createState() => _SimpleBlocProviderState<T>();}
class _SimpleBlocProviderState<T> extends State<SimpleBlocProvider<T>> { late T _state; late StreamSubscription<T> _streamSubscription;
@override void initState() { _state = widget.bloc.state; super.initState(); _streamSubscription = widget.bloc.stream.listen((data) { setState(() { _state = data; }); }); }
@override Widget build(BuildContext context) { return widget.builder(_state); }
@override void dispose() { _streamSubscription.cancel(); super.dispose(); }}
复制代码


总共不到 40 行代码就搞定了

SimpleBlocProvider 应用

现在来看怎么用,我们先来一个计数器看看。


class CounterCubit extends Cubit<int> {  CounterCubit({initial = 0}) : super(initial);
void increment() => emit(state + 1); void decrement() => emit(state - 1);
@override void onChange(Change<int> change) { super.onChange(change); }}
class SimpleBlocCounterPage extends StatelessWidget { final counter = CounterCubit(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Bloc 计数器'), ), body: Center( child: SimpleBlocProvider<int>( builder: (count) => Text( '$count', style: TextStyle( fontSize: 32, color: Colors.blue, ), ), bloc: counter, ), ), floatingActionButton: FloatingActionButton( onPressed: () { counter.increment(); }, tooltip: '点击增加', child: Icon(Icons.add), ), ); }}
复制代码



是不是和我们之前在使用 MobX,GetX 的 GetBuilder 很类似?再来看自定义类,来个简单的 Person 类,然后用 Bloc 的 event 模式试试。


class Person {  final String name;  final String gender;
const Person({required this.name, required this.gender});}
abstract class PersonEvent {}
class UsingCnNameEvent extends PersonEvent {}
class UsingEnNameEvent extends PersonEvent {}
class PersonBloc extends Bloc<PersonEvent, Person> { PersonBloc(Person person) : super(person) { on<UsingCnNameEvent>( (event, emit) => emit(Person(name: '岛上码农', gender: '男'))); on<UsingEnNameEvent>( (event, emit) => emit(Person(name: 'island-coder', gender: 'male'))); }}
class SimpleBlocCounterPage extends StatelessWidget { final personBloc = PersonBloc(Person(name: '岛上码农', gender: '男')); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Bloc 事件'), ), body: Center( child: SimpleBlocProvider<Person>( builder: (person) => Text( '姓名:${person.name},性别:${person.gender}', style: TextStyle( fontSize: 22, color: Colors.blue, ), ), bloc: personBloc, ), ), floatingActionButton: FloatingActionButton( onPressed: () { personBloc.add(UsingEnNameEvent()); }, tooltip: '点击增加', child: Icon(Icons.add), ), ); }}
复制代码


运行起来也是没问题的。

总结

本篇介绍了使用 BLoC 实现简单状态管理的 SimpleBLocProvider,这个自定义的 BlocProvider 不到 40 行,当然这个代码距离实际使用还有差距,但是对于了解一下这些第三方状态管理插件的实现机制还是有帮助的。接下来我们将使用官方的 flutter_bloc 插件来讲具体的应用实例。


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

岛上码农

关注

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

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

评论

发布
暂无评论
不到40行代码手撸一个BlocProvider_flutter_岛上码农_InfoQ写作社区