写点什么

为了弄懂 Flutter 的状态管理,- 我用 10 种方法改造了 counter-app

用户头像
Android架构
关注
发布于: 刚刚

class CounterModel extends Model {int _counter = 0;


int get counter => _counter;


void increment() {_counter++;notifyListeners();}


void decrement() {_counter--;notifyListeners();}}


其中数据变化的部分会通知 listeners, 它们收到通知后会 rebuild.


在上层初始化并提供数据类, 用ScopeModel.


访问数据有两种方法:


  • ScopedModelDescendant包裹 widget.

  • ScopedModel.of静态方法.


使用的时候注意要提供泛型类型, 会帮助我们找到离得最近的上层ScopedModel.


ScopedModelDescendant<CounterModel>(builder: (context, child, model) {return Text(model.counter.toString(),);}),


数据改变后, 只有ScopedModelDescendant会收到通知, 从而 rebuild.


ScopedModelDescendant有一个rebuildOnChange属性, 这个值默认是 true.对于 button 来说, 它只是控制改变, 自身并不需要重绘, 可以把这个属性置为 false.


ScopedModelDescendant<CounterModel>(rebuildOnChange: false,builder: (context, child, model) {return FloatingActionButton(onPressed: model.increment,tooltip: 'Increment',child: Icon(Icons.add),);},),


scoped model 这个库帮我们解决了数据访问和通知的问题, 但是 rebuild 范围需要自己控制.


  • access state

  • notify other widgets

  • minimal rebuild -> X -> 因为需要开发者自己来决定哪一部分是否需要被重建, 容易被忘记.


代码分支:?scoped-model

Provider

Provider 是官方文档的例子用的方法.去年的 Google I/O 2019 也推荐了这个方法.和 BLoC 的流式思想相比, Provider 是一个观察者模式, 状态改变时要notifyListeners().


有一个 counter 版本的 sample: https://github.com/flutter/samples/tree/master/provider_counter


Provider 的实现在内部还是利用了InheritedWidget.Provider 的好处: dispose 指定后会自动被调用, 支持MultiProvider.

Provider 实现

  • model 类继承ChangeNotifer, 也可以用with.


class CounterModel extends ChangeNotifier {int value = 0;


void increment() {value++;notifyListeners();}


void decrement() {value--;notifyListeners();}}


  • 数据提供者:?ChangeNotifierProvider.


void main() => runApp(ChangeNotifierProvider(create: (context) => CounterModel(),child: MyApp(),));


  • 数据消费者/操纵者, 有两种方式:?Consumer包裹, 用Provider.of.


Consumer<CounterModel>(builder: (context, counter, child) => Text('${counter.value}',),),


FAB:


FloatingActionButton(onPressed: () =>Provider.of<CounterModel>(context, listen: false).increment(),),


这里listen置为 false 表明状态变化时并不需要 rebuild FAB widget.

Provider 性能相关的实现细节

  • Consumer包裹的范围要尽量小.

  • listen 变量.

  • child 的处理.?Consumer中 builder 方法的第三个参数.


可以用于缓存一些并不需要重建的 widget:


