Flutter视图模型管理插件view_model_provider的使用
Flutter视图模型管理插件view_model_provider的使用
view_model_provider
A Flutter MVVM Framework.
Getting Started
基于Provider实现MVVM框架,常用的方式是 ViewModel 继承 ChangeNotifier ,再通过 ChangeNotifierProvider 提供给子Widget,ViewModel数据刷新通过调用 notifyListeners() 来通知Widget进行刷新,Widget 通过 Provider.of 、Consumer、Selector 来监听数据变化重新 build 更新UI。这种方式存在的问题有:
- ViewModel数据刷新需要每次调用 notifyListeners() 容易被遗漏
- notifyListeners() 作用在整个ViewModel,不方便进行局部UI刷新控制
- Selector 虽然可以控制局部刷新,但需要自定义 shouldRebuild 要去了解Provider原理
- 缺少 ViewModel 和 Widget 生命周期的管理
ViewModelProvider 在兼容现有功能基础刷,实现最小改动、不需要每次调用notifyListeners()、支持局部刷新UI和生命周期管理的框架。
局部刷新控制
1. 通过ValueNotifier创建可观察对象
class ViewModel extends ChangeNotifier {
final value1 = ValueNotifier(0);
final value2 = ValueNotifier(0);
}
2. 通过 ValueListenableBuilder 监听数据变化刷新
ValueListenableBuilder(
valueListenable: viewModel.value1,
builder: (context, value, child) {
debugPrint("ValueListenableBuilder $value");
return Text("ValueListenableBuilder $value");
},
)
列表刷新控制
1. 通过 ListNotifier 创建可观察对象
class ViewModel extends ChangeNotifier {
final list = ListNotifier<String>([]);
}
2. 通过 ListListenableBuilder 监听数据变化刷新
ListListenableBuilder(
valueListenable: viewModel.list,
builder: (context, value, child) {
debugPrint("ListListenableBuilder $value");
return Text("ListListenableBuilder $value");
},
)
实现生命周期管理
LifecycleWidget,提供Widget生命周期监听,开放了以下回调接口可进行初始化和解绑操作:
- create,可以监听一个数据变化
- initState,Widget initState 回调
- initFrame,Widget 第一帧绘制完成调用
- deactivate,Widget deactivate 回调
- dispose,Widget dispose 回调
- didUpdateWidget,Widget didUpdateWidget 回调
- didChangeDependencies,Widget didChangeDependencies 回调
ViewModelProvider
1. 嵌套使用
创建ViewModel 提供给子Widget使用,开放了以下回调接口可进行初始化和解绑操作:
- initViewModel,ViewModel首次初始化 Widget initState 期间执行
- bindViewModel,ViewModel 首次绑定 Widget ,方法在Widget build 期间执行
- disposeViewModel,ViewModel 销毁,Widget dispose 时执行
/// [ViewModelProvider] 创建ViewModel
class ProviderExample extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return ViewModelProvider<ViewModel>(
create: (_) => ViewModel(),
initViewModel: (context, viewModel) {
debugPrint("ProviderBuilderExample initViewModel $viewModel");
},
bindViewModel: (context, viewModel) {
debugPrint("ProviderBuilderExample bindViewModel $viewModel");
},
disposeViewModel: (context, viewModel) {
debugPrint("ProviderBuilderExample disposeViewModel $viewModel");
},
builder: (context, viewModel, child) {
debugPrint("ProviderBuilderExample builder $viewModel");
return ViewModelWidget(viewModel);
},
);
}
}
2. 通过继承来创建ViewModel
/// 继承 [ViewModelProviderWidget] 创建ViewModel
class ProviderWidgetExample extends ViewModelProviderWidget<ViewModel>
with ViewModelProviderLifecycle<ViewModel> {
ProviderWidgetExample() : super();
[@override](/user/override)
ViewModel create(BuildContext context) => ViewModel();
[@override](/user/override)
void initViewModel(BuildContext context, ViewModel viewModel) {
debugPrint("ProviderWidgetExample initViewModel $viewModel");
}
[@override](/user/override)
void bindViewModel(BuildContext context, ViewModel viewModel) {
debugPrint("ProviderWidgetExample bindViewModel $viewModel");
}
[@override](/user/override)
Widget buildChild(BuildContext context, ViewModel viewModel, Widget? child) {
debugPrint("ProviderWidgetExample build $viewModel");
return ViewModelWidget(viewModel);
}
}
3. 通过Mixin来创建ViewModel
/// 混入 [ViewModelProviderMixin] 创建ViewModel
/// 混入 [ViewModelProviderLifecycle] 监听ViewModel生命周期
/// 混入 [ViewModelProviderBuilder] 支持buildChild
class ProviderMixinExample extends SingleChildStatelessWidget
with
ViewModelProviderMixin<ViewModel>,
ViewModelProviderLifecycle<ViewModel>,
ViewModelProviderBuilder<ViewModel> {
ProviderMixinExample() : super();
[@override](/user/override)
ViewModel create(BuildContext context) => ViewModel();
[@override](/user/override)
void initViewModel(BuildContext context, ViewModel viewModel) {
debugPrint("ProviderMixinExample initViewModel $viewModel");
}
[@override](/user/override)
void initFrame(BuildContext context, ViewModel viewModel) {
debugPrint("ProviderMixinExample initFrame $viewModel");
super.initFrame(context, viewModel);
}
[@override](/user/override)
void bindViewModel(BuildContext context, ViewModel viewModel) {
debugPrint("ProviderMixinExample bindViewModel $viewModel");
}
[@override](/user/override)
Widget buildChild(BuildContext context, ViewModel viewModel, Widget? child) {
debugPrint("ProviderMixinExample build $viewModel");
return ViewModelWidget(viewModel);
}
[@override](/user/override)
Widget buildWithChild(BuildContext context, Widget? child) {
return buildProvider(context, child);
}
}
ViewModel嵌套处理
ViewModel 嵌套 ViewModel 管理子 ViewModel ,提供了两种方式,一种需要手动调用刷新,另一种通过ValueNotifier包装替换ViewModel不需要手动刷新,同ViewModelProvider一样也有相关的抽象类提供继承支持。
class ParentViewModel extends ChangeNotifier {
final valueViewModel = ValueNotifier(ChildViewModel());
var childViewModel = ChildViewModel();
void valueNotifier() {
valueViewModel.value = ChildViewModel();
}
void notifyListenerChild() {
childViewModel = ChildViewModel();
notifyListeners();
}
}
class ChildViewModel extends ChangeNotifier {
final value = ValueNotifier(0);
addValue() {
value.value++;
}
}
1. 通过 ViewModelProvider 创建父ViewModel
class ChildProviderExample extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return ViewModelProvider<ParentViewModel>(
create: (context) => ParentViewModel(),
builder: (context, viewModel, child) {
return Scaffold(
body: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ValueViewModelProviderExample(),
ChildViewModelProviderExample(),
ElevatedButton(
onPressed: () => viewModel.valueNotifier(),
child: Text("valueNotifier"),
),
ElevatedButton(
onPressed: () => viewModel.notifyListenerChild(),
child: Text("notifyListenerChild"),
),
],
),
),
);
},
);
}
}
2. 创建子ViewModelProvider
2-1 ValueViewModelProvider
作用和回调与 ChildViewModelProvider一样,接收数据类型为 ValueListenable<ChangeNotifier>
。
/// [ValueViewModelProvider] 获取子 ViewModel 例子
class ValueViewModelProviderExample extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return ValueViewModelProvider<ChildViewModel>(
/// 使用viewModel扩展函数 只监听ParentViewModel变化
create: (context) => context.viewModel<ParentViewModel>().valueViewModel,
initViewModel: (context, viewModel) {
debugPrint("ValueViewModelProvider initViewModel $viewModel");
},
bindViewModel: (context, viewModel) {
debugPrint("ValueViewModelProvider bindViewModel $viewModel");
},
disposeViewModel: (context, viewModel) {
debugPrint("ValueViewModelProvider disposeViewModel $viewModel");
},
changeViewModel: (context, viewModel, oldViewModel) {
debugPrint(
"ValueViewModelProvider changeViewModel $viewModel, $oldViewModel");
},
builder: (context, viewModel, child) {
debugPrint("ValueViewModelProvider builder $viewModel");
return Row(
children: [
ElevatedButton(
onPressed: () => viewModel.addValue(),
child: Text("addValue"),
),
ValueListenableBuilder(
valueListenable: viewModel.value,
builder: (context, value, child) => Text("${viewModel.value}"),
),
],
);
},
);
}
}
2-2 ChildViewModelProvider
需要手动刷新通常用于列表刷新Item区域,在ViewModelProvider已有回调基础上添加了:
- changeViewModel ,在子 ViewModel 被替换后可重新执行绑定流程
/// [ChildViewModelProvider] 获取子 ViewModel 例子
class ChildViewModelProviderExample extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return ChildViewModelProvider<ChildViewModel>(
/// 使用watch扩展函数 监听ParentViewModel的notifyListeners
create: (context) => context.watch<ParentViewModel>().childViewModel,
initViewModel: (context, viewModel) {
debugPrint("ChildViewModelProvider initViewModel $viewModel");
},
bindViewModel: (context, viewModel) {
debugPrint("ChildViewModelProvider bindViewModel $viewModel");
},
disposeViewModel: (context, viewModel) {
debugPrint("ChildViewModelProvider disposeViewModel $viewModel");
},
changeViewModel: (context, viewModel, oldViewModel) {
debugPrint(
"ChildViewModelProvider changeViewModel $viewModel, $oldViewModel");
},
builder: (context, viewModel, child) {
debugPrint("ChildViewModelProvider builder $viewModel");
return Column(
children: [
Row(
children: [
ElevatedButton(
onPressed: () => viewModel.addValue(),
child: Text("addValue"),
),
ValueListenableBuilder(
valueListenable: viewModel.value,
builder: (context, value, child) =>
Text("${viewModel.value}"),
),
],
),
],
);
},
);
}
}
获取ViewModel
1. 扩展函数
通过 context.viewModel<ViewModel>()
可以快速取出 ViewModelProvider
ChildViewModelProvider
和 ValueViewModelProvider
的ViewModel。
2. ViewModelBuilder
用于取出ViewModelProvider提供的ViewModel。
ValueListenableBuilder
ValueListenableBuilder 只能监听当前数据刷新,同时监听多个数据刷新可采用 ValueTuple2WidgetBuilder
到 ValueListenableTuple7Builder
和 ValueListenableListBuilder
。
/// 外部传入 ViewModel,可采用[ValueListenableBuilder]系列监听数据变化
Widget _buildValueListenable(ViewModel viewModel) {
return Column(
children: [
/// 监听单个数据变化
ValueListenableBuilder(
valueListenable: viewModel.value1,
builder: (context, value, child) {
debugPrint("ValueListenableBuilder $value");
return Text("ValueListenableBuilder $value");
}),
/// 监听多个数据变化,继承自
ValueListenableListBuilder(
valueListenables: [
viewModel.value1,
viewModel.value2,
],
builder: (context, value, child) {
debugPrint(
"ValueListenableListBuilder ${value.first}, ${value.last}");
return Text(
"ValueListenableListBuilder ${value.first}, ${value.last}");
},
),
/// 监听多个数据变化,继承自[ValueListenableListBuilder]可指定泛型
ValueListenableTuple2Builder(
valueListenables: Tuple2(viewModel.value1, viewModel.value2),
builder: (context, value, child) {
debugPrint(
"ValueListenableTuple2Builder ${value.item1}, ${value.item2}");
return Text(
"ValueListenableTuple2Builder ${value.item1}, ${value.item2}");
},
),
],
);
}
ViewModelValueBuilder
ViewModelBuilder 和 ValueListenableBuilder 组合,用于获取 ViewModel 和管理Widget刷新区域。
提供过个实现 ViewModelValueListBuilder
,ViewModelValueTuple2Builder
到 ViewModelValueTuple7WidgetBuilder
可同时监听多个ViewMode参数变化来刷新Widget。
/// 不通过外部传入 ViewModel,可采用[ViewModelValueBuilder]系列获取 ViewModel 并监听数据变化
Widget _buildViewModelValue() {
return Column(
children: [
ViewModelValueBuilder(
valueListenable: (ViewModel viewModel) => viewModel.value1,
builder: (context, viewModel, value, child) {
debugPrint("ViewModelValueBuilder $value");
return Text("ViewModelValueBuilder $value");
},
),
ViewModelValueListBuilder(
valueListenables: (ViewModel viewModel) => [
viewModel.value1,
viewModel.value2,
],
builder: (context, viewModel, value, child) {
debugPrint(
"ViewModelValueListBuilder ${value.first}, ${value.last}");
return Text(
"ViewModelValueListBuilder ${value.first}, ${value.last}");
},
),
ViewModelValueTuple2Builder(
valueListenables: (ViewModel viewModel) =>
Tuple2(viewModel.value1, viewModel.value2),
builder: (context, viewModel, value, child) {
debugPrint(
"ViewModelValueTuple2Builder ${value.item1}, ${value.item2}");
return Text(
"ViewModelValueTuple2Builder ${value.item1}, ${value.item2}");
},
),
],
);
}
完整示例Demo
import 'package:flutter/material.dart';
import 'child_provider_example.dart';
import 'list_example.dart';
import 'provider_example.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: ViewModelExample(),
);
}
}
class ViewModelExample extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return SafeArea(
top: true,
child: Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ElevatedButton(
onPressed: () {
push(context, ProviderExample());
},
child: Text("BaseExample"),
),
ElevatedButton(
onPressed: () {
push(context, ProviderWidgetExample());
},
child: Text("BaseExtendsExample"),
),
ElevatedButton(
onPressed: () {
push(context, ProviderMixinExample());
},
child: Text("BaseMixinExample"),
),
ElevatedButton(
onPressed: () {
push(context, ProviderLifecycleMixinExample());
},
child: Text("BaseLifecycleMixinExample"),
),
ElevatedButton(
onPressed: () {
push(context, ChildProviderExample());
},
child: Text("ChildExample"),
),
ElevatedButton(
onPressed: () {
push(context, ListProviderExapmle());
},
child: Text("ListExample"),
),
],
),
),
);
}
push(BuildContext context, Widget widget) {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return SafeArea(top: true, child: widget);
}));
}
}
更多关于Flutter视图模型管理插件view_model_provider的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter视图模型管理插件view_model_provider的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何在Flutter中使用view_model_provider
插件来管理视图模型的示例代码。view_model_provider
插件允许你以声明式的方式在Flutter应用中管理和共享视图模型(ViewModel),类似于在Android中使用的ViewModel概念。
首先,确保你的pubspec.yaml
文件中已经添加了view_model_provider
依赖:
dependencies:
flutter:
sdk: flutter
view_model_provider: ^x.y.z # 请替换为最新版本号
然后运行flutter pub get
来安装依赖。
1. 创建一个ViewModel
首先,创建一个简单的ViewModel类。ViewModel通常用于持有和管理UI相关的数据逻辑。
import 'package:flutter/material.dart';
import 'package:view_model_provider/view_model_provider.dart';
class CounterViewModel extends ViewModel {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // 通知监听器数据已更改
}
}
2. 创建一个Flutter Widget并使用ViewModelProvider
接下来,创建一个Flutter Widget,并使用ViewModelProvider
来提供和管理CounterViewModel
。
import 'package:flutter/material.dart';
import 'package:view_model_provider/view_model_provider.dart';
import 'counter_view_model.dart'; // 假设你的ViewModel文件名为counter_view_model.dart
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ViewModelProvider<CounterViewModel>(
createViewModel: () => CounterViewModel(),
builder: (context, model, child) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Demo Home Page'),
),
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. 运行应用
现在,你可以运行你的Flutter应用。你应该能看到一个带有按钮和计数器的简单页面。每次点击按钮时,计数器都会增加,并且UI会更新以反映最新的计数。
解释
- CounterViewModel: 这是一个简单的ViewModel类,它持有一个整数计数器,并提供一个
increment
方法来增加计数。当计数改变时,它通过调用notifyListeners()
来通知所有监听器。 - ViewModelProvider: 这是一个Widget,它负责创建并提供ViewModel实例。它接受一个
createViewModel
函数来创建ViewModel,以及一个builder
函数来构建子Widget树。builder
函数接收ViewModel作为参数,这样你就可以在UI中使用它。 - MyApp: 这是应用的主Widget,它使用
ViewModelProvider
来提供CounterViewModel
,并构建一个简单的UI,包括一个显示计数的Text和一个增加计数的FloatingActionButton。
通过这种方式,你可以使用view_model_provider
插件在Flutter应用中管理和共享ViewModel,使你的代码更加清晰和模块化。