前言
前面3篇我们介绍了 MobX 的概念和使用, MobX 使用起来确实很简单,也很高效。但是, MobX 也有一个缺陷,那就是如果状态数据要共享的时候,我们并不能像 Provider 或 Redux 那样将状态对象提升到共同上级组件来完成。还是以点赞和收藏按钮的界面为例(Redux 的可以参考:Flutter 入门与实战(六十四):这篇很长,为了性能,你忍一下 —— 从源码看 flutter_redux精准刷新)。两个按钮是独立的组件,点击时都会增加对应的数量,两个组件共享一个状态对象。
MobX 共享方式一:逐级传递
由于 MobX 本身不支持上级组件状态可以被下级组件共享,因此我们想到的最简单的方式就是将状态对象传递给下级组件。
class DynamicDetailWrapper extends StatelessWidget {
final store = ShareStore();
DynamicDetailWrapper({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
//...
children: [
_PraiseButton(store: store),
_FavorButton(store: store),
],
}
}
class _FavorButton extends StatelessWidget {
final ShareStore store;
const _FavorButton({Key? key, required this.store}) : super(key: key);
@override
Widget build(BuildContext context) {
print('FavorButton');
return Container(
alignment: Alignment.center,
color: Colors.blue,
child: TextButton(
onPressed: () {
store.increamentFavor();
},
child: Observer(
builder: (context) => Text(
'收藏 ${store.favorCount}',
style: TextStyle(color: Colors.white),
),
),
style: ButtonStyle(
minimumSize: MaterialStateProperty.resolveWith(
(states) => Size((MediaQuery.of(context).size.width / 2), 60))),
),
);
}
}
// 省略点赞按钮代码
运行一下,效果也能达到,但是存在的问题就是如果一个状态是全局的,意味着需要将这个状态对象传递给每一个需要该状态的子组件,这个方式太丑陋了!那有没有别的方法呢?
MobX 状态共享方式二:借用 Provider
既然 MobX
不支持在上层组件定义状态对象,通过context
来给下级组件共享,那可以给他拉一个搭档来,显然 Provider
是一个不错的搭档。我们可以将 MobX
的共享状态对象由 Provider
来定义,然后共享给下级组件。
P.S. 发现 Provider 都已经升级到6.0.0版本了,正好试试新版本怎么样?
class DynamicDetailProvider extends StatelessWidget {
DynamicDetailProvider({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
//省略其他代码
child: Provider(
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_PraiseButton(),
_FavorButton(),
],
),
create: (context) => ShareStore(),
),
}
}
class _FavorButton extends StatelessWidget {
const _FavorButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
print('FavorButton');
return Container(
alignment: Alignment.center,
color: Colors.blue,
child: TextButton(
onPressed: () {
context.read<ShareStore>().increamentFavor();
},
child: Observer(
builder: (context) => Text(
'收藏 ${context.read<ShareStore>().favorCount}',
style: TextStyle(color: Colors.white),
),
),
style: ButtonStyle(
minimumSize: MaterialStateProperty.resolveWith(
(states) => Size((MediaQuery.of(context).size.width / 2), 60))),
),
);
}
}
这种方式相比之前的方式就优雅多了,只是在需要共享使用 MobX
状态对象的组件的上级使用 Provider
包裹它们,然后使用 context.read()
方法获取状态对象就可以了。那还有没有更好的方式?
MobX 状态共享方式三:使用 GetIt 容器
我们在Flutter 入门与实战(二十七):使用 GetIt 同步不同页面间数据曾经介绍过GetIt 容器。事实上,GetIt 容器可以存储我们的状态对象,在需要使用状态对象的时候,从容器中取出来就可以了。
class DynamicDetailGetIt extends StatelessWidget {
DynamicDetailGetIt({Key? key}) : super(key: key) {
GetIt.I.registerSingleton<ShareStore>(ShareStore());
}
//省略 build 方法
}
class _FavorButton extends StatelessWidget {
const _FavorButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
print('FavorButton');
return Container(
alignment: Alignment.center,
color: Colors.blue,
child: TextButton(
onPressed: () {
GetIt.I.get<ShareStore>().increamentFavor();
},
child: Observer(
builder: (context) => Text(
'收藏 ${GetIt.I.get<ShareStore>().favorCount}',
style: TextStyle(color: Colors.white),
),
),
style: ButtonStyle(
minimumSize: MaterialStateProperty.resolveWith(
(states) => Size((MediaQuery.of(context).size.width / 2), 60))),
),
);
}
}
可以看到,这种方式相对上面的两种方式更为简洁:
- 注册单例状态对象到 GetIt容器
- 在需要使用状态对象的地方从GetIt容器取出即可,开箱即用!
- 无需依赖 context,可以避免 context 过于臃肿影响性能。
总结
示例源码请点击此处:MobX 状态管理源码。本篇介绍了3种可用于MobX 状态共享的方法,由于 MobX 无法直接利用上级组件给下级组件共享,因此需要借助其他方式实现状态共享。对比可以发现吗,使用GetIt 容器的方式更为简洁、优雅,组件间的耦合度也最低。因此,推荐在使用 MobX 进行状态管理代理的场合使用这种方式。
我是岛上码农,微信公众号同名,这是Flutter 入门与实战的专栏文章,对应源码请看这里:Flutter 入门与实战专栏源码。
👍🏻:觉得有收获请点个赞鼓励一下!
🌟:收藏文章,方便回看哦!
💬:评论交流,互相进步!