return Consumer<CartModel>(builder: (context, cart, child) => Stack(children: [// Use SomeExpensiveWidget here, without rebuilding every time.child,Text("Total price: ${cart.totalPrice}"),],),// Build the expensive widget here.child: SomeExpensiveWidget(),);


代码分支:?provider.

BLoC

BLoC 模式的全称是: business logic component.


所有的交互都是 a stream of asynchronous events.Widgets + Streams = Reactive.


BLoC 的实现的主要思路: Events in -> BloC -> State out.


Google I/O 2018 上推荐的还是这个, 2019 就推荐 Provider 了.当然也不是说这个模式不好, 架构模式本来也没有对错之分, 只是技术选型不同.

BLoC 手动实现

不添加任何依赖可以手动实现 BLoC, 利用:


  • Dart SDK > dart:async >?Stream.

  • Flutter 的StreamBuilder: 输入是一个 stream, 有一个 builder 方法, 每次 stream 中有新值, 就会 rebuild.


可以有多个 stream, UI 只在自己感兴趣的信息发生变化的时候重建.


BLoC 中:


  • 输入事件:?Sink<Event> input.

  • 输出数据:?Stream<Data> output.


CounterBloc 类:


class CounterBloc {int _counter = 0;


final _counterStateController = StreamController<int>();


StreamSink<int> get _inCounter => _counterStateController.sink;


Stream<int> get counter => _counterStateController.stream;


final _counterEventController = StreamController<CounterEvent>();


Sink<CounterEvent> get counterEventSink => _counterEventController.sink;


CounterBloc() {_counterEventController.stream.listen(_mapEventToState);}


void _mapEventToState(CounterEvent event) {if (event is IncrementEvent) {_counter++;} else if (event is DecrementEvent) {_counter--;}_inCounter.add(_counter);}


void dispose() {_counterStateController.close();_counterEventController.close();}}


有两个StreamController, 一个控制 state, 一个控制 event.


读取状态值要用StreamBuilder:


StreamBuilder(stream: _bloc.counter,initialData: 0,builder: (BuildContext context, AsyncSnapshot<int> snapshot) {return Text('${snapshot.data}',);},)


而改变状态是发送事件:


FloatingActionButton(onPressed: () => _bloc.counterEventSink.add(IncrementEvent()),),


实现细节:


  • 每个屏幕有自己的 BLoC.

  • 每个 BLoC 必须有自己的dispose()方法. -> BLoC 必须和StatefulWidget一起使用, 利用其生命周期释放.


代码分支:?bloc

BLoC 传递: 用 InheritedWidget

手动实现的 BLoC 模式, 可以结合InheritedWidget, 写一个 Provider, 用来做 BLoC 的传递.


代码分支:?bloc-with-provider

BLoC rxdart 实现

用了 rxdart package 之后, bloc 模块的实现可以这样写:


class CounterBloc {int _counter = 0;


final _counterSubject = BehaviorSubject<int>();


Stream<int> get counter => _counterSubject.stream;


final _counterEventController = StreamController<CounterEvent>();


Sink<CounterEvent> get counterEventSink => _counterEventController.sink;


CounterBloc() {_counterEventController.stream.listen(_mapEventToState);}


void _mapEventToState(CounterEvent event) {if (event is IncrementEvent) {_counter++;} else if (event is DecrementEvent) {_counter--;}_counterSubject.add(_counter);}


void dispose() {_counterSubject.close();_counterEventController.close();}}


BehaviorSubject也是一种StreamController, 它会记住自己最新的值, 每次注册监听, 会立即给你最新的值.


代码分支:?bloc-rxdart.

BLoC Library

可以用这个 package 来帮我们简化代码: https://pub.dev/packages/flutter_bloc


自己只需要定义 Event 和 State 的类型并传入, 再写一个逻辑转化的方法:


class CounterBloc extends Bloc<CounterEvent, CounterState> {@overrideCounterState get initialState => CounterState.initial();


@overrideStream<CounterState> mapEventToState(CounterEvent event) async* {if (event is IncrementEvent) {yield CounterState(counter: state.counter + 1);} else if (event is DecrementEvent) {yield CounterState(counter: state.counter - 1);}}}


BlocProvider来做 bloc 的传递, 从而不用在构造函数中一传到底.


访问的时候用BlocBuilderBlocProvider.of<CounterBloc>(context).


BlocBuilder(bloc: BlocProvider.of<CounterBloc>(context),builder: (BuildContext context, CounterState state) {return Text('${state.counter}',);},),


这里 bloc 参数如果没有指定, 会自动向上寻找.


BlocBuilder有一个参数condition, 是一个返回 bool 的函数, 用来精细控制是否需要 rebuild.


FloatingActionButton(onPressed: () =>BlocProvider.of<CounterBloc>(context).add(IncrementEvent()),),


代码分支:?bloc-library.

rxdart

这是个原始版本的流式处理.


和 BLoC 相比, 没有专门的逻辑模块, 只是改变了数据的形式.


利用 rxdart, 把数据做成流:


class CounterModel {BehaviorSubject _counter = BehaviorSubject.seeded(0);


get stream$ => _counter.stream;


int get current => _counter.value;


increment() {_counter.add(current + 1);}


decrement() {_counter.add(current - 1);}}


获取数据用StreamBuilder, 包围的范围尽量小.


StreamBuilder(stream: counterModel.stream,builder: (BuildContext context, AsyncSnapshot snapshot) {return Text('{snapshot.data}',);},),


Widget dispose 的时候会自动解绑.


数据传递的部分还需要进一步处理.


代码分支:?rxdart.

Redux

Redux 是前端流行的, 一种单向数据流架构.


概念:


  • Store: 用于存储State对象, 代表整个应用的状态.

  • Action: 事件操作.

  • Reducer: 用于处理和分发事件的方法, 根据收到的Action, 用一个新的State来更新Store.

  • View: 每次 Store 接到新的 State,?View就会重建.


Reducer是唯一的逻辑处理部分, 它的输入是当前StateAction, 输出是一个新的State.

Flutter Redux 状态管理实现

首先定义好 action, state:


enum Actions {Increment,Decrement,}


class CounterState {int _counter;


int get counter => _counter;


CounterState(this._counter);}


reducer 方法根据 action 和当前 state 产生新的 state:


CounterState reducer(CounterState prev, dynamic action) {if (action == Actions.Increment) {return new CounterState(prev.counter + 1);} else if (action == Actions.Decrement) {return new CounterState(prev.counter - 1);} else {return prev;}}


  • 数据提供者:?StoreProvider.放在上层:


StoreProvider(store: store,child: MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: MyHomePage(title: 'Flutter Demo Home Page'),),);


  • 数据消费者:?StoreConnector, 可读可写.


读状态:


StoreConnector<CounterState, String>(converter: (store) => store.state.counter.toString(),builder: (context, count) {return Text('$count',);},)


改变状态: 发送 action:


StoreConnector<CounterState, VoidCallback>(converter: (store) {return () => store.dispatch(action.Actions.Increment);},builder: (context, callback) {return FloatingActionButton(onPressed: callback,);},),


代码分支:?redux.

MobX

MobX 本来是一个 JavaScript 的状态管理库, 它迁移到 dart 的版本:?mobxjs/mobx.dart.


核心概念:


  • Observables

  • Actions

  • Reactions

MobX 状态管理实现

官网提供了一个 counter 的指导: https://mobx.netlify.com/getting-started


这个库的实现需要先生成一些代码.先写类:


import 'package:mobx/mobx.dart';


part 'counter.g.dart';


class Counter = _Counter w


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


ith _$Counter;


abstract class _Counter with Store {@observableint value = 0;


@actionvoid increment() {value++;}


@actionvoid decrement() {value--;

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
为了弄懂Flutter的状态管理,-我用10种方法改造了counter-app