Summary: in this tutorial, you’ll will learn about flutter Provider state management to share the state across widgets.
Provider allows you to do the following:
- Share state (shared data) across widgets.
- Notify widgets when the state changes so the app can rebuild the widgets automatically.
- Separate the logic from widgets.
We’ll create some apps to illustrates the Provider state management.
Counter app #
We’ll illustrate the Provider
using a simple counter app.
Step 1. Create a new flutter project with the following directory structure within the lib
directory:
├── counter_app.dart
├── main.dart
└── src
└── counter_provider.dart
Code language: plaintext (plaintext)
Step 2. Add the Provider
package to pubspec.yaml
file:
dependencies:
flutter:
sdk: flutter
provider: ^6.1.4
Code language: Dart (dart)
Then run:
flutter pub get
Code language: Dart (dart)
Step 3. Create a class that extends to the ChangeNotifier
class:
import 'package:flutter/foundation.dart';
class CounterProvider with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // Notifies widgets to rebuild
}
}
Code language: Dart (dart)
In this example:
ChangeNotifier
is a class that notifies listeners when something changes.notifyListeners()
triggers UI rebuilds.
Step 4. Provide the data (CounterProvider
) to your class:
import 'package:counter_provider/counter_app.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './src/counter_provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => CounterProvider(),
child: CounterApp(),
),
);
}
Code language: Dart (dart)
Step 5. Use the Provider
in your widgets:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './src/counter_provider.dart';
class CounterApp extends StatelessWidget {
const CounterApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Consumer<CounterProvider>(
builder:
(context, counter, child) => Scaffold(
appBar: AppBar(title: Text('Counter Provider Example')),
body: Center(
child: Text('${counter.count}', style: TextStyle(fontSize: 24)),
),
floatingActionButton: FloatingActionButton(
onPressed: counter.increment,
child: Icon(Icons.add),
),
),
),
);
}
}
Code language: Dart (dart)
In this example:
- First, wrap the
Scaffold
widget with theConsumer<CounterProvider>
widget. - Second, return the
Scaffold
widget in thebuilder
callback. The second argument (counter
) is an instance of theCounterProvider
. - Third, use the
counter.count
in theText
widget and call thecounter.increment
method in theonPressed
callback.
Theme Switcher App #

Here’s are the main features of the app:
- Automatically use the system theme: When the app starts, it detects current system theme (light or dark). If the system changes its theme such as auto-switches at sunset, the app updates in real-time.
- Manually toggle between light and dark modes: You can toggle the theme on the app bar to manuallly choose between the light and dark mode. It will updates instantly across the widgets.
- Persist user preferences: If you select a manual theme, the app will save it locally using shared preferences.
- Reset to following system theme: You can reset the theme to follow the current system’s theme.
We’ll use two third-party packages:
Provider
– to manage theme across the pages.SharedPreferences
– to save the user selection in the shared preferences.
Step 1. Create a new flutter project with the following directory and file structure:
├── main.dart
└── src
├── home_screen.dart
└── theme_provider.dart
Code language: Dart (dart)
Step 2. Add the Provider
and shared_preferences
packages to pubspec.yaml
file:
dependencies:
flutter:
sdk: flutter
provider: ^6.1.4
shared_preferences: ^2.5.3
Code language: Dart (dart)
Then run:
flutter pub get
Code language: Dart (dart)
Step 3. Create a ThemeProvider
class that extends the ChangeNotifier
class:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ThemeProvider with ChangeNotifier {
static const String _prefKey = 'theme_mode';
ThemeMode _themeMode = ThemeMode.system;
ThemeProvider() {
_loadThemeFromPrefs();
WidgetsBinding.instance.platformDispatcher.onPlatformBrightnessChanged =
_handleSystemBrightnessChange;
}
ThemeMode get themeMode => _themeMode;
bool get isDarkMode {
if (_themeMode == ThemeMode.system) {
return WidgetsBinding.instance.platformDispatcher.platformBrightness ==
Brightness.dark;
}
return _themeMode == ThemeMode.dark;
}
Future<void> toggleTheme(bool isDark) async {
_themeMode = isDark ? ThemeMode.dark : ThemeMode.light;
notifyListeners();
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_prefKey, _themeMode.name);
}
Future<void> setSystemTheme() async {
_themeMode = ThemeMode.system;
notifyListeners();
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_prefKey, ThemeMode.system.name);
}
Future<void> _loadThemeFromPrefs() async {
final prefs = await SharedPreferences.getInstance();
final saved = prefs.getString(_prefKey);
switch (saved) {
case 'light':
_themeMode = ThemeMode.light;
break;
case 'dark':
_themeMode = ThemeMode.dark;
break;
default:
_themeMode = ThemeMode.system;
}
notifyListeners();
}
void _handleSystemBrightnessChange() {
if (_themeMode == ThemeMode.system) {
notifyListeners();
}
}
}
Code language: Dart (dart)
Step 4. Create the App and use the ThemeProvider:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './src/theme_provider.dart';
import './src/home_screen.dart';
void main() {
// for desktop
WidgetsFlutterBinding.ensureInitialized();
runApp(
ChangeNotifierProvider(create: (_) => ThemeProvider(), child: MyApp()),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
final themeProvider = Provider.of<ThemeProvider>(context);
return AnimatedTheme(
duration: Duration(milliseconds: 300),
data: themeProvider.isDarkMode ? ThemeData.dark() : ThemeData.light(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Theme Switcher',
themeMode: themeProvider.themeMode,
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
home: HomeScreen(),
),
);
}
}
Code language: Dart (dart)
Step 5. Create a HomeScreen:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'theme_provider.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
final themeProvider = Provider.of<ThemeProvider>(context);
final isDark = themeProvider.isDarkMode;
return Scaffold(
appBar: AppBar(
title: Text('Theme Switcher'),
actions: [
IconButton(
icon: Icon(isDark ? Icons.wb_sunny : Icons.nightlight_round),
onPressed: () => themeProvider.toggleTheme(!isDark),
),
],
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Current Theme: ${isDark ? "Dark" : "Light"}',
style: TextStyle(fontSize: 18),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
themeProvider.setSystemTheme();
},
child: Text("Reset to System Theme"),
),
],
),
),
);
}
}
Code language: Dart (dart)