Nested Provider Flutter, E-Com App 8

Firstly in this section we will try to understand the concept of the nested provider in Flutter. We will see why we use it.

Secondly we will know how we can use a nested provider to enhance the performance of our E-Commerce app?

Finally we will show you the advantage of the nested provider in Flutter.

To make it happen we have changed our app entry point, that is the main dart file.

If you compare the difference between these two codes, you will understand why we have used the nested provider.

The previous code looks as follows.

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => Products(),
      builder: (context, _) => DynamicColorBuilder(
        builder: (lightDynamic, darkDynamic) => ThemeProvider(
          lightDynamic: lightDynamic,
          darkDynamic: darkDynamic,
          settings: settings,
          child: const MyApp(),
        ),
      ),
    ),
  );
}

As we can see previously we used the ChangeNotifierProvider create method to manage the state of the app. 

And in that case, we have also used class constructors to pass the data across the app. 

It was okay as long as we just got all the products on the home page and navigated to each product page.

If you want to take a look at the previous code snippets please have a look at the respective GitHub Repository.

And the e-commerce app looked like this:

Managing the Flutter backend and controlling the color
Managing the Flutter backend and controlling the color

But there was a problem. 

We couldn’t tap the favorite sign to show our preference.

How to manage state of individual product

As long as we just display the products through the ChangeNotifierProvider create method, we can pass around the data. But how can we change the state of any individual product item?

Therefore, we must have two separate controllers.

It will not only display the products, but also, at the same time, change the state of the individual product.

As a result, we have used the ChangeNotifierProvider value in the entry point.

void main() {
  runApp(
    ChangeNotifierProvider.value(
      value: Products(),
      builder: (context, _) => DynamicColorBuilder(
        builder: (lightDynamic, darkDynamic) => ThemeProvider(
          lightDynamic: lightDynamic,
          darkDynamic: darkDynamic,
          settings: settings,
          child: const MyApp(),
        ),
      ),
    ),
  );
}

Now, in the Product Grid controller we can use ChangeNotifierProvider value to provide an existing ChangeNotifier.

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

/// seven
///

class ProductsGrid extends StatelessWidget {
  const ProductsGrid({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final productsData = Provider.of<Products>(context);
    final products = productsData.items;
    return GridView.builder(
      padding: const EdgeInsets.all(10.0),
      itemCount: products.length,
      itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
        value: products[i],
        child: const ProductItem(),
      ),
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2,
        childAspectRatio: 3 / 2,
        crossAxisSpacing: 10,
        mainAxisSpacing: 10,
      ),
    );
  }
}

The above code snippet is self explanatory. We can use the context and index of all products and provide ChangeNotifier to the child widget Product Item Widget.

Nested Provider

As a result, now we can easily access each product property through the instantiated product objects.

Because we’ve defined them in our Product class.

import 'package:flutter/foundation.dart';

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

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

  void changeStateOfFavorite() {
    isFavorite = !isFavorite;
    notifyListeners();
  }
}

As we see in the above code, each product object can now use the ChangeNotifier to notify the listeners.

In short, now we can change the state of the individual product.

Watch this part of the above code.

void changeStateOfFavorite() {
    isFavorite = !isFavorite;
    notifyListeners();
  }

Now we can tap the favorite symbol and change the state.

Nested Provider allows to change the State
Nested Provider allows to change the State

To do that we need to change the code of the Product Item controller.

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

import '../models/product.dart';
import '../views/product_detail_screen.dart';

class ProductItem extends StatelessWidget {
  const ProductItem({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final product = Provider.of<Product>(context, listen: false);
    return ClipRRect(
      borderRadius: BorderRadius.circular(10),
      child: GridTile(
        footer: GridTileBar(
          backgroundColor: Colors.black87,
          leading: Consumer<Product>(
            builder: (ctx, product, child) => IconButton(
              icon: Icon(
                product.isFavorite ? Icons.favorite : Icons.favorite_border,
              ),
              color: Theme.of(context).colorScheme.secondary,
              onPressed: () {
                product.changeStateOfFavorite();
              },
            ),
          ),
          title: Text(
            product.title,
            textAlign: TextAlign.center,
          ),
          trailing: IconButton(
            icon: const Icon(
              Icons.shopping_cart,
            ),
            onPressed: () {},
            color: Theme.of(context).colorScheme.secondary,
          ),
        ),
        child: GestureDetector(
          onTap: () {
            Navigator.of(context).pushNamed(
              ProductDetailScreen.routeName,
              arguments: product.id,
            );
          },
          child: Image.network(
            product.imageUrl,
            fit: BoxFit.cover,
          ),
        ),
      ),
    );
  }
}

And we can navigate to the product detail page just like before.

Navigation in Flutter with Modal Route class
Navigation in Flutter with Modal Route class

Why have we used the Consumer to change the state of the individual product item?

Certainly we will discuss it in the next section.

If you want to clone this step, please clone this branch of GitHub Repository.

What Next?

Books at Leanpub

Books in Apress

My books at Amazon

GitHub repository

Python and Data Science

Twitter

Comments

4 responses to “Nested Provider Flutter, E-Com App 8”

  1. […] our previous section we have seen why we use ChangeNotifierProvider value constructor. However, at the same time, we have used Provider Consumer in our Product Item […]

  2. […] Well, we will explain the first question in our next discussion. However, the answer to the second question is simple. When users make some items their favorite, we will show those items as a list. […]

  3. […] To begin with, we must provide and tackle order separately. […]

  4. […] We have already provided and tackled orders separately.  […]

Leave a Reply