美文网首页
Flutter 状态管理(数据共享)

Flutter 状态管理(数据共享)

作者: 千寻与小米 | 来源:发表于2020-08-06 15:24 被阅读0次

    所有程序里界面和数据的交互都至关重要,直接决定了整个程序的结构,选好状态管理方案的重要性就不言而喻了
    目前为止,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));
    },
    )
    

    相关文章

      网友评论

          本文标题:Flutter 状态管理(数据共享)

          本文链接:https://www.haomeiwen.com/subject/rtrqrktx.html