Flutter的一个更精简的状态管理工具:consumer

consumer 是一个参考 react-consumer 方式的状态管理, 使用 dart 的 Stream 作发布订阅.react

类 react 项目,当项目到必定程度,必不可少须要一个状态管理器,flutter 有着很多状态管理库,BLOC、Provider、redux 等等;可是他们现有的问题是没有给出很便捷的状态管理优化方案。git

consumer 的特色是仅仅是发布订阅模式加 StateFulWidget,这比市面上基于 InheritedWidget 进行封装的状态管理器的优点是它不须要一个顶层的提供者模式的包裹。基于此,consumer 可让项目更简单建立子模块的独立的状态管理,固然你也可使用 consumer 的单一模式管理整个项目的状态。github

在这个前提下,咱们会发现若项目足够大,咱们须要切分多个子状态管理,或者一些局部的状态管理,这样能够有效减小事件派发的影响范围,从而提升性能;consumer 另外一个特色是强制使用者描述每一个订阅所使用的对象,这样 consumer 能够帮助优化性能,拦截没必要要的更新。redux

Feature

  • consumer 不须要一个顶层的 Provider 包裹对象;
  • consumer 能够很轻松的给子模块设置独立的状态管理;
  • consumer 使用 memo 拦截没必要要的更新,从 react.Hooks 获得的灵感;
  • consumer 很是易于使用, 仅有 3 个 API:
    • getState
    • setState
    • build

API 文档:数组

安装 consumer

修改 pubspec.yaml:app

dependencies:
 consumer: ^1.0.2
复制代码

入门指南

这是一个 Flutter 默认的初始化项目,咱们使用 consumer 改造它,移除 StateFulWidget,替换成 StatelessWidget:less

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

// *** 定义一个类,描述状态 ***
class ExampleState {
  int counter = 0;
}

// *** 建立一个 consumer ***
var consumer = Consumer(ExampleState());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Consumer Demo Home Page'),
    );
  }
}

class MyHomePage extends StatelessWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  _incrementCounter() {
    // *** 使用 setState ,触发订阅的组件更新 ***
    consumer.setState((state) => state.counter++);
  }

  @override
  Widget build(BuildContext context) {
    print('整个对象仅更新一次,更新时仅更新订阅的组件');

    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            // *** 使用 consumer.build 订阅一个组件 ***
            consumer.build(
              memo: (state) => [state.counter],
              builder: (ctx, state) {
                print('仅有 memo 返回值中的对象改变了,builder 对象才会更新');
                return Text(
                  '$state.counter',
                  style: Theme.of(context).textTheme.display1,
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

复制代码

FAQ

参数 memo 的做用是什么?

若是你项目有着很是多的状态订阅,使用 memo 能够大幅度提升性能;因此 memo 设计为必须定义的参数。ide

memo 的概念是来自于 react.Hooks, 它用来描述监听变化的对象,仅有监听对象变化时,才会派发更新。函数

一个原则是,咱们在 builder 对象中须要使用什么属性,memo 返回的数组就定义什么属性, 咱们这里有一些例子:性能

若是咱们由 consumer.build 建立的两个 widget:

// *** definition a state ***
class ExampleState {
  List<String> animates = [];
  int age = 0;
  String name = 'dog';
}

// *** create a consumer ***
var consumer = Consumer(ExampleState());

Column(
  children: <Widget>[
    consumer.build(
      memo: (state) => [state.age, state.animates],
      builder: (ctx, state) {
        print('Update when state.age change');
        return Text(
          '$state.age',
          style: Theme.of(context).textTheme.display1,
        );
      },
    ),
    consumer.build(
      memo: (state) => [state.name],
      builder: (ctx, state) {
        print('Update when state.name change');
        return Text(
          state.name,
          style: Theme.of(context).textTheme.display1,
        );
      },
    ),
  ],
);
复制代码

而后咱们更新 state.name:

consumer.setState((state){
  state.name = 'cat';
});
复制代码

此时,当咱们更新 state.name,只有订阅了 memo: (state) => [state.name] 的 widget 会更新,其余 Widget 的更新都会被 consumer 拦截。

为何个人使用了 consumer.setState 以后 Widget 并无更新?

或许你在 builder 中使用了 state.name, 不过 memo 返回的数组未包含 state.name:

Center(
  child: consumer.build(
    memo: (state) => [state.age],
    builder: (ctx, state) {
      return Text(
        state.name,
        style: Theme.of(context).textTheme.display1,
      );
    },
  ),
);
复制代码

或许你的 memo 未监放任何对象:

Center(
  child: consumer.build(
    memo: (state) => [],
    builder: (ctx, state) {
      return Text(
        state.name,
        style: Theme.of(context).textTheme.display1,
      );
    },
  ),
);
复制代码

或许你仅仅是改变了 List 或 Map 内的对象,可是没有从新设定一个新的 List 或 Map:

class ExampleState {
  List<String> names = ['dog', 'cat'];
}

var consumer = Consumer(ExampleState());

Center(
  child: consumer.build(
    memo: (state) => [state.names],
    builder: (ctx, state) {
      return Text(
        state.names[0],
        style: Theme.of(context).textTheme.display1,
      );
    },
  ),
);

// 错误的更新:
Consumer.setState((state){
  state.names[0] = 'fish'
});

// 正确的更新:
Consumer.setState((state){
  List<String> names = [...state.names];
  names[0] = 'fish'
  state.names = names;
});
复制代码

State 小技巧

若是你须要在更新以前作一些计算, 或者更方便处理数组之类的更新,你能够建立一些函数属性给 State:

这里有一个修改 List 数据的例子:

class ExampleState {
  int lastChangeNamesIndex;
  List<String> names = ['dog', 'cat'];

  changeNameAt(int index, String name) {
    lastChangeNamesIndex = index;
    List<String> nextNames = [...names];
    nextNames[index] = name;
    names = nextNames;
  }
}

var consumer = Consumer(ExampleState());

Center(
  child: consumer.build(
    memo: (state) => [state.names, state.lastChangeNamesIndex],
    builder: (ctx, state) {
      return Text(
        state.names[state.lastChangeNamesIndex],
        style: Theme.of(context).textTheme.display1,
      );
    },
  ),
);

// 轻松更新 names 和 lastChangeNamesIndex
consumer.setState((state){
  state.changeNameAt(0, 'monkey');
})
复制代码

That's all

感谢你阅读本文档和使用 consumer.

相关文章
相关标签/搜索