Flutter MVVM架构插件pmvvm的使用
Flutter MVVM架构插件pmvvm的使用
简介
PMVVM 是一个基于 MVVM 模式的 Flutter 状态管理包,它底层使用了 Provider 和 Hooks。与 BloC 相比,PMVVM 不需要太多的样板代码,更加简洁易用。
该包借鉴了一些 Stacked 包的概念,但采用了更简单和干净的方法。
工作原理
PMVVM 的核心组件有三个:
- View:代表应用程序的 UI,不包含任何应用逻辑。ViewModel 通过发送通知来更新 View 的 UI。
- ViewModel:持有状态和事件,作为 Model 和 View 之间的桥梁。ViewModel 是平台无关的,可以轻松绑定到 Web、移动或桌面视图。
- Model:持有应用数据和业务逻辑,通常是一些简单的类。
使用场景
当你的小部件有自己的事件可以直接改变状态时,例如页面、帖子等,可以使用 PMVVM。
使用方法
1. 构建 ViewModel
class MyViewModel extends ViewModel {
int counter = 0;
// 可选
@override
void init() {
// 在 MVVM 小部件的 initState 被调用后调用
}
// 可选
@override
void onBuild() {
// 当视图的 `build` 方法被调用时的回调
}
void increase() {
counter++;
notifyListeners();
}
}
2. 访问 ViewModel 中的 context
class MyViewModel extends ViewModel {
@override
void init() {
var height = MediaQuery.of(context).size.height;
}
}
3. 声明 MVVM
class MyWidget extends StatelessWidget {
const MyWidget({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MVVM<MyViewModel>(
view: () => _MyView(),
viewModel: MyViewModel(),
);
}
}
4. 构建 View
StatelessView
class _MyView extends StatelessView<MyViewModel> {
const _MyView({Key key}) : super(key: key, reactive: true);
@override
Widget render(context, vmodel) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(vmodel.counter.toString()),
SizedBox(height: 24),
RaisedButton(onPressed: vmodel.increase, child: Text('Increase')),
],
);
}
}
HookView
class _MyView extends HookView<MyViewModel> {
const _MyView({Key key}) : super(key: key, reactive: true);
@override
Widget render(context, vmodel) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(vmodel.counter.toString()),
SizedBox(height: 24),
RaisedButton(onPressed: vmodel.increase, child: Text('Increase')),
],
);
}
}
5. 使用 MVVM.builder
如果视图很简单,可以使用 MVVM.builder
:
class MyWidget extends StatelessWidget {
const MyWidget({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MVVM<MyViewModel>.builder(
viewModel: viewModel,
viewBuilder: (_, vm) {
return Text(
vm.counter.toString(),
textDirection: TextDirection.ltr,
);
},
);
}
}
高级用法
不可变性和可观察性
PMVVM 支持不可变性和可观察性,通过 Observable
类可以观察变量的变化。
1. 包装变量
/// 'MyCounter' 是变量的别名,这在日志记录中很有用
final counter = Observable<int>('MyCounter');
// 或者
final counter = Observable.initialized(0, 'MyCounter');
final counter = 0.observable('MyCounter');
2. 应用动作
counter.setValue(counter.value + 1, action: 'INCREASE');
3. 消费 Observable
/// 确保在使用 [value] 获取器之前已经初始化
/// 通过检查 [counter.hasValue] 或使用 [counter.valueOrNull] 或 [counter.valueOrDefault]
counter.value;
counter.stream.listen((value) { });
4. 观察和取消观察
class CounterPageVM extends ViewModel {
final counter = 0.reactive('MyCounter');
@override
void init() {
observe([counter]);
}
void increase() {
counter.setValue(counter.value + 1, action: 'INCREASE');
}
}
Observable API
Getter / Method | Description |
---|---|
value |
返回当前值 |
valueOrNull |
返回当前值或未初始化时为 null |
prevValue |
返回历史中的前一个值(如果存在) |
hasValue |
检查是否已初始化 |
history |
返回所有应用于 observable 的操作历史 |
stream |
返回 observable 值的流 |
valueOrDefault |
返回当前值或默认值(未初始化时) |
setValue |
更改当前值的方法 |
log |
允许更改日志形状的方法 |
clearHistory |
删除所有存储的操作 |
resetLogCallback |
清除自定义日志函数 |
模式
1. Naive 方法
使用级联符号初始化 ViewModel 的 widget 属性。这种方法的缺点是当 widget 的依赖项(属性)更新时,ViewModel 不会变得响应式。
class MyWidget extends StatelessWidget {
const MyWidget({Key key, this.varName}) : super(key: key);
final String varName;
@override
Widget build(BuildContext context) {
return MVVM<MyViewModel>(
view: () => _MyView(),
viewModel: MyViewModel()..varName = varName,
);
}
}
2. Clean 方法
类似于 ReactJS,创建一个属性类,将所有 widget 属性保存在其中。
my_widget.props.dart
class MyWidgetProps {
MyWidgetProps({required this.name});
final String name;
}
my_widget.vm.dart
class MyWidgetVM extends ViewModel {
late MyWidgetProps props;
@override
void init() {
props = context.fetch<MyWidgetProps>();
}
}
my_widget.view.dart
class MyWidget extends StatelessWidget {
MyWidget({
Key? key,
required String name,
}) : props = MyWidgetProps(name: name),
super(key: key);
final MyWidgetProps props;
Widget build(context) {
return Provider.value(
value: props,
child: MVVM<MyWidgetVM>(
view: () => _MyWidgetView(),
viewModel: MyWidgetVM(),
),
);
}
}
更多关于 PMVVM
- 可以使用
context.fetch<T>(listen: true/false)
,等同于Provider.of<T>(context)
- 可以使用
PMVVMConfig
控制enableLogging
和trackObservablesHistory
- 如果不想让视图响应 ViewModel 的状态通知,可以在构造
StatelessView
或HookView
时将reactive
设置为false
- ViewModel 生命周期方法(所有方法都是可选的)
void init() {}
void onDependenciesChange() {}
void onBuild() {}
void onMount() {}
void onUnmount() {}
void onResume() {}
void onPause() {}
void onInactive() {}
void onDetach() {}
FAQ
- 能否在生产环境中使用?
- 当然可以!它是稳定的,准备好投入使用。
- Stacked 和 PMVVM 有什么区别?
Stacked | PMVVM |
---|---|
不能在 ViewModel 中访问 BuildContext | 可以通过 Provider.of<T>(context) 、context.watch<T>() 、context.read<T>() 、context.select<T, R>(R cb(T value)) 访问 BuildContext |
需要实现 Initialisable 接口来调用 initialise |
init 事件默认被调用,只需覆盖即可(可选) |
ViewModel 中没有 build 方法 |
onBuild 方法默认在每次视图重建时被调用,可以覆盖以实现自己的逻辑(可选) |
过度包装 provider ,如 FutureViewModel 、StreamViewModel 等 |
不过度包装 provider 包,可以使用 StreamProvider/FutureProvider 或 Hooks ,提供更大的灵活性 |
总结来说,PMVVM 更简单、更干净,没有过度包装,且 Idioms 更加清晰。
依赖
provider
flutter_hooks
tint
示例代码
import 'package:flutter/material.dart';
import 'package:example/pages/counter/counter_page.view.dart';
/// import 'package:example/pages/counter_with_observable/counter_page.view.dart';
void main() {
runApp(MaterialApp(home: CounterPage()));
}
希望这些信息对你有所帮助!如果你有任何问题或需要进一步的帮助,请随时提问。
更多关于Flutter MVVM架构插件pmvvm的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter MVVM架构插件pmvvm的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,我可以为你提供一个关于如何在Flutter项目中使用pmvvm
插件来实现MVVM架构的示例代码。pmvvm
是一个帮助在Flutter中实现MVVM架构模式的插件。下面是一个基本的示例,展示如何使用pmvvm
来组织你的Flutter应用。
首先,确保你的pubspec.yaml
文件中已经添加了pmvvm
依赖:
dependencies:
flutter:
sdk: flutter
pmvvm: ^最新版本号 # 请替换为当前最新版本号
然后,运行flutter pub get
来安装依赖。
接下来,我们创建一个简单的Flutter应用,使用pmvvm
来实现MVVM架构。这个示例将包含一个简单的计数器应用。
1. 创建ViewModel
首先,我们创建一个CounterViewModel
来处理业务逻辑。
import 'package:pmvvm/pmvvm.dart';
class CounterViewModel extends BaseViewModel {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // 通知视图更新
}
}
2. 创建View (UI)
接下来,我们创建一个简单的Flutter界面来显示和更新计数器。
import 'package:flutter/material.dart';
import 'package:pmvvm/pmvvm.dart';
import 'counter_view_model.dart'; // 导入我们之前创建的ViewModel
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ViewModelBuilder<CounterViewModel>.reactive(
viewModelBuilder: () => CounterViewModel(),
builder: (context, model, child) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter MVVM with pmvvm'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'${model.count}',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: model.increment,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
},
);
}
}
3. 运行应用
最后,在你的main.dart
文件中,使用CounterPage
作为根页面来运行应用。
import 'package:flutter/material.dart';
import 'counter_page.dart'; // 导入我们之前创建的View
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter MVVM Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CounterPage(),
);
}
}
总结
以上代码展示了如何使用pmvvm
插件在Flutter中实现一个简单的MVVM架构应用。通过分离视图(UI)和视图模型(业务逻辑),我们可以更容易地管理和维护代码,特别是在大型项目中。ViewModelBuilder
组件负责将视图模型绑定到视图,并在视图模型数据变化时自动更新视图。