Flutter 状态管理(数据共享)

所有程序里界面和数据的交互都至关重要,直接决定了整个程序的结构,选好状态管理方案的重要性就不言而喻了
目前为止,Flutter 里的状态管理有很多的实现方法,官方也给出了很多的案例

官方对state的图释

官方给出的参考举例

  • setState
  • ChangeNotifier
  • Delegate
  • scoped_model -InheritedWidget
  • Sigslot
  • provide
  • flutter-provide
  • rx dart, fish redux, bloc
  • EventBus

setState

最原始最基本 最重要的方式 setState,支持规模较小的程序足够了,其它方式最终都需要调用 setState

Function callback

Flutter 内置 ChangeNotifier, ValueNotifier 都可以认为是类似方案

Delegate

可以认为是多个回调函数,其他语言里都有类似模式,名称似乎来源于 Objective-C。

Sigslot

源自 Qt 里的经典编程模式,Dart 可以轻易实现。这种方式在 Flutter 里可能根本不会有太多应用

pkg:scoped_model

个人以为是最佳方案,源自 Fuchsia 代码,在其中广泛使用,设置程序几乎都是这个模式,后来独立为 package,包括注释也只有 287 行代码。由于 Fuchsia App 结构都是后台Service+前台UI,这个方案绝对是最合适的方案。使用 InheritedWidget 实现,性能不可能更好。

pkg:provide

出自Flutter dev team,绝对的官方了,总共代码 675行。实现方式和 scoped_model 类似,增加 Providers,Provider 支持 Stream。

flutter-provide

可以认为这个比 provide 更早,功能更丰富,实现依然是 InheritedWidget。可能不会有太广泛使用,但是在时间上有历史意义,故列出。

rxdart, fish-redux,bloc

略……^ _ ^

EventBus

分享主体:

InheritedWidget

可能对InheritedWidget 比较陌生,但是在Flutter开发当中,我们几乎每个Page里都能看到的身影,并且用过它,,它可以高效的将数据在Widget树中向下传递、共享,这在一些需要在Widget树中共享数据的场景中非常方便,如Flutter中,正是通过InheritedWidget来共享应用主题(Theme)和Locale(当前语言环境)信息的。它是自上而下的

如:

 MediaQuery.of(context).size.width
 Theme.of(context).snackBarTheme.actionTextColor

使用姿势

  final InheritedWidgetModel model;

  //点击+号的方法
  final Function() increment;

  //点击-号的方法
  final Function() reduce;

  const MainInheritedWidget({Key key, Widget child, this.model, this.increment, this.reduce})
      : super(key: key, child: child);

  static MainInheritedWidget of(BuildContext context) {
    context.dependOnInheritedWidgetOfExactType<MainInheritedWidget>();
//    context.inheritFromWidgetOfExactType(MainInheritedWidget);
  }

  @override
  bool updateShouldNotify(MainInheritedWidget oldWidget) {
    return oldWidget.model != model;
  }

触发事件

//触发,完全是自定义
 MainInheritedWidget.of(context).increment();
//取数据
Text( "${MainInheritedWidget.of(context).model.count}",style: TextStyle(fontSize: 50 ,fontWeight: FontWeight.w900),),

scoped_model

短小精干的scoped_model,是以InheritedWidget为基础的开发,所以直接展示,它的读取在一个函数体里去操作

class Counter extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ScopedModelDescendant<CounterModel>(
      builder: (context, _, model) => ActionChip(
        label: Text('${model.count}'),//可以直接取
        onPressed: model.increaseCount,//可以直接改变
      ),
    );
  }
}

ChangeNotifier

通知Notification的发送是通过disPatch进行分发的,就好像Android里面的事件分发,当NotificationListener监听到了通知事件,这时候会走到其onNotification回调中,根据回调中的返回值类型(true还是false)来决定是否还继续向父亲节点发送通知。它是自下而上。

接受数据

NotificationListener<TestNotification>(
        onNotification: (notification) {
          setState(() {
            count = notification.count;
          });
          return true;
        },

发送数据

 TestNotification(count: count).dispatch(context);

我们需要注意的地方:

  Builder(
    builder: (context) {
      return RaisedButton(
        color: Colors.blue,
        child: Text('+'),
        onPressed: () {
          count++;
          TestNotification(count: count).dispatch(context);
        },
      );
    },
  )

为什么我们一定要用Builder 取构建一下?
原因是通知在分发的时候,需要一个context参数,这个参数指的是Notification监听的子widget的context,如果直接的话,context是根widget的,这样会导致监听不到子widget了。
所以需要我们通过Builder构建出我们子widget的context,

Provide

和scoped_model一样,Provide也是借助了InheritWidget,将共享状态放到顶层MaterialApp之上。底层部件通过Provier获取该状态,并通过混合ChangeNotifier通知依赖于该状态的组件刷新。Provide还提供了Provide.stream,让我们能够以处理流的方式处理数据

直接上才艺:

void main() {
 var counter = ProvideCounterModel();
 var providers = Providers();
 providers..provide(Provider<ProvideCounterModel>.value(counter));
 runApp(ProviderNode(
   child: ProvideApp(),
   providers: providers,
 ));

接受数据:

child: Provide<ProvideCounterModel>(
 builder: (context, child, counter) {
   return Text(
     "${counter.count}",
     style: TextStyle(fontSize: 50, fontWeight: FontWeight.w900),
   );
 },
)

发送数据:

RaisedButton(
    color: Colors.blue,
    child: Text('+'),
    onPressed: () {
      Provide.value<ProvideCounterModel>(context).increment();
    },
  )

EventBus

在APP中,我们经常会需要一个广播机制,用以跨页面事件通知,Flutter中我们可以使用event_bus提供的事件总线功能来实现一些状态的更新,其核心是基于Dart Streams(流);事件总线通常实现了订阅者模式,订阅者模式包含发布者和订阅者两种角色,可以通过事件总线来触发事件和监听事件

EventBus才艺展示

import 'package:event_bus/event_bus.dart';

EventBus eventBus = new EventBus();


class CounterEvent {
  int count;

  CounterEvent({this.count});
}

接受数据

  @override
  void initState() {
    super.initState();
    print("initState");
    subscription = eventBus.on<CounterEvent>().listen((event) {
      setState(() {
        count = event.count;
      });
    });
  }

  @override
  void dispose() {
    print("dispose");
    if (subscription != null) {
      subscription.cancel();
    }
    super.dispose();
  }

发送数据

RaisedButton(
color: Colors.blue,
child: Text('+'),
onPressed: () {
  count ++;
  eventBus.fire(CounterEvent(count: count));
},
)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,183评论 6 516
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,850评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,766评论 0 361
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,854评论 1 299
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,871评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,457评论 1 311
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,999评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,914评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,465评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,543评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,675评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,354评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,029评论 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,514评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,616评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,091评论 3 378
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,685评论 2 360