MoLc是我自己基于Provider封装的一个状态管理的库,目前用于一个2w日活的小商业项目,表现还算可以,拿出来和大家分享一下。
MoLc是Model、Logic的组合,旨在分离数据模型和业务逻辑。我是Android出身,MVP模式在当年还算火热,所以刚接触Flutter就做了这样一个封装,也就是MoLc的初版。下面来讲讲MoLc实现了什么,个人觉得MoLc适合大部分中大型项目,欢迎大佬前来指导不足之处。
使用MoLc之前要了解Model
和Logic
。
Model
Model
是存放Widget状态的类,直接使用
class TestModel extends Model {}
复制代码
如果你想要在Model
中使用context
,比如需要通过context
获取文本S.of(context)
,那么使用子类WidgetModel
class Test1Model extends WidgetModel {}
复制代码
在子context
中获取上层context
的Model
用context.read<T>()
,这是Provider提供的,大家应该很熟悉。
- 刷新
test1Model.refresh();
复制代码
- Selector
不同于Provider的Selector,MoLc的Selector是用于精细化刷新。
混入SelectorMixin<T>
class TestModel extends Model with SelectorMixin<Tuple3>{}
复制代码
实现 T selectWith();
方法, 返回值的是否改变决定了Model是否刷新。
@override
Tuple3<int, String, bool> selectWith() {
return Tuple3(this.field1, this.field2, this.field3);
}
复制代码
Logic
Logic
是存放业务逻辑的类,使用
class TestLogic extends Logic {}
复制代码
同样可以在子context
中获取上层context的Logic
,也是用context.read<T>()
。 Logic
也提供了子类WidgetLogic
用于获取context
。
另外,Logic还提供了MoLogic
提供Model
,但不建议在普通业务逻辑函数中使用,不然数据模型和业务逻辑分离的初衷就被破坏了。仅建议在dispose
中使用model,用来执行一些页面或者小组件销毁时需要的逻辑。
MoLcWidget
MoLc的核心组件是 ModelWidget
、LogicWidget
、MoLcWidget
,前两个适合只有Model
或者Logic
的Widget,用的比较少,重点讲一下MoLcWidget
,下面是MoLcWidget
的写法:
class ExamplePage extends StatelessWidget {
const ExamplePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MoLcWidget<_ExampleModel, _ExampleLogic>(
modelCreate: (_) => _ExampleModel(),
logicCreate: (_) => _ExampleLogic(),
init: (_, model, logic) => logic.init(model),
builder: (context, model, logic, __) => Container(),
);
}
}
class _ExampleModel extends Model {}
class _ExampleLogic extends Logic {
void init(_ExampleModel model) {}
}
复制代码
如同Provider一样,还是有些样板代码,不过可以使用IDE的Live Template,也还算方便。MoLc把model和logic从Widget里分离出去,你可以在model里定义Widget的状态,可以在logic写Widget逻辑代码,然后在MoLcWidget里面调用。看到这里你可能觉得没什么,只是对Provider的一层封装而已,没错,这就是他的第一个版本的基本功能。
TopProvider
接下来说数据共享,很多使用Provider的人可能用的最多的反而是处于APP之上的顶级Model,像这样:
MultiProvider(
providers: ...,
child: MaterialApp(
...
),
),
复制代码
这是由于InheritedWidget的特性,context节点位置要位于MaterialApp 之上才能让App中的所有路由页面访问到,才能实现跨页面传输数据。这里MoLc封装了一层TopProvider
,用于内部的一些全局容器的实现和更方便的访问TopModel
。
TopProvider(
providers: ...,
child: MaterialApp(
...
),
),
复制代码
TopModel
- 全局获取
在MoLc中,顶级Mode被命名为TopModel
,继承于Model
,在上述TopProvider
里注册。你几乎可以在任意地方通过顶级函数top<T>()
来获取你需要的TopModel
,而不需要传入context
。
- 局部刷新
在实际项目中顶级Model带来了另一个问题,顶级Model的刷新会导致整个App的build,也就是性能问题。为了解决这个问题,MoLc定义了一个名为EventModel<T>
的Mixin
,T
为Event的类型。
你可以给为你的TopModel
定义一个enum
的Event:
enum TestEvent { event1, event2, event3 }
复制代码
然后TopModel
混入EventModel<T>
class TestTopModel extends TopModel with EventModel<TestEvent> {}
复制代码
需要监听的Model
混入EventConsumerMixin
class Test2Model extends Model with EventConsumerForModel {}
复制代码
Model
监听Event
model.listenTopModelEvent(TestEvent.event4);
复制代码
TopModel
发送Event
testTopModel.refreshEvent(TestEvent.event4)
复制代码
这样就完成的TopModel
对于需要监听部分事件Model
的局部刷新。
ExposedMixin
上面提到过InheritedWidget
的特性,只能实现子context对于父context的数据访问。而实际在业务中,我们常常会遇到父context访问子context的需求,或者需要兄弟context相互访问。MoLc也提供了这样的实现,只需要混入一个ExposedMixin
。
Model
混入ExposedMixin
class Test3Model extends Model with ExposedMixin {}
复制代码
然后你就可以在任意地方通过顶级函数find<T>()
获取到这个Model
,如果这个Model
当前不存在或者已经disposed
是获取不到的。
Logic
混入ExposedMixin
class Test3Logic extends Logic with ExposedMixin {}
复制代码
此时的Logic
你可以在任意地方进行调用,轻松的实现了Logic
的复用。这里有一个TODO:未注册在BuildContext
中的Logic
的调用,目前只能调用当前APP的BuildContext
树中存在的Logic
。
最后放上pub链接,感兴趣可以尝试一下。