美文网首页Flutter
Flutter状态管理(四)scoped_model

Flutter状态管理(四)scoped_model

作者: 天色将变 | 来源:发表于2020-09-08 21:59 被阅读0次
    简介

    scoped_model是一个简单的第三方状态管理框架,它的源码只有一个dart文件,代码量非常少,巧妙的利用了InheritedWidget和AnimatedBuilder的特性,达到了状态管理的目的。
    官网地址:https://pub.flutter-io.cn/packages/scoped_model

    如何安装
    • pubspec.yaml中:
    dependencies:
      scoped_model: ^1.0.1
    
    • 运行: flutter pub get
    • dart文件中引用:import 'package:scoped_model/scoped_model.dart';
    代码示例

    还是那个点击按钮+1的示例,改用scoped_model实现:


    image.png
    import 'package:flutter/material.dart';
    import 'package:scoped_model/scoped_model.dart';
    class ScopedModelTest extends StatefulWidget {
      @override
      _ScopedModelTestState createState() => _ScopedModelTestState();
    }
    
    class _ScopedModelTestState extends State<ScopedModelTest> {
      // 创建model
      CountModel _model = CountModel();
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("ScopedModel"),
            centerTitle: true,
          ),
          body: Center(
            child: ScopedModel<CountModel>(
              model: _model,
              child: CountWidget(),// 要将使用model中数据的widget成为ScopedModel的子widget
            ),
          ),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: (){
              _model.increment();// 出发model的+1方法
            },
          ),
        );
      }
    }
    
    // 自定义Model
    class CountModel extends Model{
      // 要共享的状态数据
      int _counter = 0;
      int get counter => _counter;
      // +1的方法
      void increment(){
        ++_counter;
        notifyListeners();
      }
    }
    
    // 使用了状态数据的Widget
    class CountWidget extends StatefulWidget {
      @override
      _CountWidgetState createState() => _CountWidgetState();
    }
    
    class _CountWidgetState extends State<CountWidget> {
      @override
      Widget build(BuildContext context) {
        return Container(
          child: ScopedModelDescendant<CountModel>(
            builder: (context,child,model){
              return Text(model.counter.toString());
            },
          ),
        );
      }
    }
    

    解析:

    • 自定义CountModel 继承 Model,成员_counter是状态数据;同时定义了方法increment(),方法内部是对状态数据的更改,然后调用notifyListeners()方法,告诉所有依赖此状态数据的widget,此状态数据更新了,内部最终是调用了widget的setState方法进行了页面重新build,渲染了新的状态数据。
    • 自定义CountWidget,模拟了一个使用了状态数据的Widget。需要注意使用ScopedModelDescendant<CoutModel>,这是一个StatelessWidget,在内部调用ScopedModel.of<T>(context, rebuildOnChange: rebuildOnChange)获取到泛型CountModel在渲染树中的实例,通过builder传递回来,然后通过model.counter获取到model中定义的状态数据。
    • 使用ScopedModel
    ScopedModel<CountModel>(
              model: _model,
              child: CountWidget(),// 要将使用model中数据的widget成为ScopedModel的子widget
            ),
    

    接收两个参数:model是前面创建的CountModel,也就是_counter所在的数据,是需要通过floatingActionButton去进行操作的。child是依赖CountModel的Widget。ScopedModel内部最终是将model和child传递给了一个InheritedWidget,利用InheritedWidget的特性将model从渲染树中下传到了CountWidget。

    • floatingActionButton中通过_model.increment()更改model中的状态数据,进而出发notifyListeners().
    核心原理
    • 数据共享:ScopedModel内部是一个InheritedWidget,CountWidget是ScopedModel的child,实际是InheritedWidget的child,model是InheritedWidget中的共享数据,这样通过InheritedWidget的特性,CountWidget就能获取到model,从而显示model的数据。
    • 跨Widget更新:Model的本质是一个Listenable,内部有一个Set<VoidCallback> _listeners,是所有使用到该model位置的的Widget的回调,比如这里的ScopedModel。AnimatedBuilder是ScopedModel的一个中间件 ,其本质是一个AnimatedWidget,AnimatedWidget是一个StatefulWidget,在initState内部会去调用model的addListener方法,方法参数是一个内容是setState的方法。model内更改状态数据去notifyListeners时,会调用到该setState,进而触发重新build。
    源码分析

    注意这里列出的源码都是删减版的,只保留了核心内容

    • Model
    abstract class Model extends Listenable {
      // 维护了一个回调的set
      final Set<VoidCallback> _listeners = Set<VoidCallback>();
      /// 在AnimatedWidget的initState内调用该方法
      @override
      void addListener(VoidCallback listener) {
        _listeners.add(listener);
      }
    
      /// CountModel中increment方法内部调用该方法,将所有的回调给执行。
      @protected
      void notifyListeners() {
        _listeners.toList().forEach((VoidCallback listener) => listener());
      }
    }
    
    • ScopedModel
      注意深追
      AnimatedBuilder --> AnimatedWidget
      _InheritedModel --> InheritedWidget
    class ScopedModel<T extends Model> extends StatelessWidget {
      /// The [Model] to provide to [child] and its descendants.
      final T model;
    
      /// The [Widget] the [model] will be available to.
      final Widget child;
    
      ScopedModel({@required this.model, @required this.child})
          : assert(model != null),
            assert(child != null);
    
      @override
      Widget build(BuildContext context) {
        // 注意深追AnimatedBuilder,--> AnimatedWidget  
        return AnimatedBuilder(
          animation: model,
          builder: (context, _) => _InheritedModel<T>(model: model, child: child),
        );
      }
    }
    
    • AnimatedWidget的state中
      这里的listenable就是前面的model
      initState中去addListener,参数方法内是setState
    abstract class AnimatedWidget extends StatefulWidget {
      const AnimatedWidget({
        Key key,
        @required this.listenable,
      }) : assert(listenable != null),
           super(key: key);
    
      final Listenable listenable;
    
      @override
      _AnimatedState createState() => _AnimatedState();
    
    }
    
    class _AnimatedState extends State<AnimatedWidget> {
      @override
      void initState() {
        super.initState();
        widget.listenable.addListener(_handleChange);
      }
      void _handleChange() {
        setState(() {
          // The listenable's state is our build state, and it changed already.
        });
      }
    
    }
    
    
    • _InheritedModel
      这是一个InheritedWidget,前面的model和CountWidget最终都是传递到这里来
    class _InheritedModel<T extends Model> extends InheritedWidget {
      final T model;
      final int version;
    
      _InheritedModel({Key key, Widget child, T model})
          : this.model = model,
            this.version = model._version,
            super(key: key, child: child);
    
      @override
      bool updateShouldNotify(_InheritedModel<T> oldWidget) =>
          (oldWidget.version != version);
    }
    
    • ScopedModelDescendant 是为了获取共享的model而做的一层封装,通过ScopedModel.of<T>(context, rebuildOnChange: rebuildOnChange)获取到model,在通过builder传递出去。build的类型是ScopedModelDescendantBuilder
    class ScopedModelDescendant<T extends Model> extends StatelessWidget {
     
      @override
      Widget build(BuildContext context) {
        return builder(
          context,
          child,
          ScopedModel.of<T>(context, rebuildOnChange: rebuildOnChange),
        );
      }
    }
    
    typedef Widget ScopedModelDescendantBuilder<T extends Model>(
      BuildContext context,
      Widget child,
      T model,
    );
    

    相关文章

      网友评论

        本文标题:Flutter状态管理(四)scoped_model

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