A set of widgets to help with the implementation of the Provider architecture as shown by FilledStacks

Overview

Provider Architecture - Deprecated on 21 April 2020

Notice

V2 of this package is renamed to Stacked

Stacked is the name of the architecture that was originally inspired by MVVM for the first implementation. It has some name changes but everything else is the same so a migration would be painless. There are multiple things that it improves on. The two main things are ViewModel management (creating only when required, not creating it everytime it's rebuilt), responsive services which allows viewmodels to register for service changes. A series will be released that covers the entire architecture stack used for production development along with stacked.

Migrating from provider_architecture to Stacked

Lets start with a statement to ease your migration panic ๐Ÿ˜… stacked is the exact same code from provider_architecture with naming changes and removal of some old deprecated properties. If you don't believe me, open the repo's side by side and look at the lib folders. Well, up till yesterday (22 April 2020) I guess, when I updated the BaseViewModel. I wanted to do this to show that stacked is production ready from the go. It's a new package but it's been used by all of you and the FilledStacks development team for months in the form of provider_architecture. With that out of the way lets start the migrate.

ViewModelProvider Migration

This class has now been more appropriately named ViewModelBuilder. This is to match it's functionality more closely. Building UI FROM the ViewModel. The ViewModel is used to drive the state of the reactive UI.

Migrations to take note of:

  • ViewModelProvider -> ViewModelBuilder
  • Named constructor withoutConsumer is now called nonReactive
  • Named constructor withConsumer is now called reactive
  • Instead of passing a constructed ViewModel which was constructing every rebuilder we pass a viewModelBuilder. A function that returns a ChangeNotifier.
  • reuseExisting has changed to disposeViewModel and now has a default value of true. If you used reuseExisting=true it has to change to disposeViewModel=false.

Lets look at that in code. We'll go over withoutConsumer / nonReactive first

class HomeViewMultipleWidgets extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ViewModelProvider<HomeViewModel>.withoutConsumer(
      viewModel: HomeViewModel(),
      onModelReady: (model) => model.initialise(),
      reuseExisting: true,
      builder: (context, model, _) => Scaffold(
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            model.updateTitle();
          },
        ),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[TitleSection(), DescriptionSection()],
        ),
      ),
    );
  }
}

Will Change to

class HomeViewMultipleWidgets extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ViewModelBuilder<HomeViewModel>.nonReactive( // Take note here
      viewModelBuilder: () => HomeViewModel(), // Take note here
      disposeViewModel: false, // Take note here
      onModelReady: (model) => model.initialise(),
      builder: (context, model, _) => Scaffold(
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            model.updateTitle();
          },
        ),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[TitleSection(), DescriptionSection()],
        ),
      ),
    );
  }
}

For the withConsumer function we do the following

class HomeView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ViewModelProvider<HomeViewModel>.withConsumer(
    );
  }
}

Changes to

class HomeView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ViewModelBuilder<HomeViewModel>.reactive( // Take note here

    );
  }
}

ProviderWidget Migration

The only change here was the name.

class DuplicateNameWidget extends ProviderWidget<Human> {

}

// Becomes

class DuplicateNameWidget extends ViewModelWidget<Human> {

}

The rest of the package is all new functionality which can be seen above. Please check out the issues for tasks we'd like to add. If you would like to see any functionality in here please create an issue and I'll asses and provide feedback.

End of notice

This package contains base widgets that can be used to implement the MvvmStyle provider architecture as laid out by FilledStacks.

ViewModelProvider

The ViewModelProvider was first built in the Provider Architecture Tutorial where it was titled BaseView. The ViewModelProvider is used to create the "binding" between a ViewModel and the View. There is no two-way binding in this architecture, which is why I don't want to say it's an Mvvm implementation. The ViewModelProvider wraps up all the ChangeNotifierProvider code which allows us to trigger a rebuild of a widget when calling notifyListeners within the ViewModel.

A ViewModel is simply a dart class that extends ChangeNotifier. The ViewModelProvider has 2 constructors, one with a builder and one without a builder. The tutorial mentioned above emulates the default implementation which has been put into the .withConsumer named constructor. The .withoutConsumer constructor is for UI that does not require the model at the constructor level. The withoutConsumer construction was born in this tutorial where we wanted to reduce the boiler plate when the same data has to go to multiple widgets.

With Consumer

An example of this would be the traditional View-ViewModel setup we have.

