Flutter simple state management with dart provider
This is a reimplementation of the default Flutter application using provider for state management. The default Flutter application is a screen with a static text that says “You have pushed the button this many times.”, a number right below this text, and a floating button at the bottom right corner. When the button is pressed, the number will increase by 1 each time.
1. A Counter class to hold the number.
/// 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)); } }
2. A stateless widget for displaying the number. This is the number that increases by 1 when the button is pressed. It accesses the state variable count by context.watch
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().count}', key: const Key('counterState'), style: Theme.of(context).textTheme.headline4); } }
3. A stateless widget with all the UIs. In this example, they are the text that says “You have pushed the button this many times.”, a number right below this text, and a floating button at the bottom right corner. The number that increases by 1 when the button is pressed, is the stateless widget Counter defined above. It accesses the increment function from Counter by context.read
class MyHomePage extends StatelessWidget { const MyHomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Example'), ), body: Center( child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: const[ 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 ().increment(), tooltip: 'Increment', child: const Icon(Icons.add), ), ); } }
4. A stateless widget that is the root widget for the entire app. It is a MaterialApp containing the stateless widget MyHomePage defined above.
class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return const MaterialApp( home: MyHomePage(), ); } }
5. Lastly, the main function, the entry point of the app. The Counter
is provided at the root widget, so the child widgets can get and interact with Counter with context.watch
and context.read
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: (_) => Counter()), ], child: const MyApp(), ), ); }
6. All together in the main.dart
// ignore_for_file: public_member_api_docs, lines_longer_than_80_chars 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: (_) => 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 const MaterialApp( home: MyHomePage(), ); } } class MyHomePage extends StatelessWidget { const MyHomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Example'), ), body: Center( child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: const[ 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 ().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 ().count}', key: const Key('counterState'), style: Theme.of(context).textTheme.headline4); } }
7. Don’t forget to include the provider library in the pubspec.yaml
dependencies: flutter: sdk: flutter provider: ^6.0.0
Search within Codexpedia
Search the entire web