美文网首页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