56

Flutter中的状态管理

 5 years ago
source link: https://hicc.me/flutter-state-management/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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

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

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;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  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 的时候,你只能放在它们共有的祖先组件上,然后逐层传递,这样有势必会造成多余的组件更新。

6nUN3qm.jpg!web

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 {
  @override
  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 {
  @override
  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的方式虽说有缺陷倒也上手容易,已经能很好的解决问题,初学者不妨从它来开始……


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK