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!