hiccLoghicc log by wccHipo日志

Flutter中的状态管理

toc

Intro

Flutter作为出自Google的一个跨平台(iOS,Android)应用开发方案。布局方式上和React或者说React Native非常相似——组件(Widget)化。写起来非常的高效,却有着React Native所不具有的优势: 一套代码到处运行,原生渲染,原生调用,不需要像RN需要桥接。

前端应用除去布局部分,就属状态管理最复杂难搞了。官方文档中只是提及了最基础的部分,因此本文中着重讨论这部分。

下面基本上转述自Google I/O '18上视频Build reactive mobile apps with Flutter,内容较水,推荐大家看视频就够了😄


setSate

是的你没看错,就是和React中一模一样的setStae。Flutter将组件分为StatefulWidget,StatelessWidget,自然有状态的组件使用继承Flutter将组件为StatefulWidget。flutter create app开箱中代码就是实例了setSate

class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; _MyHomePageState createState() => new _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text(widget.title), ), body: new Center( child: new Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ new Text( 'You have pushed the button this many times:', ), new Text( '$_counter', style: Theme.of(context).textTheme.display1, ), ], ), ), floatingActionButton: new FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: new Icon(Icons.add), ), ); } }

写法也比较简单,适用于组件层比较浅的情况,但是如果需要跨组件共享state的时候,你只能放在它们共有的祖先组件上,然后逐层传递,这样有势必会造成多余的组件更新。

Build-reactive-mobile-apps-with-Flutter--Google-I-2FO--18--0001

InheritedWidget, context

逐层传递state太过于笨重,Flutter官方提供了InheritedWidget Class来去优化这个问题,基本上就是将需要共享的State放在一个继承InheritedWidget的类中,然后在使用的组件widget中直接取用就是。

明眼人一看便知,这就是React中Context。

正如React中有基于context的社区库Redux,正式使用时候InheritedWidget相对比较基础,你需要写一大堆模版类的代码来满足需求,因此推荐使用flutter社区的库scoped_model来方便开发。下面是库官方的例子:

class CounterModel extends Model { int _counter = 0; int get counter => _counter; void increment() { _counter++; notifyListeners(); } } class CounterApp extends StatelessWidget { Widget build(BuildContext context) { return new ScopedModel<CounterModel>( model: new CounterModel(), child: new Column(children: [ new ScopedModelDescendant<CounterModel>( builder: (context, child, model) => new Text( model.counter.toString()), ), new Text("Another widget that doesn't depend on the CounterModel") ]) ); } }

就是两个类ModelScopedModelDescendant,前者用来存储数据,后者用来包裹组件以此来提供state

值得注意的所有被包裹过的组件在状态变化的时候都会重新渲染,这样可能会造成不必要性能损失。ScopedModelDescendant也提供了阻止重新渲染的参数rebuildOnChange: false。**。

稍微了解过React的可以想得到,这个就类似于shouldComponentUpdate,不太建议使用,很容易滥用误用造成难以发现的bug。

StreamBuilder, ReactiveX

正如上文所说,状态管理很难,特别是异步环境下的状态管理更难,难在哪里?不外乎就是能够做到:

  • 方便拿到State
  • 能够将更新高效的通知给依赖组件
  • 能够精准的做到最小更新

想要更好解决这些问题,就需要引入Reactive响应的概念了。引用前端届的RxJS来说:

Observable = lodash for async

Flutter的官方语言Dart中内置了Stream的概念

Stream ~= Observable

因此不言而喻,就是将需要需要管理的State转化为Stream,然后使用Flutter官方的StreamBuilder来订阅所需要数据源,方便快捷,高效。

class MyTimer { static Stream<String> timerInterval$ = new Stream.periodic(Duration(seconds: 1)).map((int) => new DateTime.now().toString()); } class _RealTimeText extends StatelessWidget { Widget build(BuildContext context) { // TODO: implement build return StreamBuilder( stream: MyTimer.timerInterval$, builder: (context, snapshot) => Text(snapshot.data), ); } }

Rx相对Stream来说,提供了更多方法,社区中资料也多,dart社区也有RxDart, 正式使用还是少不了。

总结

上面的三种算是主流,官方推荐的Flutter 状态管理的方法了,Rx很强大,但是概念相对复杂,也相对难以掌控,Scope model的方式虽说有缺陷倒也上手容易,已经能很好的解决问题,初学者不妨从它来开始……