Asver Seb
Asver Seb

Reputation: 65

How app theme mode can be saved in flutter according to user

I have added dark and light mode in my app but I am unable to save the mode selected. I am new so that's why I'm unable to understand what to do. Here's my code of dark and light mode button though which upon tapping it changes the mode.

ListTile(
                  onTap: () {
                    MyApp.themeNotifier.value =
                        MyApp.themeNotifier.value == ThemeMode.light
                            ? ThemeMode.dark
                            : ThemeMode.light;
                  },
                  leading: Icon(MyApp.themeNotifier.value == ThemeMode.light
                      ? Icons.dark_mode
                      : Icons.light_mode),
                  title: MyApp.themeNotifier.value == ThemeMode.light
                      ? Text(
                          "Dark Mode",
                          style: TextStyle(
                            fontFamily: "Lato",
                          ),
                        )
                      : Text(
                          "Light Mode",
                          style: TextStyle(
                            fontFamily: "Lato",
                          ),
                        ),
                ),

I want the mode to get selected. I know shared preferences and some other local databases can be used but how can anyone please tell by code snippet etc which I can implement please, Thanks

Upvotes: 2

Views: 1468

Answers (2)

Ruble
Ruble

Reputation: 4824

Here's how it's possible to do this using Riverpod state management and Shared preferences database.

Required imports:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';

Creating a provider SharedPreferences:

// provider stores the instance SharedPreferences
final sharedPreferencesProvider = Provider<SharedPreferences>((_) {
  return throw UnimplementedError();
});

Creating a ChangeNotifierProvider returning AppTheme.

// provider to work with AppTheme
final appThemeProvider = ChangeNotifierProvider((ref) {
  return AppTheme(ref.watch(sharedPreferencesProvider));
});

class AppTheme extends ChangeNotifier {
  AppTheme(this._prefs);

  final SharedPreferences _prefs;

  /// Get the current value from SharedPreferences.
  bool getTheme() => _prefs.getBool('isDarkMode') ?? false;

  /// Store the current value in SharedPreferences.
  void setTheme(bool isDarkMode) {
    _prefs.setBool('isDarkMode', isDarkMode);

    notifyListeners();
  }
}

Note that main method is asynchronous:

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // initialization SharedPreferences
  final SharedPreferences prefs = await SharedPreferences.getInstance();

  return runApp(ProviderScope(
    // override SharedPreferences provider with correct value
    overrides: [
      sharedPreferencesProvider.overrideWithValue(prefs),
    ],
    child: MyApp(),
  ));
}

Next:

class MyApp extends ConsumerWidget {
  // using const we reduce unnecessary rebuild widget
  final Widget _myHomePage = const MyHomePage();

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    print(MyApp);

    // ref.watch allows you to monitor changes and rebuild our widget if necessary
    final bool isDarkMode = ref.watch(appThemeProvider).getTheme();

    return MaterialApp(
      title: 'Flutter demo',
      theme: ThemeData.light(),
      darkTheme: ThemeData.dark(),
      themeMode: isDarkMode ? ThemeMode.dark : ThemeMode.light,
      home: _myHomePage,
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage();

  @override
  Widget build(BuildContext context) {
    print(MyHomePage);

    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Theme Riverpod Demo'),
      ),
      body: YourListTile(),
    );
  }
}

Your widget:

class YourListTile extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    print(YourListTile);

    final bool isDarkMode = ref.watch(appThemeProvider).getTheme();

    return SwitchListTile(
      value: isDarkMode,
      onChanged: (_) {
        // the use of ref.read is necessary in callbacks
        ref.read(appThemeProvider.notifier).setTheme(!isDarkMode);
      },
      secondary: isDarkMode
          ? const Icon(Icons.dark_mode)
          : const Icon(Icons.light_mode),
      title: isDarkMode
          ? const Text(
              "Dark Mode",
              style: TextStyle(
                fontFamily: "Lato",
              ),
            )
          : const Text(
              "Light Mode",
              style: TextStyle(
                fontFamily: "Lato",
              ),
            ),
    );
  }
}

Learn more about packages here:

Upvotes: 7

Paul
Paul

Reputation: 472

I wrote an article about this in particular. Parts of it are out of date and it covers more than your question, but the short answer is: save it locally and include an option to use the system choice if you can (Light/Dark/System).

I recommend using the shared preferences package. It's a flutter favorite and kept up to date.

To save the user choice, have an async method listen for the user switching the theme and update the preference.

Future<void> saveUserTheme(String chosenTheme) async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  await prefs.setString('UserTheme', chosenTheme);
}

On startup, you can load the choice before starting the app asynchronously:

Future<String> getTheme() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  return prefs.getString('UserTheme') ?? 'default;
}

Upvotes: 2

Related Questions