// View
class HomeView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Using the withConsumer constructor gives you the traditional viewmodel
    // binding which will rebuild when notifyListeners is called. This is used
    // when the model does not have to be consumed by multiple different UI's.
    return ViewModelProvider<HomeViewModel>.withConsumer(
      viewModelBuilder: () => HomeViewModel(),
      onModelReady: (model) => model.initialise(),
      builder: (context, model, child) => Scaffold(
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            model.updateTitle();
          },
        ),
        body: Center(
          child: Text(model.title),
        ),
      ),
    );
  }
}

// ViewModel
class HomeViewModel extends ChangeNotifier {
  String title = 'default';

  void initialise() {
    title = 'initialised';
    notifyListeners();
  }

  int counter = 0;
  void updateTitle() {
    counter++;
    title = '$counter';
    notifyListeners();
  }
}

When notifyListeners is called in the ViewModel the builder is triggered allowing you to rebuild your UI with the new updated ViewModel state. The process here is you update your data then call notifyListeners and rebuild your UI.

Without Consumer

The .withoutConsumer constructor is best used for providing your ViewModel to multiple children widgets. It was created to make it easier to build and provide the same ViewModel to multiple UI's. It was born out of my Responsive UI architecture where we would have to provide the same ViewModel to all the different responsive layouts. Here's a simple example.

// Viewmodel in the above code

// View
class HomeViewMultipleWidgets extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ViewModelProvider<HomeViewModel>.withoutConsumer(
      viewModelBuilder: () => HomeViewModel(),
      onModelReady: (model) => model.initialise(),
      builder: (context, model, _) => Scaffold(
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            model.updateTitle();
          },
        ),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[TitleSection(), DescriptionSection()],
        ),
      ),
    );
  }
}

class TitleSection extends ProviderWidget<HomeViewModel> {
  @override
  Widget build(BuildContext context, HomeViewModel model) {
    return Row(
      children: <Widget>[
        Text(
          'Title',
          style: TextStyle(fontSize: 20),
        ),
        Container(
          child: Text(model.title),
        ),
      ],
    );
  }
}

class DescriptionSection extends ProviderWidget<HomeViewModel> {
  @override
  Widget build(BuildContext context, HomeViewModel model) {
    return Row(
      children: <Widget>[
        Text(
          'Description',
          style: TextStyle(fontSize: 14, fontWeight: FontWeight.w700),
        ),
        Container(
          child: Text(model.title),
        ),
      ],
    );
  }
}

So what we're doing here is providing the ViewModel to the children of the builder function. The builder function itself won't retrigger when notifyListeners is called. Instead we will extend from ProviderWidget in the widgets that we want to rebuild from the ViewModel. This allows us to easily access the ViewModel in multiple widgets without a lot of repeat boilerplate code. We already extend from a StatelessWidget so we can change that to ProviderWidget and we always have a build function so we simply add the ViewModel as a parameter to that. This is the same as calling Provider<ViewModel>.of in every widget we want to rebuild.

Reusing Viewmodel Instance

An example of how to use one viewmodel instance across the application with the help of get_it.

// Registering the viewmodel inside the get_it service locator
GetIt locator = GetIt.instance;

setupServiceLocator() {
  // Singleton of the viewmodel
  locator.registerLazySingleton<HomeViewModel>(() => HomeViewModel());
}

// View
class HomeView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Using the withConsumer constructor gives you the traditional viewmodel
    // binding which will rebuild when notifyListeners is called. But instead
    // of creating a new instance of the viewmodel, the singleton instance from
    // the get_it locator is passed through.
    return ViewModelProvider<HomeViewModel>.withConsumer(
      viewModelBuilder: () => locator<HomeViewModel>(),
      onModelReady: (model) => model.initialise(),
      reuseExisting: true,
      builder: (context, model, child) => Scaffold(
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            model.updateTitle();
          },
        ),
        body: Center(
          child: Text(model.title),
        ),
      ),
    );
  }
}

// ViewModel
class HomeViewModel extends ChangeNotifier {
  String title = 'default';

  void initialise() {
    title = 'initialised';
    notifyListeners();
  }

  int counter = 0;
  void updateTitle() {
    counter++;
    title = '$counter';
    notifyListeners();
  }
}

Note that the ViewModelProvider constructor is called with parameter reuseExisting: true. This enables us to pass an existing instance of a viewmodel. In this example we register the viewmodel as lazy singleton using get_it and inside the ViewModelProvider constructor we simply reference the instance of the viewmodel from the locator. This way we can access the viewmodel data (in this example counter) across the application from different views.

