How to Use Provider for State Management in Flutter || Part - 1

Provider is one of the recommended state management options when using Flutter. It simplifies data flow within your app, making it more manageable and scalable. Here’s a brief overview:

  1. What is Provider?

    • Provider is a package in Flutter that allows you to manage application state efficiently.

    • It follows the provider design pattern, enabling components to consume data without needing to know the source of that data.

    • This decoupling makes it easier to refactor, test, and maintain your code.

  2. How does it work?

    • When you place a Provider widget in your widget tree, all its children have access to the values exposed by it.

    • There are different types of providers, but for this explanation, let’s focus on ChangeNotifierProvider.

    • ChangeNotifierProvider allows you to listen to changes in the provider and automatically rebuilds widgets when needed.

    • You can create your own custom providers by extending ChangeNotifier you can create your own custom providers by extending ChangeNotifier or other provider classes.

  3. Example: Creating a ThemeProvider

    • Suppose you want to change the main colour of your app dynamically.

    • You can create a ThemeProvider class that extends ChangeNotifier.

    • It exposes a mainColor value and a changeThemeColor function to update it.

    • When the mainColor changes, the class notifies its listeners using notifyListeners() .

  4. Usage: Wrapping the Widget Tree

    • In your app, wrap the root widget (usually MaterialApp) with a ChangeNotifierProvider<ThemeProvider>.

    • Use a Consumer<ThemeProvider> widget to access the value of ThemeProvider within your widgets.

    • For example:

        class MyApp extends StatelessWidget {
          @override
          Widget build(BuildContext context) {
            return ChangeNotifierProvider<ThemeProvider>(
              create: (context) => ThemeProvider(),
              child: Consumer<ThemeProvider>(
                builder: (context, themeProvider, child) => MaterialApp(
                  // Your app content here
                ),
              ),
            );
          }
        }
      
    • Now any child widget can access and update the main color using ThemeProvider.mainColor .


How to Use provider in a Flutter Project

Provider is a powerful state management solution for Flutter. Let’s walk through the steps to use it in your project:

  1. Add the Provider Package:

    • Open your pubspec.yaml file and add the provider dependency:

        dependencies:
          flutter:
            sdk: flutter
          provider: ^6.1.2
      
    • Run flutter pub get to fetch the package.

  2. Create a Provider Class:

    • Create a new Dart file (e.g., data.dart) to store your data.

    • Define a class (e.g., Counter) that extends ChangeNotifier.

    • Inside this class, declare your state variables (e.g., _count).

  3. Expose State with the Provider:

    • In your Counter class, provide methods to modify the state (e.g., incrementing the count).

    • Use notifyListeners() to notify listeners when the state changes.

  4. Wrap Your App with the Provider:

    • In your main.dart, wrap your app with a ChangeNotifierProvider:

        void main() {
          runApp(
            ChangeNotifierProvider(
              create: (context) => Counter(),
              child: MyApp(),
            ),
          );
        }
      
  5. Access State in Widgets:

    • Use Consumer<Counter> to access the state within your widgets:

        Consumer<Counter>(
          builder: (context, counter, child) {
            return Text('Count: ${counter.count}');
          },
        )
      

ChangeNotifierProvider in Flutter:

ChangeNotifierProvider is a class from the provider package in Flutter that links the lifecycle of a ChangeNotifier to widgets in the widget tree. Let’s break it down:

  1. What is aChangeNotifier?

    • A ChangeNotifier is a simple class in the Flutter SDK that provides change notification to its listeners.

    • When something is a ChangeNotifier, you can subscribe to its changes. It acts as an observable, notifying listeners when its internal state changes.

  2. How doesChangeNotifierProvider work?

    • ChangeNotifierProvider is responsible for creating and supplying an instance of a ChangeNotifier directly to the UI.

    • It ensures that when the ChangeNotifier emits a signal(due to changes).,dependent widgets are notified and can rebuild accordingly.

  3. Difference betweenChangeNotifierProvider and Provider:

    • The Provider class exposes a value to all widgets but doesn’t listen to changes from that value.

    • In contrast, ChangeNotifierProvider listens to changes from the ChangeNotifier and provides the updated values to dependent widgets.

