一、前言
此处系列章节目录,待更新
二、引入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());
});
}
}
}