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