Provider Widget

The provider widget is an implementation of a widget class that provides us with the provided value as a parameter in the build function of the widget. Above is an example of using the widget but here's another one that doesn't make use of a ViewModel. Lets say for instance you have a data model you want to use in multiple widgets. We can use the Provider.value call to supply that value and inside the multiple widgets we inherit from the ProviderWidget and make use of the data.

// View
class HomeViewProviderWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Provider.value(
        value: Human(name: 'Dane', surname: 'Mackier'),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[FullNameWidget(), DuplicateNameWidget()],
        ),
      ),
    );
  }
}

// Model
class Human {
  final String name;
  final String surname;

  Human({this.name, this.surname});
}

// consuming widget 1
class FullNameWidget extends ProviderWidget<Human> {
  @override
  Widget build(BuildContext context, Human model) {
    return Row(
      children: <Widget>[
        Container(
          child: Text(
            model.name,
            style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30),
          ),
        ),
        SizedBox(
          width: 50,
        ),
        Container(
          child: Text(
            model.surname,
            style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30),
          ),
        ),
      ],
    );
  }
}

// consuming widget 2
import 'package:example/datamodel/human.dart';
import 'package:flutter/material.dart';
import 'package:provider_architecture/provider_architecture.dart';

class DuplicateNameWidget extends ProviderWidget<Human> {
  @override
  Widget build(BuildContext context, Human model) {
    return Row(
      children: <Widget>[
        Container(
          child: Text(model.name),
        ),
        SizedBox(
          width: 50,
        ),
        Container(
          child: Text(model.name),
        ),
      ],
    );
  }
}

The package do not implement the architecture for you but it definitely helps the implementation.

Non-updating Provider Widget

Sometimes you want a widget to have access to the viewmodel but you don't want it to rebuild. In the case of a button that has to call a function on the viewmodel but uses none of the viewmodel state for the UI. In this case you can set the listen value to false for the super constructor of the ProviderWidget

class UpdateTitleButton extends ProviderWidget<HomeViewModel> {
  UpdateTitleButton({
    Key key,
  }) : super(key: key, listen: false);

