Flutter how to get brightness without MediaQuery?

Issue

My goal is to create an app where the user can choose his preferred theme.
I’m saving the user’s choice with shared preferences so I can load it the next app start.
The user can either select:
– Dark Mode (Independent from the OS Settings)
– Light Mode (Independent from the OS Settings)
– System (Changes between Dark Mode and Light mode depending on the OS settings)
With the help of BLoC, I almost achieved what I want. But the problem is that I need to pass the brightness inside my Bloc event. And to get the system (OS) brightness I need to make use of

MediaQuery.of(context).platformBrightness

But the Bloc gets initiated before MaterialApp so that MediaQuery is unavailable. Sure I can pass the brightness later(from a child widget of MaterialApp) but then (for example, if the user has dark mode activated) it goes from light to dark but visible for a really short time for the user(Because inside the InitialState I passed in light mode).

class MyApp extends StatelessWidget {
  final RecipeRepository recipeRepository;

  MyApp({Key key, @required this.recipeRepository})
      : assert(recipeRepository != null),
        super(key: key);

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider<ThemeBloc>(create: (context) =>
        ThemeBloc(),),

      ],
      child: BlocBuilder<ThemeBloc, ThemeState>(
        builder: (context, state){

          return MaterialApp(
            theme: state.themeData,
            title: 'Flutter Weather',
            localizationsDelegates: [
              FlutterI18nDelegate(fallbackFile: 'en',),
              GlobalMaterialLocalizations.delegate,
              GlobalWidgetsLocalizations.delegate
            ],
            supportedLocales: [
              const Locale("en"),
              const Locale("de"),
            ],
            home: Home(recipeRepository: recipeRepository),
          );
        },
      ),
    );
  }
}

ThemeBloc:

class ThemeBloc extends Bloc<ThemeEvent, ThemeState> {
  @override
  ThemeState get initialState =>
      ThemeState(themeData: appThemeData[AppTheme.Bright]);

  @override
  Stream<ThemeState> mapEventToState(
    ThemeEvent event,
  ) async* {
    if (event is LoadLastTheme) {
      ThemeData themeData = await _loadLastTheme(event.brightness);
      yield ThemeState(themeData: themeData);
    }
    if (event is ThemeChanged) {
      await _saveAppTheme(event.theme);
      yield ThemeState(themeData: appThemeData[event.theme]);
    }
  }

  Future<ThemeData> _loadLastTheme(Brightness brightness) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    String themeString = prefs.getString(SharedPrefKeys.appThemeKey);
    print("saved theme: $themeString");
    if ((prefs.getString(SharedPrefKeys.appThemeKey) != null) &&
        themeString != "AppTheme.System") {
      switch (themeString) {
        case "AppTheme.Bright":
          {
            return appThemeData[AppTheme.Bright];
          }
          break;

        ///Selected dark mode
        case "AppTheme.Dark":
          {
            return appThemeData[AppTheme.Dark];
          }
          break;
      }
    }

    print("brightness: $brightness");
    if (brightness == Brightness.dark) {
      return appThemeData[AppTheme.Dark];
    } else {
      return appThemeData[AppTheme.Bright];
    }

  }

  Future<void> _saveAppTheme(AppTheme appTheme) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    await prefs.setString(SharedPrefKeys.appThemeKey, appTheme.toString());
  }
}

Solution

If you absolutely must do it like this, you can get MediaQuery data directly from the low-level window object like this:

final brightness = MediaQueryData.fromWindow(WidgetsBinding.instance.window).platformBrightness;

However, I would strongly recommend you consider that if you need access to MediaQuery from within your bloc, you should instead move your BlocProvider to get instantiated after your MaterialApp so you can access MediaQuery normally.

Answered By – Abion47

Answer Checked By – Marie Seifert (FlutterFixes Admin)

Leave a Reply

Your email address will not be published.