How to listen for changes to platformBrightness in Flutter?

Viewed 5597

I'm setting up a system where when the user changes the system theme to dark mode, it changes the theme and with Flutter, it works all well and good! However, when the user changes the system theme, the system navigation and status bar don't change their colors. I have code running on the home page inside the build method but that doesn't seem to do it. Here's the code inside the home page build method:

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    Brightness sysBrightness = MediaQuery.of(context).platformBrightness;
    if (sysBrightness == Brightness.dark)
      Themes.setDarkSystemColors();
    else
      Themes.setLightSystemColors();

    return Scaffold(
      appBar: CustomAppBar(title: "Home"),
      drawer: CustomDrawer(),
      body: SizedBox(),
    );
  }
}

Here's the code in the main app with theme: and darkTheme::

    return MaterialApp(
      initialRoute: '/',
      routes: routes,
      navigatorObservers: [_routeObserver],
      theme: Themes.lightTheme,
      darkTheme: Themes.darkTheme,
      debugShowCheckedModeBanner: false,
      title: 'School Life',
    );
5 Answers

Override initState method and use onPlatformBrightnessChanged:

@override
void initState() {
  super.initState();
  final window = WidgetsBinding.instance.window;

  // This callback is called every time the brightness changes.
  window.onPlatformBrightnessChanged = () {
    final brightness = window.platformBrightness;
  };
}

To handle default behavior, add the following line in above callback. Thanks to @DJafari

WidgetsBinding.instance.handlePlatformBrightnessChanged();

@CopsOnRoad answer works perfectly, but it disabled auto reaction to platform brightness change in your widgets, to fix this problem use this :

@override
void initState() {
  super.initState();

  var window = WidgetsBinding.instance!.window;
  window.onPlatformBrightnessChanged = () {
    WidgetsBinding.instance?.handlePlatformBrightnessChanged();
    // This callback is called every time the brightness changes.
    var brightness = window.platformBrightness;
  };
}

another way to handle this :

class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
  @override
  void initState() {
    WidgetsBinding.instance?.addObserver(this);
    super.initState();
  }

  @override
  void dispose() {
    WidgetsBinding.instance?.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangePlatformBrightness() {
    var brightness = Theme.of(context).brightness;
    super.didChangePlatformBrightness();
  }
}

There is a ThemeMode enum (since Flutter version 1.9, I think). So you can set theme to light and darkTheme to dark and themeMode (ThemeMode.system (default), ThemeMode.light, ThemeMode.dark) determines which theme will be used.

MaterialApp(
  theme: ThemeData.light(),
  darkTheme: ThemeData.dark(),
  themeMode: ThemeMode.system,
)

However, I'm not sure if there is a way to listen for platformBrightness changes (ThemeMode also uses platformBrightness under the hood). You will need to rebuild MaterialApp...

[Feb 2022] - Working fine for me.

Use - final Brightness brightness = MediaQuery.platformBrightnessOf(context);.

This will listen to theme changes. You can render differently based on this change.

 @override
  Widget build(BuildContext context) {
    final Brightness brightness = MediaQuery.platformBrightnessOf(context);
    bool isDarkMode = brightness == Brightness.dark;

Later in the Widgets render based on isDarkMode

For Example:

AssetImage(isDarkMode ? darkBgImage : lightBgImage)

Container(
              decoration: BoxDecoration(
                image: DecorationImage(
                  image: AssetImage(isDarkMode ? darkBgImage : lightBgImage),
                  fit: BoxFit.cover,
                  colorFilter:
                      const ColorFilter.mode(Colors.black45, BlendMode.darken),
                ),
              ),
            ),
Related