  @override
  Widget build(BuildContext context, model) {
    return FloatingActionButton(
      onPressed: () {
        model.updateTitle();
      },
    );
  }
}
Comments
  • Using provider_architecture with responsive_builder

    Using provider_architecture with responsive_builder

    First of all, I really appreciate your amazing plugin both the responsive_builder and provider_architecture. It really saves my days and headache of separation of concerns THANKS.

    Am trying to use provider_architecture along with reponseive_builder. Am currently trying it for Mobile Orientation(Landscape and portrait). The Portrait works but when I rotate the device the Landscape it doesn't change the UI(as expected). I even try to add Watch and Tablet yet still the same. have try to use ViewModelProvider<LoginViewModel>.withoutConsumer and ViewModelProvider<LoginViewModel>.withConsumer Here is my code

     @override
      Widget build(BuildContext context) {
        return ViewModelProvider<LoginViewModel>.withConsumer(
          viewModel: LoginViewModel(),
          builder: (context, model, child) => 
          ResponsiveBuilder(
          builder:  (context, sizingInformation){
              if (sizingInformation.deviceScreenType == DeviceScreenType.Tablet) {
                return Container(color: Colors.red,);
              }
    
              if (sizingInformation.deviceScreenType == DeviceScreenType.Mobile) {
                
                    return OrientationLayoutBuilder(
                  portrait: (context) => Container(color: Colors.amber,),
                  landscape: (context) => LoginViewLandscape(),
                    );
    
              }if (sizingInformation.deviceScreenType == DeviceScreenType.Watch) {
                return Container(color: Colors.black,);
              }
    
              return Container(color:Colors.purple);
          }
          ));
      }
    

    What can be wrong here?

    opened by SilenceCodder 4
  • Update provider package

    Update provider package

    The provider package that package depends on is now version 4.0.1. I don't know if that makes a difference for any of the code, but I generally like to use the most up to date packages.

    opened by suragch 3
  • Turn off Listen for ProviderWidget

    Turn off Listen for ProviderWidget

    We have identified in some cases that we don't want to rebuild a widget when notifyListeners is called. This stems from the need to use a ViewModel in a widget, but that widget does not make use of the state of the ViewModel. Since the ViewModel is not using the state a rebuild is a wasted resource.

    A solution to this would be to add a construction optional parameter to the ProviderWidget called listen. This property will be a boolean with a default value true. We will then use this property in the Provider element where we use Provider.of(context) and pass the property to the listen property on the .of call.

    enhancement 
    opened by FilledStacks 3
  • What is the best practice for using the ViewModelProvider as a singleton

    What is the best practice for using the ViewModelProvider as a singleton

    Usually I want to keep the state of my ViewModelProvider for the life of the app. I recall in your filledstacks tutorals you would use Get_it, locator.registerLazySingleton(() => MyViewModelProvider()); to solve this. Is that what you would recommend?

    opened by kauaicreative 2
  • added widget test for package and example

    added widget test for package and example

    Added widget test for package and example. For the package, I have added widget test for ProviderWidget, ViewModelProvider.withConusmer as well as ViewModelProvider.withoutConsumer,

    #12 While opening the issue, I was facing difficulty to write the test cases and thought it was related to this package. After doing some research, I found out it was due to Provider Package.

    The test cases will be helpful to you to check if package is working properly or not after every update.

    opened by AyushBherwani1998 1
  • using widgets.dart rather than material.dart

    using widgets.dart rather than material.dart

    Since there seems to be no absolute requirement to use material.dart, it would be better to use the lower level widgets.dart. That way there would not be potential conflicts for users who want to use cupertino.dart.

    opened by suragch 0
  • Docs & code mismatch

    Docs & code mismatch

    Docs say withBuilder/withoutBuilder. The code says withConsumer/withoutConsumer.

    https://github.com/FilledStacks/provider_architecture/blob/a5375f634d19df26ab12360909e93c24c9c0c83f/lib/viewmodel_provider.dart#L14

    https://github.com/FilledStacks/provider_architecture/blob/a5375f634d19df26ab12360909e93c24c9c0c83f/lib/viewmodel_provider.dart#L21

    opened by chimon2000 0
  • Caching the Model Data

    Caching the Model Data

    First of all thanks for provider_architecture in reducing the pain of building up a large and maintainable app

    After reading couple of issues and blog posts. Registering viewModel as registerLazySingleton rather than registerFactory to avoid multiple requests in API or any data layer. I want to know how to achieve caching in this architecture?

    • One way I could think of handling in Services which would give cached data and making background call to update the new data.

    But I am interested in your view and how do you approach it. It would be nice if you are able to explain it with a real use case.

    opened by Amerr 1
  • how to use mvvm architecture with graphql flutter

    how to use mvvm architecture with graphql flutter

    Do we need to follow MVVM with graphql flutter ? If yes how to integrate graphql flutter and provider architecture plugin.

    https://pub.dev/packages/graphql_flutter

    thank you.

    opened by Flutter-Stack 0
  • Reusing ViewModel with ChangeNotifierProvider.value constructor

    Reusing ViewModel with ChangeNotifierProvider.value constructor

    Hi, first of all, thank you very much for this fantastic package, it makes my MVVM life much easier.

    I am holding my data models in a List inside of my view model. I do not want to dispose the view model and recreate my data every time I navigate away and back to a certain screen, which is why I hold my view model as a lazy singleton using get_it. This works fine but throws an error when the ChangeNotifierProvider constructor is called within the ViewModelProvider built function with a reused view model.

    What I am currently doing is a reimplementation of the ViewModelProvider class where I call the ChangeNotifierProvider.value constructor inside the built function, which gives me the opportunity to reuse my view model.

      @override
      Widget build(BuildContext context) {
        if (widget.providerType == _ProviderType.WithoutConsumer) {
          return ChangeNotifierProvider.value(
            value: _model,
            child: widget.builder(context, _model, null),
          );
        }
    
        return ChangeNotifierProvider.value(
          value: _model,
          child: Consumer(
            builder: widget.builder,
            child: widget.staticChild,
          ),
        );
      }
    

    My suggestion is to provide an additional parameter in the ViewModelProvider.withConsumer and .withoutConsumer constructor bool reuse = false, which will be looked for in the build function. If reuse == true, the alternative ChangeNotifierProvider.value constructor could be called.

    Or, alternatively, what would be the disadvantage of always using the .value constructor?

    Thank you very much for your feedback!

    opened by mindthefish 3
Owner
Dane Mackier
Full-Stack applications developer focussed on complete Mobile products.
Dane Mackier
An open source food delivery product and service that will be developed on the FilledStacks YouTube channel

