To implement the MVVM framework based on Provider, the ViewModel inherits ChangeNotifier and provides it to child widgets through ChangeNotifierProvider. ViewModel data refreshes notify widgets of the refresh by calling notifyListeners().
Widgets use provider. of, Consumer, and Selector to listen for data changes and rebuild and update their UI. The problems with this approach include:
- ViewModel data refreshes require notifyListeners() to be called every time
- NotifyListeners () work on the entire ViewModel, making local UI refresh controls inconvenient
- Although Selector can control partial refresh, you need to customize shouldRebuild to understand the principle of the Provider
- Lack of ViewModel and Widget lifecycle management
ViewModelProvider is a framework for minimal changes, no notifyListeners() calls, local UI refreshes, and lifecycle management that are compatible with existing functionality
I’ll give you the source code and then I’ll talk about the view_model_provider later
Local refresh control
1. Use ValueNotifier to create observables
class ViewModel extends ChangeNotifier {
final value1 = ValueNotifier(0);
final value2 = ValueNotifier(0);
}
Copy the code
2. Use ValueListenableBuilder to listen for data changes and refresh
ValueListenableBuilder(
valueListenable: viewModel.value1,
builder: (context, value, child) {
debugPrint("ValueListenableBuilder $value");
return Text("ValueListenableBuilder $value"); },)Copy the code
List refresh control
1. Use ListNotifier to create observables
class ViewModel extends ChangeNotifier {
final list = ListNotifier<String>([]);
}
Copy the code
2. Use ListListenableBuilder to listen for data changes
ListListenableBuilder(
valueListenable: viewModel.list,
builder: (context, value, child) {
debugPrint("ValueListenableBuilder $value");
return Text("ValueListenableBuilder $value"); },)Copy the code
Implement life cycle management
LifecycleWidget, which provides Widget lifecycle listening, opens the following callback interface for initialization and unbinding
- Create can listen for a data change
- InitState, Widget initState callback
- InitFrame, the Widget’s first frame drawing completes the call
- Deactivate, Widget deactivate callback
- Dispose, Widget Dispose callback
- DidUpdateWidget, Widget didUpdateWidget callback
- DidChangeDependencies, Widget didChangeDependencies callback
ViewModelProvider
The ViewModel is created for use by the child widgets and opens up the following callback interfaces for initialization and unbinding
- InitViewModel, executed during the first initialization of Widget initState by ViewModel
- BindViewModel, the ViewModel binds the Widget for the first time, and the method is executed during Widget build
- DisposeViewModel, ViewModel destruction, Widget dispose
// [ViewModelProvider] creates the ViewModel
class ProviderExample extends StatelessWidget {
@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");
returnViewModelWidget(viewModel); }); }}Copy the code
You can also create a ViewModel by inheriting the ViewModelProviderWidget
Create ViewModel by inheriting [ViewModelProviderWidget]
class ProviderWidgetExample extends ViewModelProviderWidget<ViewModel> {
ProviderWidgetExample() : super(a);@override
ViewModel create(BuildContext context) => ViewModel();
@override
void initViewModel(BuildContext context, ViewModel viewModel) {
debugPrint("ProviderWidgetExample initViewModel $viewModel");
}
@override
void bindViewModel(BuildContext context, ViewModel viewModel) {
debugPrint("ProviderWidgetExample bindViewModel $viewModel");
}
@override
Widget buildChild(BuildContext context, ViewModel viewModel, Widget child) {
debugPrint("ProviderWidgetExample build $viewModel");
returnViewModelWidget(viewModel); }}Copy the code
ViewModel nested processing
The ViewModel is nested within the ViewModel to manage the child viewModels, providing two ways to manually call the refresh, and another way to replace the ViewModel with a ValueNotifier wrapper that does not need to manually refresh. Like the ViewModelProvider, there are related abstract classes that provide inheritance support.
class ParentViewModel extends ChangeNotifier {
final valueViewModel = ValueNotifier(ChildViewModel());
var childViewModel = ChildViewModel();
void valueNotifier(a) {
valueViewModel.value = ChildViewModel();
}
void notifyListenerChild(a) { childViewModel = ChildViewModel(); notifyListeners(); }}class ChildViewModel extends ChangeNotifier {
final value = ValueNotifier(0); addValue() { value.value++; }}Copy the code
1 Use ViewModelProvider to create the parent ViewModel
class ChildProviderExapmle extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ViewModelProvider<ParentViewModel>(
create: (context) => ParentViewModel(),
builder: (context, viewModel, child) {
return Scaffold(
body: Container(
child: Column(
children: [
ValueViewModelProviderExample(),
ChildViewModelProviderExample(),
ElevatedButton(
onPressed: () => viewModel.valueNotifier(),
child: Text("valueNotifier"),
),
ElevatedButton(
onPressed: () => viewModel.notifyListenerChild(),
child: Text("notifyListenerChild"),),),),); }); }}Copy the code
2 Create a child ViewModelProvider
2-1 ChildViewModelProvider
The need for manual refreshes is usually used for list refreshes in the Item field, which is added to the ViewModelProvider’s existing callbacks
- ChangeViewModel, the binding process can be re-executed after the child ViewModel is replaced
// [ChildViewModelProvider] gets the child ViewModel example
class ChildViewModelProviderExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChildViewModelProvider<ParentViewModel, ChildViewModel>(
create: (_, parent) => parent.childViewModel,
changeViewModel: (context, parent, viewModel, oldViewModel) {
debugPrint(
"ChildViewModelProvider changeViewModel $viewModel, $oldViewModel");
},
builder: (context, parent, viewModel, child) {
debugPrint("ChildViewModelProvider builder $viewModel");
return Row(
children: [
ValueListenableBuilder(
valueListenable: viewModel.value,
builder: (context, value, child) => Text("${viewModel.value}"),
),
ElevatedButton(
onPressed: () => viewModel.addValue(),
child: Text("addValue"), a)],); }); }}Copy the code
2-2 ValueViewModelProvider
The function and callback are the same as ChildViewModelProvider, receiving ValueListenable
// [ValueViewModelProvider] gets the child ViewModel example
class ValueViewModelProviderExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ValueViewModelProvider<ParentViewModel, ChildViewModel>(
create: (_, parent) => parent.valueViewModel,
changeViewModel: (context, parent, viewModel, oldViewModel) {
debugPrint(
"ValueViewModelProvider changeViewModel $viewModel, $oldViewModel");
},
builder: (context, parent, viewModel, child) {
debugPrint("ValueViewModelProvider builder $viewModel");
return Row(
children: [
ValueListenableBuilder(
valueListenable: viewModel.value,
builder: (context, value, child) => Text("${viewModel.value}"),
),
ElevatedButton(
onPressed: () => viewModel.addValue(),
child: Text("addValue"), a)],); }); }Copy the code
Access to the ViewModel
1 Extension Function
ViewModelProvider ChildViewModelProvider and ValueViewModelProvider’s viewModel can be quickly retrieved by context.viewModel< viewModel >(). This method is used during Widget build, and you can use the provider-provided extension context.read
() directly if you want to use it during initStatus.
2 ViewModelBuilder
Use to retrieve the ViewModel provided by the ViewModelProvider
ValueListenableBuilder
ValueListenableBuilder can only listen for current data refreshes, At the same time can be used to monitor multiple data refresh ValueTuple2WidgetBuilder ValueListenableTuple7Builder and ValueListenableListBuilder
[ValueListenableBuilder] [ValueListenableBuilder
Widget _buildValueListenable(ViewModel viewModel) {
return Column(
children: [
/// listen for single data changes
ValueListenableBuilder(
valueListenable: viewModel.value1,
builder: (context, value, child) {
debugPrint("ValueListenableBuilder $value");
return Text("ValueListenableBuilder $value");
}),
/// listen for multiple data changes, inherited from
ValueListenableListBuilder(
valueListenables: [
viewModel.value1,
viewModel.value2,
],
builder: (context, value, child) {
debugPrint(
"ValueListenableListBuilder ${value.first}, ${value.last}");
return Text(
"ValueListenableListBuilder ${value.first}, ${value.last}"); },),/ / / to monitor multiple data changes, inherited from [ValueListenableListBuilder] can specify generic
ValueListenableTuple2Builder(
valueListenables: Tuple2(viewModel.value1, viewModel.value2),
builder: (context, value, child) {
debugPrint(
"ValueListenableTuple2Builder ${value.item1}, ${value.item2}");
return Text(
"ValueListenableTuple2Builder ${value.item1}, ${value.item2}"); },),,); }Copy the code
ViewModelValueBuilder
The ViewModelBuilder and ValueListenableBuilder combination is used to get the ViewModel and manage the Widget refresh area.
Provide an implementation ViewModelValueListBuilder, ViewModelValueTuple2Builder to ViewModelValueTuple7WidgetBuilder can monitor multiple ViewMode parameter changes at the same time to refresh the Widget
[ViewModelValueBuilder] [ViewModelValueBuilder] [ViewModelValueBuilder
Widget _buildViewModelValue(a) {
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}"); },),,); }Copy the code
At the end
If you study with me, you can pay attention to my public account, ❤️ program ape Development Center ❤️, and we will share technology regularly every week. Join me and learn together!