Skip to content

shiwam77/state_management

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MVVM Pattern in Flutter with Shared ViewModels

This repository demonstrates the implementation of the Model-View-ViewModel (MVVM) design pattern in Flutter. It uses the provider package for state management, with a focus on using shared ViewModels across multiple screens. This allows for efficient state management where the ViewModel's state can be shared between different UI components while still following the MVVM pattern.


Table of Contents


Introduction

The MVVM pattern helps separate the concerns of the app by dividing it into three key components:

  • Model: Represents the data structure.
  • View: Represents the UI and layout.
  • ViewModel: Handles business logic and state, and provides data to the view.

In this example, we also demonstrate the use of Shared ViewModels. This allows different parts of the application to share the same ViewModel instance, ensuring that changes in one part of the app reflect in other parts.


Key Components

ViewModel

The ViewModel holds the app's business logic and state. It communicates with the model layer to fetch or store data and exposes that data to the view. The ViewModel can be shared across different screens.

Key Methods:

  • initState: Initialize the ViewModel.
  • init: Set up any necessary dependencies or services.
  • onBuild: Called when the view is built.
  • onMount: Called when the view is mounted.
  • onUnmount: Called when the view is unmounted.
  • onResume, onPause, onInactive, onDetach: Lifecycle hooks to manage app states.

MVVM Widget

The MVVM widget binds the ViewModel with the UI. It handles lifecycle management and ensures that the ViewModel is provided to the view.

MVVM<MyViewModel>(
  viewModel: MyViewModel(),
  view: () => MyView(),
);

StatelessView

StatelessView is a subclass of StatelessWidget that builds the UI based on the ViewModel data. It can optionally listen for updates based on the reactive flag.

Example:

class MyView extends StatelessView<MyViewModel> {
  @override
  Widget render(BuildContext context, MyViewModel vm) {
    return Scaffold(
      body: Center(
        child: Text(vm.data),
      ),
    );
  }
}

SharedVM

The SharedVM widget enables the sharing of a ViewModel across different parts of the app. It ensures that data is maintained consistently across different views or screens. This is particularly useful when you want to share state between multiple views that need to react to changes in the same ViewModel instance.

Example:

SharedVM<MyViewModel>(
  builder: (context, vm) => MyView(),
);

void setupLocator() {
  locator.registerFactory<FirstScreenVm>(() => FirstScreenVm());
  locator.registerFactory<SecondScreenVm>(() => SecondScreenVm());
}

class FirstScreen extends StatelessWidget {
  const FirstScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return MVVM(
        view: () => Builder(builder: (context) {
              return const FirstScreenView();
            }),
        viewModel: locator<FirstScreenVm>());
  }
}

class FirstScreenView extends StatelessView<FirstScreenVm> {
  const FirstScreenView({super.key});

  @override
  Widget render(BuildContext context, FirstScreenVm vm) {
    return Column(
      children: [
        ElevatedButton(
          onPressed: () {
            vm.increment();
          },
          child: const Text('Increment'),
        ),
        Text(vm.counter.toString()),
        ElevatedButton(
          onPressed: () {
            vm.decrement();
          },
          child: const Text('Decrement'),
        ),
        ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => const SecondScreen()),
            );
          },
          child: const Text('Go to Next Screen'),
        ),
      ],
    );
  }
}

class SecondScreen extends StatelessWidget {
  const SecondScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return MVVM<SecondScreenVm>(
        viewModel: locator<SecondScreenVm>(),
        view: () => SecondScreenView());
  }
}

/// Example One
class SecondScreenView extends StatelessView<SecondScreenVm> {
  const SecondScreenView({super.key});

  @override
  Widget render(BuildContext context, SecondScreenVm vm) {
    return SharedVM<FirstScreenVm>(
      builder: (context, sharedVm) {
        return Scaffold(
          body: Column(
            children: [
              Text("Shared ViewModel"),
              Row(
                children: [
                  ElevatedButton(
                    onPressed: () {
                      sharedVm.increment();
                    },
                    child: const Text('Increment Shared'),
                  ),
                  Text(sharedVm.counter.toString()),
                  ElevatedButton(
                    onPressed: () {
                      sharedVm.decrement();
                    },
                    child: const Text('Decrement Shared'),
                  ),
                ],
              ),
              ElevatedButton(
                onPressed: () {
                  Navigator.pop(context);
                },
                child: const Text('Back'),
              ),
              Text("Current ViewModel"),
              Row(
                children: [
                  ElevatedButton(
                    onPressed: () {
                      vm.increment();
                    },
                    child: const Text('Increment Current'),
                  ),
                  Text(vm.counter.toString()),
                  ElevatedButton(
                    onPressed: () {
                      vm.decrement();
                    },
                    child: const Text('Decrement Current'),
                  ),
                ],
              ),
            ],
          ),
        );
      },
    );
  }
}
/// Example two for easy usage
class SecondScreenView
    extends StatelessViewWithSharedVmCurrentVM<FirstScreenVm, SecondScreenVm> {
  const SecondScreenView({super.key, super.reactiveShared = false});

  @override
  Widget render(
      BuildContext context, FirstScreenVm sharedVm, SecondScreenVm vm) {
    return Scaffold(
        body: Column(
      children: [
        Text("Shared vm"),
        Row(
          children: [
            ElevatedButton(
              onPressed: () {
                sharedVm.increment();
              },
              child: const Text('Increment'),
            ),
            Text(sharedVm.counter.toString()),
            ElevatedButton(
              onPressed: () {
                sharedVm.decrement();
              },
              child: const Text('decrement'),
            ),
          ],
        ),
        Spacer(),
        ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: const Text('back'),
        ),
        Spacer(),
        Text("Current vm"),
        Row(
          children: [
            ElevatedButton(
              onPressed: () {
                vm.increment();
              },
              child: const Text('Increment'),
            ),
            Text(vm.counter.toString()),
            ElevatedButton(
              onPressed: () {
                vm.decrement();
              },
              child: const Text('decrement'),
            ),
          ],
        ),
      ],
    ));
  }

  @override
  FirstScreenVm provideSharedViewModel() {
    return locator<FirstScreenVm>();
  }
}

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors