How to use a switch to change between light and dark theme with Provider in Flutter?

Viewed 67

I'm a flutter beginner and recently I've been trying to figure out how to dynamically switch between dark and light themes using a switch. I've watched several tutorials, then tried to figure it out on my own. After a few hours I got to this point, where I'm seeing no errors and where I think the code is doing the right things. Essentially the problem is that the switch turns off, but then turns back on, with no change in theme. I suspect I wrote some wrong code somewhere, but I'm not sure where. Here is the code:

This is my main.dart file:

import 'package:provider/provider.dart';
import 'package:working_with_fin_apis/providers/theme_provider.dart';
import 'package:working_with_fin_apis/screens/home_page.dart';
import 'package:working_with_fin_apis/screens/settings_page.dart';

void main() {
  runApp(
    MultiProvider(providers: [
      ChangeNotifierProvider(
        create: (_) => ThemeProvider(),
      ),
    ], child: const MyApp()),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        debugShowCheckedModeBanner: false,
        themeMode: Provider.of<ThemeProvider>(context).themeMode,
        initialRoute: '/',
        routes: {
          '/': (context) => const HomePage(),
          '/settings': (context) => const SettingsPage(),
        });
  }
}

This is my settings.dart, where the switch is:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:working_with_fin_apis/providers/theme_provider.dart';

class SettingsPage extends StatelessWidget {
  const SettingsPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(
          'PlaceHolder',
          style: TextStyle(),
        ),
        actions: [
          IconButton(
            onPressed: () {
              Navigator.pushNamed(context, '/');
            },
            icon: const Icon(Icons.arrow_back),
          ),
        ],
      ),
      body: Center(
        child: Column(
          children: [
            Switch(
              value: true,
              onChanged: Provider.of<ThemeProvider>(context).toggleTheme(true),
            ),
          ],
        ),
      ),
    );
  }
}

And finally my theme_provider.dart that has the actual ChangeNotifier:

import 'package:flutter/material.dart';

class ThemeProvider with ChangeNotifier {
  ThemeMode _theme = ThemeMode.light;

  ThemeMode get themeMode => _theme;

  dynamic toggleTheme(bool isDark) {
    _theme = isDark ? ThemeMode.dark : ThemeMode.light;
    notifyListeners();
  }
}

So my question is what am I missing? I think it's just a wrong way of notifying each of the different parts, but I'm not sure. I'm trying to find the simplest solution but I can't seem to figure it out, most of the code is inspired from provider tutorials that weren't related to theming, so that might be some of the problem. Any advice or resources are welcome, thanks.

1 Answers

You have to declare a bool variable in the provider for the switch to work correctly

bool _isDark = false;
bool get isDark => _isDark;

And change its value in the function with the new value

void toggleTheme(bool newValue) {
_isDark = newValue;
if (_theme == ThemeMode.lightTheme) {
  _theme = ThemeMode.darkTheme;
} else {
  _theme = ThemeMode.lightTheme;
}
notifyListeners();
}

And in the switch widget use this bool variable and receive the value in onChanged and pass it to the provider function and set listen to false in the provider call for the function

Switch(
       value: Provider.of<ThemeProvider>(context).isDark,
       onChanged:(value) => Provider.of<ThemeProvider>(context,listen: false).toggleTheme(value),
      ),
Related