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>(); }
StreamSubscription<T> listen(void onData(T event)?, {Function? onError, void onDone()?, bool? cancelOnError});因此,我们可以在 listen 的 onData 中调用 setState 就可以做到刷新界面了。我们组件销毁的时候需要取消监听,因此我们在_SimpleBlocProviderState 中定义一个属性_streamSubscription存储 listen 方法的返回值,并在 dispose 中取消监听。
_streamSubscription = widget.bloc.stream.listen((data) { setState(() { _state = data; }); }); // @override void dispose() { _streamSubscription.cancel(); super.dispose(); }接下来就比较简单了,在_SimpleBlocProviderState的 build 方法中直接返回 builder 携带状态数据_state构建组件即可。
@override Widget build(BuildContext context) { return widget.builder(_state); }这样,只要 BLoC 的状态数据发生了改变,就会通过 listen监听更新SimpleBlocProvider的_state,并刷新SimpleBlocProvider组件,从而更新了 builder 构建的组件。完整代码如下:
typedef StateBuilder<T> = Widget Function(T state); // 堆代码 duidaima.com 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 行代码就搞定了!
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 模式试试。
// 堆代码 duidaima.com 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), ), ); } }运行起来也是没问题的。