skip to Main Content

I’m trying to make an option on my app to allow the user to choose between a light theme, a dark theme or just leave the theme to match the one on their phone’s preferences, that is, if they have a dark appearance and the user selects the option it will match the phone’s appearance.

What I have right now is:

  1. My Provider which looks like this:
class ThemeChanger with ChangeNotifier {

  ThemeData _themeData;

  ThemeChanger(this._themeData);

  getTheme() => _themeData;

  setTheme( ThemeData theme) {
    this._themeData = theme;
    notifyListeners();
  }

}
  1. My home.dart, where I have the basics to work with the view and looks like this
class HomePage extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Scaffold( 
      appBar: AppBar( 
        title: const Text('AppBar'),
      ),
      body: const ButtonsList(),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.ads_click),
        onPressed: (){}),
    );
  }
}


class ButtonsList extends StatelessWidget {
  const ButtonsList({super.key});

  @override
  Widget build(BuildContext context) {

    final theme = Provider.of<ThemeChanger>(context);

    return Center(
      child: Row( 
        mainAxisAlignment: MainAxisAlignment.center,
        children: [ 
          FloatingActionButton(
            child: Icon(Icons.sunny),
            onPressed: () => theme.setTheme(ThemeData.light())
          ),
          SizedBox(width: 20,),
          FloatingActionButton(
            child: Icon(Icons.dark_mode),
            onPressed: () => theme.setTheme(ThemeData.dark())
          ),
          SizedBox(width: 20,),
          FloatingActionButton(
            child: Icon(Icons.settings),
            onPressed: (){} //should pick the system theme
          ),
        ],
      ),
    );
  }
}
  1. And finally my main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tema_provider/blocs/theme.dart';
import 'package:tema_provider/pages/home.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: ( _ ) => ThemeChanger( ThemeData.light() ),
      child: MaterialAppWithTheme(),
    );
  }
}

class MaterialAppWithTheme extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    final theme = Provider.of<ThemeChanger>(context);

    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: theme.getTheme(),
      home: HomePage()
    );
  }
}

One of the problems I have is that I know the system’s theme can be access with the themeMode.system but in my code I’m working with ThemeData, I’ve tried changing the code on my 1st block of code and work with ThemeMode instead because it also has dark and light modes but it didn’t work.

I don’t know if what I’m trying to do is possible, all I’ve found so far is switch between dark/light mode which the app does with the code as it is.

Any help/indication/link to a resource is highly appreciated 🙂

2

Answers


  1. What I understand from your question is you want to access system theme but you are using theme data.
    Here is the solution I’m using for my application.

    var brightness = SchedulerBinding.instance.platformDispatcher.platformBrightness;
       bool isDarkMode = brightness == Brightness.dark;
    

    Now to listen live theme changes in app from OS you can implement below things.

    void listenForSystemThemeChanges(WidgetRef ref, BuildContext context) {
        var window = View.of(context).platformDispatcher;
        window.onPlatformBrightnessChanged = () {
          WidgetsBinding.instance.handlePlatformBrightnessChanged();
            // This callback is called every time the system theme changes.
            var brightness = window.platformBrightness;
            bool isDarkMode = brightness == Brightness.dark;
        };
      }
    

    Let me know if you are still not able to figure out the issue.

    Login or Signup to reply.
  2. here, your approach is wrong.

    MaterialApp has theme and darkTheme property, and flutter uses theme when themeMode is ThemeMode.light and use darkTheme when themeMode is ThemeMode.dark.

    So that, it will be standard if you change the themeMode for changing the theme of Futter App.

    I found this on a podcast of Majid Hajian, told by Rydmike
    author of flex_color_scheme package.

    when themeMode is ThemeMode.system then the Flutter app’s theme will change (listening) on OS theme mode auto.

    here is how I do this,

    I used Riverpod for state management and Hive for local storage

    final themeProvider = ChangeNotifierProvider<ThemeController>((ref) {
      final database = ref.watch(databaseService);
    
      return ThemeController(database);
    });
    
    class ThemeController with ChangeNotifier {
      ThemeController(this._database);
    
      late final DatabaseService _database;
    
      String get theme => _database.savedTheme;
    
      void toggle(bool mode) {
        (mode)
            ? _database.toggleSaveTheme("dark")
            : _database.toggleSaveTheme("light");
    
        notifyListeners();
      }
    }
    
    
    
    //** DATABASE CLASS */
    final databaseService = Provider<DatabaseService>((ref) {
      return DatabaseService(ref);
    });
    
    class DatabaseService {
      final Ref ref;
    
      DatabaseService(this.ref) {
        initTheme();
      }
    
      get savedTheme => ref.watch(hiveProvider).get(AppStrings.theme);
    
      void initTheme() {
        ref.watch(hiveProvider).put(AppStrings.theme, 'light');
      }
    
      void toggleSaveTheme(String mode) =>
          ref.watch(hiveProvider).put(AppStrings.theme, mode);
    }
    
    /// My APP CLASS
    
    class MyApp extends HookConsumerWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        final appTheme = ref.watch(themeProvider);
    
        return MaterialApp.router(
          title: AppStrings.appName,
          debugShowCheckedModeBanner: false,      
          //:theme
          themeMode: appTheme.theme.isEmpty
              ? ThemeMode.system
              : appTheme.theme == "dark"
                  ? ThemeMode.dark
                  : ThemeMode.light,
          
          theme: AppTheme().lightTheme,
          darkTheme: AppTheme().darkTheme,
        );
      }
    }
    

    If you have any confusion on the implementation feel free to ask please.

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