在前面的文章中我们学习了如何使用Provider来实现状态管理,本次就来介绍下如何使用Scoped和flutter_redux进行状态管理。
Scoped_model
在前面的文章中我们了解到Provider其实是借助于InheritedWidget与Listenable实现的状态管理,Scoped_model呢其实也是这样实现的,只不过 Scoped_model使用AnimatedBuilder实现数据接收并通过ScopedModelDescendant做了层封装,而Provider没有借助于Widget。
我们还是按照上篇文章的例子来进行讲解。
惯例第三方库我们需要在pubspec.yaml中进行声明
然后调用pub get
首先我们定义数据存储的model,这次我们需要继承Model,这个Model是Scoped_model中的封装类,其实也是继承Listenable
1 2 3 4 5 6 7 8 9 10 11 12
| class UserModel extends Model { String _nickName = "userName";
String get nickName => _nickName;
void updateNickName(String nickName) { _nickName = nickName; notifyListeners(); } }
|
然后我们需要在合适的地方初始化UserModel,这次我们在app的入口来初始化它
1 2 3 4 5
| void main() { runApp(new MaterialApp( home: MyApp(model: UserModel(),), )); }
|
然后使用ScopedModel(ScopedModel其实是一个继承与StatelessWidget的Widget)来构建Wdiget,具体实现原理也很简单,大家可以去看下源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class MyApp extends StatelessWidget { final UserModel model;
const MyApp({Key key, @required this.model}) : super(key: key);
@override Widget build(BuildContext context) { return ScopedModel<UserModel>( model: model, child: MaterialApp( title: 'Scoped Model Demo', home: HomePage(), ), ); } }
|
最后我们在需要使用数的地方使用ScopedModelDescendant来构建Wdiget并获取到相应的Model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("scopedTitle"), ), body: Center( child: Column( children: [ ScopedModelDescendant<UserModel>( builder: (context, child, model) { return Text( model.nickName, style: Theme.of(context).textTheme.display1, ); }, ), RaisedButton( child: Text("去设置界面"), onPressed: () { Navigator.push(context, MaterialPageRoute(builder: (context) { return SecondPage(); })); }, ) ], ), ), ); } }
|
第二个界面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| class SecondPage extends StatelessWidget { @override Widget build(BuildContext context) { TextEditingController _unameController = TextEditingController();
return Scaffold( appBar: AppBar( title: Text("ProviderTitle"), ), body: Column( children: [ TextField( autofocus: true, controller: _unameController, decoration: InputDecoration( labelText: "用户名", hintText: "用户名或邮箱", prefixIcon: Icon(Icons.person)), ), ScopedModelDescendant<UserModel>( builder: (context, child, model) { return RaisedButton( onPressed: () { model.updateNickName(_unameController.text); }, child: Text("设置"), ); }, ), ], ), ); } }
|

ScopedModel使用起来非常的简单,接下来我们来看下flutter_redux如何使用。
flutter_redux
Redux是一种单向数据流架构,往往作用于前端页面当中,在Flutter我们同样可以使用Redux来进行状态管理

我们在Redux中,所有的状态都储存在Store里。这个Store会放在App顶层。
View拿到Store储存的状态(State)并把它映射成视图。View还会与用户进行交互,用户点击按钮滑动屏幕等等,这时会因为交互需要数据发生改变。
Redux让我们不能让View直接操作数据,而是通过发起一个action来告诉Reducer,状态得改变啦。
这时候Reducer接收到了这个action,他就回去遍历action表,然后找到那个匹配的action,根据action生成新的状态并把新的状态放到Store中。
Store丢弃了老的状态对象,储存了新的状态对象后,就通知所有使用到了这个状态的View更新(类似setState)。这样我们就能够同步不同view中的状态了。
接下来我们看下如何使用Redux
首先引入第三方库
1 2
| flutter_redux: ^0.6.0 redux: ">=4.0.0 <5.0.0"
|
首先我们建立基础类
1 2 3 4 5 6 7 8 9 10 11
| enum Actions { UserState }
class ActionType { String type; ActionType(this.type); }
class UserState extends ActionType { String userName; UserState({this.userName}) : super('userName'); }
|
根据传入的类型进行业务处理
1 2 3 4 5 6
| UserState counterReducer(UserState state, dynamic action) {
if ("userName"==( action as UserState).type) { return action; } }
|
在根结点初始化Redux
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| void main() { final store = Store<UserState>(counterReducer, initialState: UserState(userName: "userName"));
runApp(FlutterReduxApp( store: store, )); }
class FlutterReduxApp extends StatelessWidget { final Store<UserState> store;
FlutterReduxApp({Key key, this.store}) : super(key: key);
@override Widget build(BuildContext context) { return StoreProvider<UserState>( store: store, child: MaterialApp(
title: "Flutter Redux Demo", routes: { "/SecondPage": (context) => SecondPage()}, home: HomePage(), ), ); } }
|
第一个界面使用StoreConnector监听数据变更
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| class HomePage extends StatelessWidget {
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("scopedTitle"), ), body: Center( child: Column( children: [ StoreConnector<UserState, String>( converter: (store) => store.state.userName, builder: (context, userName) { return Text( userName, style: Theme .of(context) .textTheme .display1, ); }, ), RaisedButton( child: Text("去设置界面"), onPressed: () { Navigator.pushNamed(context, "/SecondPage"); }, ) ], ), ),
); } }
|
第二个界面,使用store.dispatch()发送事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| class SecondPage extends StatelessWidget { @override Widget build(BuildContext context) { TextEditingController _unameController = TextEditingController();
return Scaffold( appBar: AppBar( title: Text("ProviderTitle"), ), body: Column( children: [ TextField( autofocus: true, controller: _unameController, decoration: InputDecoration( labelText: "用户名", hintText: "用户名或邮箱", prefixIcon: Icon(Icons.person)), ), StoreConnector<UserState, VoidCallback>( converter: (store) { return () => store.dispatch(UserState(userName: _unameController.text)); }, builder: (store, callback) { return RaisedButton( onPressed: callback, child: Text("设置"), ); }, ), ], ), ); } }
|

Redux使用起来相对的麻烦,但是面对复杂的业务逻辑使用Redux就会体现出他的价值。
小结
- Scoped_model使用InheritedWidget和Listenable实现监听和数据传递
- Redux的用法跟react里的用法类似
- Redux虽然不是最佳的解决方案,但是面对复杂的界面就会突出它的特点