美文网首页
Flutter状态管理:ScopedModel

Flutter状态管理:ScopedModel

作者: 青叶小小 | 来源:发表于2021-01-19 13:20 被阅读0次

    一、前言

    此处系列章节目录,待更新

    二、引入ScopedModel第三方库

    // pubspec.yaml
    dependencies:
      flutter:
        sdk: flutter
    
      scoped_model: ^1.0.1
    

    三、新增Model

    // CountModel.dart
    import 'package:scoped_model/scoped_model.dart';
    
    class CountModel extends Model {
      int _count = 0;
      int get count => _count;
    
      void increment() {
        _count++;
        notifyListeners();
      }
    }
    

    四、局部刷新(单组件/单页面内部状态)

    4.1 新增页面(ScopedModelPage)

    // ScopedModelPage.dart
    import 'package:flutter/material.dart';
    import 'package:scoped_model/scoped_model.dart';
    import 'package:stateresearch/model/CountModel.dart';
    
    class ScopedModelPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return ScopedModel<CountModel>(
          model: CountModel(),
          child: Scaffold(
            appBar: AppBar(
              title: Text("ScopedModelPage"),
            ),
            body: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Text('You have pushed the button this many times:'),
                  ScopedModelDescendant<CountModel>(
                    builder: (context, child, model) => Text('${model.count}'),
                  ),
                ],
              ),
            ),
            floatingActionButton: ScopedModelDescendant<CountModel>(
              builder: (context, child, model) {
                return FloatingActionButton(
                  onPressed: model.increment,
                  tooltip: 'Increment',
                  child: Icon(Icons.add),
                );
              },
            ),
          ),
        );
      }
    }
    

    4.2、修改main文件

    // 改写 main.dart
    import 'package:flutter/material.dart';
    import 'package:stateresearch/pages/ScopedModelPage.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter状态管理',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            visualDensity: VisualDensity.adaptivePlatformDensity,
          ),
          home: ScopedModelPage(),
        );
      }
    }
    

    五、全局刷新(页面/组件状态共享)

    5.1、新增两个页面(ScopedModelPageTwo和ScopedModelPageThree)

    // ScopedModelPageTwo.dart
    import 'package:flutter/material.dart';
    import 'package:scoped_model/scoped_model.dart';
    import 'package:stateresearch/model/CountModel.dart';
    import 'package:stateresearch/pages/ScopedModelPageThree.dart';
    
    class ScopedModelPageTwo extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("ScopedModelPageTwo"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text('You have pushed the button this many times:'),
                ScopedModelDescendant<CountModel>(
                  builder: (context, child, model) => Text('${model.count}'),
                ),
              ],
            ),
          ),
          floatingActionButton: ScopedModelDescendant<CountModel>(
            builder: (context, child, model) {
              return FloatingActionButton(
                onPressed: () {
                  model.increment();
                  Future.delayed(Duration(seconds: 2), () {
                    Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {
                      return ScopedModelPageThree();
                    }));
                  });
                },
                tooltip: 'Increment',
                child: Icon(Icons.add),
              );
            },
          ),
        );
      }
    }
    
    // ScopedModelPageThree.dart
    import 'package:flutter/material.dart';
    import 'package:scoped_model/scoped_model.dart';
    import 'package:stateresearch/model/CountModel.dart';
    
    class ScopedModelPageThree extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("ScopedModelPageThree"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text('You have pushed the button this many times:'),
                ScopedModelDescendant<CountModel>(
                  builder: (context, child, model) => Text('${model.count}'),
                ),
              ],
            ),
          ),
          floatingActionButton: ScopedModelDescendant<CountModel>(
            builder: (context, child, model) {
              return FloatingActionButton(
                onPressed: model.increment,
                tooltip: 'Increment',
                child: Icon(Icons.add),
              );
            },
          ),
        );
      }
    }
    

    5.2、修改main文件

    import 'package:flutter/material.dart';
    import 'package:scoped_model/scoped_model.dart';
    import 'package:stateresearch/model/CountModel.dart';
    import 'package:stateresearch/pages/ScopedModelPageTwo.dart';
    
    void main() {
      runApp(
         // APP顶层进行全局监听
         // route 会进行向下传递该 Model
        // 因此其它页面无需 ScopedModel
        // 只需要通过 ScopedModelDescendant<T> 获取 Model 即可
        ScopedModel<CountModel>(
            model: CountModel(),
            child: MyApp(),
        ),
      );
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter状态管理',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            visualDensity: VisualDensity.adaptivePlatformDensity,
          ),
          home: ScopedModelPageTwo(),
        );
      }
    }
    

    六、多Model全局共享

    6.1、新增Model(ListModel)和 mixin的Model(GlobalScopedModel)

    // ListModel.dart
    import 'package:scoped_model/scoped_model.dart';
    
    class ListModel extends Model {
      List<String> _list = [];
      List<String> get list => _list;
    
      void push(String value) {
        _list.add(value);
        notifyListeners();
      }
    }
    
    // GlobalScopedModel.dart
    import 'package:scoped_model/scoped_model.dart';
    import 'package:stateresearch/model/CountModel.dart';
    import 'package:stateresearch/model/ListModel.dart';
    
    class GlobalScopedModel extends Model with CountModel, ListModel {}
    

    6.2、新增analysis配置

    // analysis_options.yaml
    // 该配置告诉Dart Analyzer放开minx的限制
    // 默认with的类强制是继承于Object类
    analyzer:
      errors:
        mixin_inherits_from_not_object: ignore
    

    6.3、修改两个页面(ScopedModelPageTwo和ScopedModelPageThree)

    // ScopedModelPageTwo.dart
    import 'dart:math';
    
    import 'package:flutter/material.dart';
    import 'package:scoped_model/scoped_model.dart';
    import 'package:stateresearch/model/GlobalScopedModel.dart';
    import 'package:stateresearch/pages/ScopedModelPageThree.dart';
    
    class ScopedModelPageTwo extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("ScopedModelPageTwo"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text('You have pushed the button this many times:'),
                ScopedModelDescendant<GlobalScopedModel>(
                  builder: (context, child, model) => Text('${model.count}'),
                ),
              ],
            ),
          ),
          floatingActionButton: ScopedModelDescendant<GlobalScopedModel>(
            builder: (context, child, model) {
              return FloatingActionButton(
                onPressed: () {
                  model.increment();
                  model.push("chris-${Random().nextInt(10)}");
                  Future.delayed(Duration(seconds: 2), () {
                    Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {
                      return ScopedModelPageThree();
                    }));
                  });
                },
                tooltip: 'Increment',
                child: Icon(Icons.add),
              );
            },
          ),
        );
      }
    }
    
    // ScopedModelPageThree.dart
    import 'package:flutter/material.dart';
    import 'package:scoped_model/scoped_model.dart';
    import 'package:stateresearch/model/GlobalScopedModel.dart';
    
    class ScopedModelPageThree extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("ScopedModelPageThree"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text('You have pushed the button this many times:'),
                ScopedModelDescendant<GlobalScopedModel>(
                  builder: (context, child, model) => Text('${model.count}'),
                ),
                ScopedModelDescendant<GlobalScopedModel>(
                  builder: (context, child, model) => Text('${model.list}'),
                ),
              ],
            ),
          ),
          floatingActionButton: ScopedModelDescendant<GlobalScopedModel>(
            builder: (context, child, model) {
              return FloatingActionButton(
                onPressed: model.increment,
                tooltip: 'Increment',
                child: Icon(Icons.add),
              );
            },
          ),
        );
      }
    }
    

    6.4、修改main文件

    // main.dart
    import 'package:flutter/material.dart';
    import 'package:scoped_model/scoped_model.dart';
    import 'package:stateresearch/model/GlobalScopedModel.dart';
    import 'package:stateresearch/pages/ScopedModelPageTwo.dart';
    
    void main() {
      runApp(
        ScopedModel<GlobalScopedModel>(
          model: GlobalScopedModel(),
          child: MyApp(),
        ),
      );
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter状态管理',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            visualDensity: VisualDensity.adaptivePlatformDensity,
          ),
          home: ScopedModelPageTwo(),
        );
      }
    }
    

    七、总结

    ScopedModel可以全局+局部使用(即使用了全局ScopedModel,也不影响某个Widget使用自己的ScopedModel)

    使用ScopedModel,其优点:

    • 显示逻辑与业务逻辑分离;

    缺点:

    • 模型复杂时,notifyListeners的时机选择很重要,否则会频繁刷新;
    • Model的API内部是异步(Microtask),但其API名看不出来是异步;

    附录、源码之Model

    abstract class Model extends Listenable {
      final Set<VoidCallback> _listeners = Set<VoidCallback>();
      int _version = 0;
      int _microtaskVersion = 0;
    
      @override
      void addListener(VoidCallback listener) {
        _listeners.add(listener);
      }
    
      @override
      void removeListener(VoidCallback listener) {
        _listeners.remove(listener);
      }
    
      int get listenerCount => _listeners.length;
    
      @protected
      void notifyListeners() {
        if (_microtaskVersion == _version) {
          _microtaskVersion++;
          scheduleMicrotask(() {
            _version++;
            _microtaskVersion = _version;
            _listeners.toList().forEach((VoidCallback listener) => listener());
          });
        }
      }
    }
    

    相关文章

      网友评论

          本文标题:Flutter状态管理:ScopedModel

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