Flutter Provider Package - State Management Made Easy

Provider is a state management solution for Flutter applications. 


It is a part of the Flutter ecosystem and is used to manage and share data and application state across the widget tree efficiently. 


Provider makes it easy to implement the InheritedWidget pattern and helps in managing the state in a clean and scalable way.

In this tutorial, you will learn how to use Provider to manage state in your Flutter app and avoid common pitfalls that arise when managing state manually.

The Flutter Provider package provides several types of providers:

  • Provider: The most basic type, which provides a value to its descendants.
  • ChangeNotifierProvider: Used for providing a ChangeNotifier class and automatically handling its lifecycle.
  • ListenableProvider: Similar to ChangeNotifierProvider, but for classes that implement the Listenable interface.
  • ValueNotifierProvider: Provides a ValueNotifier and is particularly useful for simple state management.
  • StreamProvider: Provides a stream of data to its descendants.
  • FutureProvider: Provides a future result to its descendants.

The difference between Provider.of, Consumer, and Selector in Flutter Provider

  • Consumer updates the entire subtree when any value within the provided object changes.
  • Selector allows you to specify which values trigger a rebuild of the subtree.
  • Provider.of does not differentiate between specific values within the provided object and will rebuild the entire widget using it when notifyListeners is called.

Provider is a great choice for state management in Flutter when:

  • You have a small to medium-sized application.
  • You want a simple and lightweight solution.
  • You need a way to efficiently share state across the widget tree.

You might consider other options like BLoC when:

  • You have a large and complex application with a lot of shared state and business logic.
  • You need more advanced features like middleware, dependency injection, or powerful reactive programming.
  • Remember that the choice of state management solution in Flutter depends on the specific requirements of your project and your familiarity with the chosen solution.

..
Here's a table summarizing the differences between setState and Provider in Flutter

FeaturesetStateProvider
UsageManages state within a single widgetManages state across multiple widgets
ScopeLimited to a widget subtreeCan be used to share state across the app
State SharingState must be passed down explicitlyState is provided and accessed via a provider
Code StructureState and UI logic in the same classSeparation of state management from UI components
RebuildRebuilds the widget and its subtreeRebuilds only necessary parts of the widget tree
ComplexitySimple and straightforwardScalable and flexible with various state types
PerformanceMay trigger unnecessary rebuildsOptimizes UI updates by rebuilding only as needed
..

Code Sample:

To implement state management using the Provider package in Flutter. 

Explore a demo app that demonstrates how to increment a counter using the Provider.of method and update the UI dynamically



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



//lib/counter_notifier.dart
// Create a custom ChangeNotifier class:
class CounterNotifier extends ChangeNotifier {
  int _count = 0; // Initialize a private integer variable to store the count.

  int get count => _count; // A getter method to access the count value.

  void increment() {
    _count++; // Increment the count when this method is called.
    notifyListeners(); // Notifies listeners to rebuild when the state changes.
  }
}


// lib/main.dart
void main() {
  runApp(
    // Wrap your app in the Provider widget
    // ChangeNotifierProvider: Used for providing a class that extends ChangeNotifier.
    // It handles the lifecycle of the ChangeNotifier automatically.

    ChangeNotifierProvider(
      create: (context) => CounterNotifier(),
      child: const MyApp(),
    ),
  );
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Provider Demo',
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Flutter Provider Demo'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Text(
                'You have pushed the button this many times:',
              ),
              // Use the Provider.of method to access your data model
              Consumer<CounterNotifier>(
                builder: (context, counter, child) => Text(
                  '${counter.count}',
                  style: Theme.of(context).textTheme.headlineMedium,
                ),
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            // Use the Provider.of method to call methods on your data model
            Provider.of<CounterNotifier>(context, listen: false).increment();
          },
          tooltip: 'Increment',
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}



..

Code Sample 2: to use multiple providers

To extend this example to use multiple providers, you can wrap your app with a MultiProvider widget and include multiple providers inside it


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

void main() {
  runApp(
    MultiProvider(
      providers: [
        // Provide the CounterNotifier using ChangeNotifierProvider.
        ChangeNotifierProvider(create: (context) => CounterNotifier()),
        // Add another provider for a different data model.
        // Replace YourDataModel with the actual class for your second data model.
        ChangeNotifierProvider(create: (context) => YourDataModel()),
        // You can add more providers for additional data models if needed.
      ],
      child: const MyApp(),
    ),
  );
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter MultiProvider Demo',
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Flutter MultiProvider Demo'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children:[
              const Text(
                'Counter Value:',
              ),
              // Use the Consumer widget to access the CounterNotifier.
              Consumer<CounterNotifier>(
                builder: (context, counter, child) => Text(
                  '${counter.count}',
                  style: Theme.of(context).textTheme.headline5,
                ),
              ),
              const SizedBox(height: 20),
              const Text(
                'Other Data Value:',
              ),
              // Use the Consumer widget to access the YourDataModel.
              Consumer<YourDataModel>(
                builder: (context, dataModel, child) => Text(
                  '${dataModel.someValue}',
                  style: Theme.of(context).textTheme.headline5,
                ),
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            // Increment the counter using the CounterNotifier.
            Provider.of<CounterNotifier>(context, listen: false).increment();
          },
          tooltip: 'Increment',
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}

// Create a custom ChangeNotifier class for your first data model.
class CounterNotifier extends ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

// Create a custom data model class for your second data model.
class YourDataModel extends ChangeNotifier {
  int someValue = 42; // Example data property.

  // You can add methods and properties specific to this data model.
}

..


Comments