Flutter Provider

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.dartCode language: plaintext (plaintext)

Step 2. Add the Provider package to pubspec.yamlfile:

dependencies:
  flutter:
    sdk: flutter
  provider: ^6.1.4Code language: Dart (dart)

Then run:

flutter pub getCode 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 the Consumer<CounterProvider> widget.
  • Second, return the Scaffold widget in the builder callback. The second argument (counter) is an instance of the CounterProvider.
  • Third, use the counter.count in the Text widget and call the counter.increment method in the onPressed callback.

Theme Switcher App #

flutter dark mode

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.dartCode language: Dart (dart)

Step 2. Add the Provider and shared_preferencespackages to pubspec.yamlfile:

dependencies:
  flutter:
    sdk: flutter
  provider: ^6.1.4
  shared_preferences: ^2.5.3Code language: Dart (dart)

Then run:

flutter pub getCode 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)

Was this tutorial helpful ?