skip to Main Content

I would like to change the app theme according to switch button. I am using ChangeNotifier to do that.

class ThemesProvider extends ChangeNotifier {
  ThemeMode themeMode = ThemeMode.system;
  bool get isDarkMode => themeMode == ThemeMode.dark;
  void changeTheme(bool isOn) {
    themeMode = isOn ? ThemeMode.dark : ThemeMode.light;
    notifyListeners();
  }
}

that’s the class that I am using to change the theme.

final themesProvider = ChangeNotifierProvider((_) => ThemesProvider());

class MyApp extends ConsumerWidget {
  const MyApp({
    super.key,
  });
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final themeModeState = ref.watch(themesProvider).themeMode;
    return MaterialApp(
      theme: Themes.lightTheme,
      darkTheme: Themes.darkTheme,
      themeMode: themeModeState,
      debugShowCheckedModeBanner: false,
      home: const SignInScreen(),
    );
  }
}

That is my main file.

final themesProvider = ChangeNotifierProvider((_) => ThemesProvider());

class MainScreen extends ConsumerWidget {
  const MainScreen({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final themeModeState = ref.watch(themesProvider);
    return Scaffold(
      appBar: AppBar(
        title: Text("Try Me"),
        leading: Consumer(
          builder: (context, ref, child) {
            return Switch(
                value: themeModeState.isDarkMode,
                onChanged: (value) {
                  themeModeState.changeTheme(value);
                });
          },
        ),
      ),
    );
  }
}

And this is where I try to change the theme.

I/flutter ( 4525): ThemeMode.dark
I/flutter ( 4525): ThemeMode.light
I/flutter ( 4525): ThemeMode.dark

It seems the function is working fine. I don’t know where I made a mistake. Can one of you please help me to fix that.

2

Answers


  1. This short example works as expected:

    void main() => runApp(const ProviderScope(child: MyApp()));
    
    class ThemesProvider extends ChangeNotifier {
      ThemeMode themeMode = ThemeMode.system;
      bool get isDarkMode => themeMode == ThemeMode.dark;
      void changeTheme(bool isOn) {
        themeMode = isOn ? ThemeMode.dark : ThemeMode.light;
        notifyListeners();
      }
    }
    
    final themesProvider = ChangeNotifierProvider((_) => ThemesProvider());
    
    class MyApp extends ConsumerWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        final themeModeState = ref.watch(themesProvider).themeMode;
    
        return MaterialApp(
          theme: ThemeData.light(),
          darkTheme: ThemeData.dark(),
          themeMode: themeModeState,
          debugShowCheckedModeBanner: false,
          home: const MainScreen(),
        );
      }
    }
    
    class MainScreen extends ConsumerWidget {
      const MainScreen({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        final themeModeState = ref.watch(themesProvider);
        return Scaffold(
          appBar: AppBar(
            title: Text("Try Me, current: ${themeModeState.themeMode}"),
            leading: Switch(
              value: themeModeState.isDarkMode,
              onChanged: (value) {
                themeModeState.changeTheme(value);
              },
            ),
          ),
        );
      }
    }
    

    The problem is probably here:

    ...
    theme: Themes.lightTheme,
    darkTheme: Themes.darkTheme,
    ...
    

    Or maybe you declare two different instances of ThemesProvider() and this has some effect (I think not)

    Login or Signup to reply.
  2. For ChangeNotifier and StateNotifier, you should call functions on notifier. e.g.

    ref.read(yourProvider.notifier).function()
    

    So onChanged function in Switch widget have to changed to following:

    onChanged: (value) {
      ref.read(themesProvider.notifier).changeTheme(value);
    });
    

    Remark: You should use read instead of watch inside onPressed, onChanged and other functions.

    It is usually not recommended to use ChangeNotifier, but instead use StateNotifier, which promotes immutable architecture and makes code more maintainable.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search