Box't Out An open source food delivery product and service that will be developed on the FilledStacks YouTube channel. The repo will contain all the s

Dane Mackier 379 Jan 7, 2023
The repo contains the source code for all the tutorials on the FilledStacks Youtube channel.

Flutter tutorials The repo contains the source code for all the written tutorials by Filledstacks. All Tutorials plus additional snippets and shorter

Dane Mackier 4.5k Dec 31, 2022
A simple screen that is shown when your app gets crashed instead of the normal crash dialog. It's very similar to the one in Flutter.

Red Screen Of Death What A simple screen that is shown when your app gets crashed instead of the normal crash dialog. It's very similar to the one in

Ahmad Melegy 178 Dec 9, 2022
A splash view is a short introduction to an application shown when it is launched.

A splash view is a short introduction to an application shown when it is launched. In the splash view, basic introductory information, such as the company logo, content, etc. is displayed at the moment the application load.

Rahul Chouhan 3 Sep 3, 2022
Target the specific design of Material for Android and Cupertino for iOS widgets through a common set of Platform aware widgets

Flutter Platform Widgets This project is an attempt to see if it is possible to create widgets that are platform aware. Currently in order to render t

null 1.3k Jan 4, 2023
Target the specific design of Material for Android and Cupertino for iOS widgets through a common set of Platform aware widgets

Flutter Platform Widgets This project is an attempt to see if it is possible to create widgets that are platform aware. Currently in order to render t

null 1.3k Jan 4, 2023
Purpose of this project is to create extendable architecture of making platform aware Widgets which automatically select platform specific implementation

Old good factory Main obstacle in creating native experience on Flutter is the fact that you are asked to rebuild two layouts using platform specific

Swav Kulinski (Robotoaster) 101 Oct 14, 2022
Note provider - Note App using Provider state management, Sqflite and Localization for two language Arabic and English.

note_provider Sqflite with provider statemanagement Getting Started This project is a starting point for a Flutter application. A few resources to get

Mohanned Anwar 0 Jan 1, 2022
Dusyeri provider task - Dusyeri provider task built using flutter

DรผลŸyeri Provider Task https://github.com/alper-mf/dusyeri_provider_task/blob/7d1

null 1 May 9, 2022
A go-to handbook with a curated set of resources to help the participants of any Flutter Hackathon..

Let's Get to Speed for Your Next Hackathon Legacy Hack'19, the first of its kind International Flutter Hackathon, organised by the Flutter Community a

Ayush Shekhar 138 Dec 6, 2022
Music player application for android. It's uses MVVM architecture and Provider & ValueNotifier state management.

music-player-flutter Flutter music player application which is my personal project published to play store. Project structures are as following,

null 30 Jul 10, 2022
โš–๏ธ A Flutter Architecture for small/medium/large/big large scale using Provider as State Management with Get It!

Flutter Provider Architecture Mobile Application Developed in Flutter. Running on both mobile platforms, Android ?? & iOS ?? . About this app This app

Samuel Matias 82 Jan 4, 2023
Flutter app using MVVM architecture pattern , dio , provider , intl packages.

News App Simple news application using free news API for fetching realtime data from the internet. Features -Used MVVM architecture pattern. Packages

null 3 Mar 25, 2022
Responsive Whatsapp CLone - MVVM Architecture,Provider

whatsapp_clone Flutter project. Using MVVM Architecture, firebase, Provider and Rest API This project is a starting point for a Flutter application. A

Pranav Pv 20 Jan 2, 2023
Menaclub app for admin using nodejs as backend,RestAPI,provider as statemangement and follows MVVM architecture

menzclub_admin A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started i

Pranav Pv 8 Nov 7, 2022
Touranment Manager app using Firebase ,provider and MVVM Architecture

tournament_lotter A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you starte

Pranav Pv 10 Nov 25, 2022
๐Ÿš€ User management app built in flutter using clean architecture, MVVM, get it, dio, RxDart, bloc, cubit, getX and provider

?? Go Rest app In this project, we are going to build a user management app using Flutter. We have used the Go REST API to make HTTP request methods.

Sina 99 Aug 14, 2023
Raden Saleh 53 Jul 27, 2023
Set of Flutter widgets built using physics based animations.

Elastic Widgets Set of Flutter widgets built using physics based animations. Widgets Seek Bar Range Picker Installation Add this to your package's pub

Alireza Abiri 76 Dec 4, 2021