Understanding the Differences Between Provider and Riverpod State Management in Flutter with Examples

Shivam Kumar Nayak
4 min readOct 25, 2023

--

flutter

Introduction:

Flutter, Google’s open-source UI software development toolkit, has gained immense popularity among developers for building cross-platform mobile applications. As your Flutter project grows, you’ll quickly realize the importance of efficient state management. Two prominent state management libraries in the Flutter ecosystem are Provider and Riverpod. In this article, we’ll explore these libraries, and their key differences, and provide practical examples to help you make an informed choice for your Flutter application.

What is State Management?

Before diving into the specifics of Provider and Riverpod, it’s essential to understand what state management is and why it’s crucial in Flutter development. State management refers to how you handle and share the data that represents your app’s current state. In a Flutter app, this could be anything from user authentication status to the data fetched from an API.

Provider: A Brief Overview

Provider is a straightforward, easy-to-use state management solution for Flutter applications. It offers a variety of providers that allow you to expose and consume data within your app. Some of the core provider types include:

  1. ChangeNotifierProvider: It is used to manage the state that needs to be updated frequently.
  2. StreamProvider: Ideal for handling real-time data, such as data streams from web sockets or Firestore.
  3. FutureProvider: Useful for data that is fetched asynchronously.
  4. ListenableProvider: Allows you to listen to any Listenable, including ValueNotifier.

How Provider Works

Provider works on the principle of “inversion of control.” It creates a tree of providers, and you can access the provided values anywhere in the widget tree. When a change occurs in the provider, the widgets listening to it will rebuild, ensuring that your UI remains up-to-date with the state.

Provider Example:

  1. First, add the necessary dependencies to your pubspec.yaml:
dependencies:
flutter:
sdk: flutter
provider: ^4.3.2

2. Create a new Flutter project, and then create a new Dart file named main.dart. Here's the code for the Provider-based counter app:

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

void main() {
runApp(MyApp());
}

class Counter with ChangeNotifier {
int _count = 0;

int get count => _count;

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

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => Counter(),
child: MaterialApp(
home: CounterApp(),
),
);
}
}

class CounterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counter = Provider.of<Counter>(context);

return Scaffold(
appBar: AppBar(title: Text('Provider Counter App')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Counter: ${counter.count}'),
ElevatedButton(
onPressed: () {
counter.increment();
},
child: Text('Increment'),
),
],
),
),
);
}
}

Explanation:

In this Provider example, we create a Counter class that extends ChangeNotifier. It has a count property, and the increment method updates the count and notifies listeners. We use the ChangeNotifierProvider to provide the Counter to the widget tree. The CounterApp widget then consumes this data using Provider.of.

Riverpod: A Deeper Dive

Riverpod is built on top of Provider and takes state management to a more advanced level. It addresses some of the limitations of Provider and offers enhanced flexibility and scalability. Key features of Riverpod include:

  1. No Global Singletons: In Provider, you might end up creating a global singleton for a provider. Riverpod encourages you to create a scope for your providers, reducing the risk of naming conflicts and ensuring a cleaner codebase.
  2. Immutable Data: Riverpod promotes immutability, making it easier to manage state changes without unexpected side effects.
  3. Advanced Dependency Injection: Riverpod excels in managing complex dependencies, and you can easily provide different instances of the same object within different scopes.

How Riverpod Works

Riverpod introduces the concept of “providers” and “containers.” Providers represent the data, and containers are used to read, watch, and modify the state. This separation allows you to work with immutable data and prevents accidental state changes.

Riverpod Example:

  1. Add the necessary dependencies to your pubspec.yaml:
dependencies:
flutter:
sdk: flutter
riverpod: ^0.14.0

2. Create a new Dart file named main_riverpod.dart. Here's the code for the Riverpod-based counter app:

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

void main() {
runApp(MyApp());
}

final counterProvider = StateNotifierProvider<Counter, int>((ref) {
return Counter();
});

class Counter {
Counter([int count = 0]) : _count = count;

int _count;

int get count => _count;

void increment() {
_count++;
}
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ProviderScope(
child: MaterialApp(
home: CounterApp(),
),
);
}
}

class CounterApp extends ConsumerWidget {
@override
Widget build(BuildContext context, ScopedReader watch) {
final count = watch(counterProvider);

return Scaffold(
appBar: AppBar(title: Text('Riverpod Counter App')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Counter: $count'),
ElevatedButton(
onPressed: () {
context.read(counterProvider.notifier).increment();
},
child: Text('Increment'),
),
],
),
),
);
}
}

Explanation:

In the Riverpod example, we define a counterProvider using StateNotifierProvider. We create a Counter class that holds the count state, and the increment method is used to modify the count. The ProviderScope widget is used to set up the scope for providers. The CounterApp widget consumes the state using watch and modifies it using context.read.

Key Differences Between Provider and Riverpod:

  1. Scoping: Riverpod enforces scoping by design, which encourages better organization of your providers. In Provider, you can easily create global providers, leading to potential naming conflicts.
  2. Immutable Data: Riverpod emphasizes immutability, reducing unexpected state changes, which can be a concern with Provider.
  3. Advanced Dependency Injection: Riverpod excels in managing complex dependencies, whereas Provider is more straightforward for basic use cases.

Conclusion:

Both Provider and Riverpod are powerful state management solutions for Flutter, and the choice between them depends on your project’s complexity and your personal preferences. Provider is more straightforward and may be suitable for smaller projects, while Riverpod offers advanced features and encourages a more organized and immutable codebase. Understanding the differences and choosing the right library can significantly impact the maintainability and scalability of your Flutter app.

--

--

Shivam Kumar Nayak
Shivam Kumar Nayak

Written by Shivam Kumar Nayak

Passionate software developer crafting elegant solutions to empower users and drive technological innovation.

Responses (2)