What is provider pattern Flutter?

In Flutter when we discuss provider pattern to give examples of provider, we usually think of state management.

It is true that provider package with the help of ChangeNotifierProvider and ChangeNotifier manages state most efficiently.

But provider is actually a flutter architecture that provides the current data model to the place where we need it.

In this very short, but important article on Provider pattern flutter we’ll quickly learn the concept. Let’s have a look at how we can provide the current data model to anywhere in our widget tree.

We’re going to design a shop app where we we’ll have some products. But instead of passing data through class constructor, we’ll rely on provider package with the help of ChangeNotifierProvider and ChangeNotifier.

Before we start, let me tell you where you’ll find the source code. We’ve created a folder branch-one where we keep the main dart file for this example.

Keeping other files intact, you can run the main dart file and get the same result.

Firstly, we must have a data model at our local storage.

In our models folder we have two files Product and Products.

The Product class is the parent class based on which we have added a list of Products.

import 'package:flutter/foundation.dart';

class Product with ChangeNotifier {
  final String id;
  final String title;
  final String description;
  final double price;
  final String imageUrl;

  Product({
    required this.id,
    required this.title,
    required this.description,
    required this.price,
    required this.imageUrl,
  });
}

The Products file is too long, so we cut it short for brevity. The list of products has actually instantiated the product object several times.

import 'package:flutter/material.dart';

import 'product.dart';

class Products with ChangeNotifier {
  final List<Product> products = [
    Product(
      id: 'p1',
      title: 'Classic Watch',
      description: 'A Classic Watch accessorized with style.',
      price: 9.99,
      imageUrl:
          'https://cdn.pixabay.com/photo/2018/02/24/20/39/clock-3179167_960_720.jpg',
    ),
    Product(
      id: 'p1',
      title: 'Shoe with Gears',
      description: 'Shoes paired with excersise accessories.',
      price: 9.99,
      imageUrl:
          'https://cdn.pixabay.com/photo/2017/07/02/19/24/dumbbells-2465478_960_720.jpg',
    ),
...

The question is how we can provide this data model to our home screen.

When should I use provider in Flutter?

We use provider for many purposes. However, at present we want to count how many products objects are there in our local storage.

Let’s just count the length of the products.

To do that, we must place our ChangeNotifierProvider on the top of our main app.

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/products.dart';

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(
          create: (_) => Products(),
        ),
      ],
      child: const ShopAppWithProvider(),
    ),
  );
}

We’re interested about Product objects that have already been instantiated. As a result ChangeNotifierProvider create named parameter returns Products.

Subsequently, we can retrieve all the products by two ways.

Let’s see the first way.

class ShopAppWithProvider extends StatelessWidget {
  const ShopAppWithProvider({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: ShoHomePage(),
    );
  }
}

class ShoHomePage extends StatelessWidget {
  const ShoHomePage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {    
    return Scaffold(
      appBar: AppBar(
        title: const Text('Products App'),
      ),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('All products length:'),
            Text(
              '${context.watch<Products>().products.length}',
              key: const Key('productKeyOne'),
              style: Theme.of(context).textTheme.headline4,
            ),            
          ],
        ),
      ),
    );
  }
}

Now, directly context can watch the products length and give us a nice output like the following image.

Counting the products length
Counting the products length

However, we can count the products length by using Provider also.

What does provider do in Flutter?

As we’ve said earlier, provider does many things and one of them is to provide the current data model to any widget in the tree.

Therefore, this time we’ll change the above code a little bit and will add provider.

class ShopAppWithProvider extends StatelessWidget {
  const ShopAppWithProvider({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: ShoHomePage(),
    );
  }
}

class ShoHomePage extends StatelessWidget {
  const ShoHomePage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    final products = Provider.of<Products>(context).products;
    return Scaffold(
      appBar: AppBar(
        title: const Text('Products App'),
      ),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('All products length:'),
            Text(
              '${context.watch<Products>().products.length}',
              key: const Key('productKeyOne'),
              style: Theme.of(context).textTheme.headline4,
            ),
            const SizedBox(
              height: 10.0,
            ),
            Text(
              '${products.length}',
              key: const Key('productKeyTwo'),
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
    );
  }
}

In the above code, a few lines are worth noting. The first one is the usage of Provider.

final products = Provider.of<Products>(context).products;

Next, the provider provides the current data model products to the Text widget.

Text(
              '${products.length}',
              key: const Key('productKeyTwo'),
              style: Theme.of(context).textTheme.headline4,
            ),

Therefore, the next image shows two numbers of products length, instead of one.

Counting products length two different ways using Provider
Counting products length two different ways using Provider

And, finally, you may have noticed that each Text widget has different keys.

Text(
              '${context.watch<Products>().products.length}',
              key: const Key('productKeyOne'),
              style: Theme.of(context).textTheme.headline4,
            ),
            const SizedBox(
              height: 10.0,
            ),
            Text(
              '${products.length}',
              key: const Key('productKeyTwo'),
              style: Theme.of(context).textTheme.headline4,
            ),

Otherwise, it will throw an exception. Why, because it is final.

We’ll learn more things about provider, so stay tuned as we’ll build that shop app with provider step by step.

What Next?

Books at Leanpub

Books in Apress

My books at Amazon

GitHub repository

Technical blog

Twitter

Comments

5 responses to “What is provider pattern Flutter?”

  1. […] Just like List and Map, the State management might scare us initially. But, we’ll stick with Provider package. […]

  2. […] Next, as the name suggests, Provider package provides not only a simple state management technique, but the data model also. […]

  3. Avatar
    Thong

    I don’t know:
    class Product with ChangeNotifier{}
    and
    class Products extends ChangeNotifier{}
    ? With ? extends.

    1. Avatar

      “With” refers to mixins in Dart and Flutter, which are normal classes from which we can borrow methods(or variables) without extending the class.
      However, we can extend it also. I’ll prefer “with”.

  4. […] In this section, we’ll see how to use class Constructors to pass the provided value or ThemeData object. The next section will show you a more robust and easy way to use Provider in providing ThemeData object with less lin… […]

Leave a Reply