In this article, you can explore the fundamentals of using Provider in Flutter to manage the state efficiently. This guide covers essential concepts, from creating and defining providers to accessing and updating data within your Flutter applications. It is ideal for developers looking to simplify state management in their basic Flutter projects.
Create Provider
Data changes over time.
- Use with ChangeNotifier;
- Call notifyListeners() once the data changes to get updates on the UI;
class ProductProvider with ChangeNotifier { final Future<List<Product>> _product; final List<String> _someData; ProductProvider() { } void doSomeLogic() { ... _someData = newData; notifyListeners(); } void doSomeLogicAgain() { ... _product = newProduct; notifyListeners(); }
Provider data doesn’t change over time.
class ProductProvider { final Future<List<Product>> _product; final List<String> _someData; ProductProvider() { } void doSomeLogic() { ... _someData = newData; } void doSomeLogicAgain() { ... _product = newProduct; } }
Define Providers in the Application
- Use MultiProvider to define all your Provider classes
- Provider sends updates to the UI => use ChangeNotifierProvider
- Provider has immutable data and doesn’t update => Provider
class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) => MultiProvider( providers: [ Provider<ProductRepository>( create: (BuildContext context) => ProductRepository(), ), ChangeNotifierProvider<ProductProvider>( create: (BuildContext context) => ProductProvider(), ) ], child: Theme.of(context).platform == TargetPlatform.android ? const MaterialDesignApp() : const CupertinoDesignApp(), ); }
Access Data from the Provider
Access data, one-time reads, method calls
- Provider.of<YorProvider>(context, listen: false).doSomething();
- context.read<YourProvider>().yourData;
floatingActionButton: FloatingActionButton( onPressed: () => context.read<Counter>().increment(), child: Icon(Icons.add), ),
floatingActionButton: FloatingActionButton( onPressed: () => Provider.of<Counter>(context, listen:false).increment(), child: Icon(Icons.add), ),
Listening for the updates from Provider, data changes over time
API calls:
- Provider.of<YorProvider>(context, listen: true).yourData;
- context.watch<YourProvider>().yourData;
- context.select<T, R>(R cb(T value));
Widgets:
- Consumer
- Selector
class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Text('Count: ${context.watch<Counter>().count}'); } }
class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Text('Count: ${Provider.of<Counter>(context, listen: true).count}'); } }
class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Consumer<Counter>( builder: (context, counter, child) { return Text('Count: ${counter.count}'); }, ); } }
class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Selector<MyProvider, int>( selector: (BuildContext context, MyProvider myProvider) => myProvider.count builder: (context, counter, child) { return Text('Count: $count'); }, ); } } class MyProvider with ChangeNotifier { final int count; final String dataString; final List<Product> product; final String moreData; }
class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { final count = context.select((MyProvider p) => p.count); return Text('Count: $count'); } } class MyProvider with ChangeNotifier { final int count; final String dataString; final List<Product> product; final String moreData; MyProvider(this.count, this.dataString, this.product, this.moreData); }
Best Practices
- Use Multiple Providers for Complex State. For complex apps, split your state management into multiple smaller providers rather than having a single, monolithic provider. This improves maintainability and performance.
- Avoid Unnecessary Rebuilds. Use the select method or Selector widget to listen to specific parts of the model that need to be rebuilt, avoiding unnecessary widget rebuilds.
- Use Read and Watch Appropriately. Use context.read<T>() to access a provider’s instance without listening to changes. Use context.watch<T>() when you need to rebuild your widget when the provider data changes.
- Dispose of Providers Correctly. When using Provider in a widget that needs to be disposed of, ensure you properly clean up resources by using the dispose method.
Discover more from FutureWare
Subscribe to get the latest posts sent to your email.