βMore on State Management
State management is crucial for building dynamic, efficient, and bug-free apps. Let's delve into three popular state management techniques: Provider, Riverpod, and Bloc, using the provided examples.
1. Provider π
Provider is a simple yet powerful state management technique. Let's dissect the provided example:
// This code is distributed under the MIT License.
// Copyright (c) 2019 Remi Rousselet.
// You can find the original at https://github.com/rrousselGit/provider.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// This is a reimplementation of the default Flutter application
// using provider + [ChangeNotifier].
void main() {
runApp(
// Providers are above [MyApp] instead of inside it, so that
// tests can use [MyApp] while mocking the providers
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => Counter()),
],
child: const MyApp(),
),
);
}
// Mix-in [DiagnosticableTreeMixin] to have access to
// [debugFillProperties] for the devtool ignore: prefer_mixin
class Counter with ChangeNotifier, DiagnosticableTreeMixin {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
// Makes `Counter` readable inside the devtools by listing all
// of its properties.
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(IntProperty('count', count));
}
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
colorSchemeSeed: Colors.blue,
useMaterial3: true,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Provider example'),
),
body: const Center(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
// Extracted as a separate widget for performance
// optimization. As a separate widget, it will rebuild
// independently from [MyHomePage].
//
// This is totally optional (and rarely needed).
// Similarly, we could also use [Consumer] or
// [Selector].
Count(),
],
),
),
floatingActionButton: FloatingActionButton(
key: const Key('increment_floatingActionButton'),
// Calls `context.read` instead of `context.watch` so
// that it does not rebuild when [Counter] changes.
onPressed: () => context.read<Counter>().increment(),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
class Count extends StatelessWidget {
const Count({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text(
// Calls `context.watch` to make [Count] rebuild when
// [Counter] changes.
'${context.watch<Counter>().count}',
key: const Key('counterState'),
style: Theme.of(context).textTheme.headlineMedium,
);
}
}Explanation:
Model (
Counterclass): This class holds the state (_count) and has a method to update the state (increment). When the state changes,notifyListenersis called to notify the UI to rebuild.Provider Setup (
MultiProvider):MultiProvideris used to provideCounterto the widget tree.Accessing and Updating State:
context.read<Counter>().increment()is used to call theincrementmethod and update the state.context.watch<Counter>().countis used to access the current state and rebuild the UI when it changes.
2. Riverpod π
Riverpod is a flexible and powerful state management solution. Let's analyze the provided example:
Explanation:
Provider Declaration:
counterProvideris declared globally, specifying how to create the state.Provider Scope:
ProviderScopeis used to enable Riverpod for the project.Accessing and Updating State:
ref.read(counterProvider.notifier).state++is used to update the state. AConsumerwidget is used to rebuild the UI when the state changes.
3. Bloc π©
Bloc separates business logic from UI, making the codebase more organized and testable. Let's dissect the provided example:
Explanation:
Bloc Declaration:
CounterBlocis defined to manage the state of a counter. It defines how to handleIncrementandDecrementevents to update the state.Provider Setup:
BlocProvideris used to provideCounterBlocto the widget tree.Accessing and Updating State:
context.read<CounterBloc>().add(Increment())is used to dispatch anIncrementevent to theCounterBloc, which in turn updates the state.
These breakdowns will help you understand how each part of the code contributes to state management in the respective examples. Each technique has its own set of advantages and suits different use cases.
By understanding and practicing these techniques, you'll be well on your way to mastering state management in Flutter.
Last updated
Was this helpful?