So, if you want to manage state using a ChangeNotifier, use ChangeNotifierProvider to integrate it seamlessly into your Flutter app!


MultiProvider in Flutter

In Flutter, MultiProvider is a powerful widget provided by the provider package. It allows you to combine multiple providers into a single widget tree. Here’s how it works:

  1. Combining Multiple Providers:

    • Suppose you have different data models (e.g., ChatProvider, MessageProvider) that you want to use across your app.

    • You can wrap your widget tree with a MultiProvider and provide a list of providers. Each provider corresponds to a specific data model.

    • For example:

        MultiProvider(
          providers: [
            ChangeNotifierProvider<ChatProvider>(
              create: (context) => ChatProvider(),
            ),
            ChangeNotifierProvider<MessageProvider>(
              create: (context) => MessageProvider(),
            ),
            // Add more providers as needed
          ],
          child: YourApp(),
        )
      
  2. Accessing Providers:

    • Once you’ve set up the MultiProvider, you can access the provided data models using Provider.of<T>(context).

    • For instance, in your widget, you can use:

        final chatProvider = Provider.of<ChatProvider>(context);
        final messageProvider = Provider.of<MessageProvider>(context);
      
  3. Context Hierarchy:

    • The context hierarchy matters. Providers should be above the widgets that need access to them.

    • If you want to use providers across different routes, place the MultiProvider above your MaterialApp.

    • However, having multiple MaterialApp widgets is not recommended. Instead, consider passing the providers as values to new routes without nesting additional MaterialApp widgets.


Best Practices for organising provider setup:

Certainly! Organising your Provider setup effectively is crucial for maintaining a clean and scalable Flutter app. here are some best practices:

  1. Separate Concerns:

    • Divide your code into logical layers: UI, business logic, and data.

    • Place UI-related widget in separate files(e.g.,screens, widgets folders).

    • Keep business logic (providers)in their own files(e.g.,providers folder)

  2. Provider Classes:

    • Create separate provider classes for different data models or features.

    • For example, if you have tasks and user authentication, create TaskProvider and AuthProvider.

  3. Nested Providers:

    • Use nested providers when one provider depends on another.

    • For instance, if a TaskList widget needs access to both tasks and user data, nest TaskProvider and AuthProvider .

  4. Provider Tree:

    • Visualise your provider tree. Understand which widgets depend on which providers.

    • Avoid unnecessary providers at the root level. Only expose what's needed.

  5. Scoped Providers:

    • Use Provider.of or Consumer for specific widgets that need access to a provider.

    • Avoid using Provider.of at the root level unless it's truly global state.

  6. Provider Architecture:

    • Consider using a layered architecture(e.g., Clean Architecture)with providers.

    • Separate data sources (API, local storage) from business logic providers.

  7. Value vs. ChangeNotifier:

    • Use ChangeNotifierProvider for mutable state(e.g.,tasks, user preferences).

    • Use Provider.value for immutable data (e.g., constants, configuration).

  8. Dispose Resources:

    • Clean up resources(e.g.,close streams, cancel subscriptions) in your provider's dispose method.

    • Avoid memory leaks.

  9. Testing:

    • Write unit tests for your providers.

    • Use ProviderScope in your tests to isolate providers.

  10. Localisation and Themes:

    • Consider using providers for localisation (language) and themes (dark mode, light mode).

Using the Provider package for state management in Flutter offers a scalable solution for managing application state. By leveraging ChangeNotifierProvider and other provider classes, developers can efficiently handle state changes and ensure their apps remain maintainable and testable. Organising your provider setup with best practices, such as separating concerns and using nested providers, enhances the structure and readability of your code. Whether managing simple state or complex data models, Provider simplifies the process, making it an essential tool for any Flutter developer.

10
Subscribe to my newsletter

Read articles from Fenisha Koladiya directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Fenisha Koladiya
Fenisha Koladiya