Managing State in Flutter

Sep 22 2022 · Dart 2.17, Flutter 3.0, Android Studio Chipmunk

Part 2: Use Provider

14. Use Multiple Providers

Episode complete

Play next episode

Next
About this episode
Leave a rating/review
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 13. Understand Consumers Next episode: 15. Learn Other Provider Features

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Transcript: 14. Use Multiple Providers

So far we’ve been using one provider to access our model, but chances are, you’ll have lots of different models in your app. In such a case, we need to use multiple providers. Now, can nest providers much the same way as we nest widgets.

While this will technically work, it may be a bit confusing. Providers aren’t widgets. They provide data so searching a widget tree to review your state may slow you down.

Provider allows us to flatten our nested provider by using the MultiProvider. The multi-provider simply takes in an array of providers in a simple list.

Now we want to add additional pillars to our app. We want to track iOS and Android pillars. This means we have three pillar states. So big question, when we you request the state back from Provider,

How will it know the difference between an Android pillar and a Flutter pillar? Well, it can’t. Our state types need to be unique when working with provider. There are two ways for us work around this limitation. We can create a single type that contains all of our domains, or we can create wrapper classes for our state. For the sake of demonstration, we will create a wrapper.

Open your project in progress or download the starter project for this episode. We want to create three different types to represent our three different pillars. To do this, we’re going to create an abstract class call Domains that will define the base behavior and then create subclasses for each our pillars.

In the model folder, create a new file called domain.dart. Import both the material library as well as the pillar class.

import 'package:flutter/material.dart';
import 'pillar.dart';

Next, create the abstract class Domain, making sure to extend the ChangeNotifier.

abstract class Domain extends ChangeNotifier { 

}

This class will take in a pillar. Add the following:

  final Pillar pillar;
  Domain(this.pillar);

Like we did with the inherited widget, we’re going to expose all the various pillar properties through the abstract class.

void increaseArticleCount({int by = 1}) {
    pillar.increaseArticleCount(by: by);
    notifyListeners();
}

int get articleCount => pillar.articleCount;
String get imageName => pillar.type.imageName;
Color get backgroundColor => pillar.type.backgroundColor;

And that’s our base class. Now for the subclasses for each pillar. We’ll create one for Flutter, iOS and Android.

class Flutter extends Domain {
  Flutter(super.pillar);
}

class Android extends Domain {
  Android(super.pillar);
}

class Swift extends Domain {
  Swift(super.pillar);
}

Now we have our three different types which means, we can use them with three change providers. Time to put them to use. Open main.dart and import domain.dart.

import 'models/domain.dart';

Now it’s time to add our multi-provider. We’ll pass in three different change providers, creating each of our subclasses, passing in the pillar data.

body: MultiProvider(
    providers: [
    ChangeNotifierProvider<Flutter>(
        create: (context) => Flutter(
        Pillar(
            type: PillarType.flutter,
            articleCount: 115,
        ),
        ),
    ),
    ChangeNotifierProvider<Android>(
        create: (context) => Android(
        Pillar(
            type: PillarType.android,
            articleCount: 282,
        ),
        ),
    ),
    ChangeNotifierProvider<Swift>(
        create: (context) => Swift(
        Pillar(
            type: PillarType.swift,
            articleCount: 608,
        ),
        ),
    )
    ],

And that’s it - we now have our data being routed through Provider. Now, let’s access it. Open tutorials_page.dart. First import domain.dart.

import '../models/domain.dart';

Scroll down. Notice we are using a consumer that provides one object. We still want to use the consumer, except instead of receiving one item, we want to receive three items.

There’s actually consumers dedicated to just that. If you wanted to receive two items, you’d use a Consumer2. In our case, we want to receive three items, so we’ll use a Consumer3. Update the consumer to the following:

return Consumer3<Flutter, Android, Swift>(
        builder: (_, flutter, android, swift, __) {
...
        ); // Column
      } 
    ); // Consumer3

Since we aren’t using the context or using any prebuilt children, we use the underscores. Now we want to get the total amount of tutorials. We’ll need to add all the tutorials together. Add the following:

var totalArticles =
          android.articleCount + flutter.articleCount + swift.articleCount;

Now update the total tutorial amount using the totalArticles.

Padding(
    padding: const EdgeInsets.only(top: 24.0),
    child: Text(
        'Total Tutorials: $totalArticles',
        style: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
    ),
)

Now to update the tutorial widget. Instead of repeating all our Provider code, we’ll pass in each domain. Open tutorial_widget.dart. Update it to accept a domain.

const TutorialWidget({required this.domain, super.key});
final Domain domain;

Now that we have the domain, we can access all of our required data. First, in the Inkwell button, we need to increase the article count and use the correct image. Add the following:

InkWell(
  onTap: () {
    widget.domain.increaseArticleCount();
  },
  child: Image.asset('assets/images/${widget.domain.imageName}',
    width: 110, height: 110),

Next, we’ll update the circle avatar to show the correct widgets. Add the following:

CircleAvatar(
            backgroundColor: widget.domain.backgroundColor,
            child: Text(widget.domain.articleCount.toString()),
          ),

Finally, update the method so that it no longer users a consumer since we aren’t accessing the provider.

  Widget build(BuildContext context) {
    return Stack(
      ...
    );
  }

And that’s it - we have the tutorial widget. We just need to update the calling site. Open tutorials_page.dart. Update the Column to the following:

Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
    Center(
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          TutorialWidget(
            domain: flutter,
          ),
          TutorialWidget(
            domain: android,
          ),
          TutorialWidget(
            domain: swift,
          ),
        ],
      ),
    ),

And with that, we now have a multi-provider in action. Build and run the app. Tap on each different domain, and you’ll see both the individual article count will increase as well as the total article count.