How to listen for changes to platformBrightness in Flutter?
Asked Answered
R

7

19

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',
    );
Regression answered 6/10, 2019 at 19:40 Comment(0)
L
39

You can do it in many ways:

  • Using didChangeDependencies

    // This callback will be invoked every time the platform brightness changes.
    @override
    void didChangeDependencies() {
      super.didChangeDependencies();
    
      // Get the brightness. 
      var brightness = MediaQuery.of(context).platformBrightness;
    }
    
  • Using WidgetsBindingObserver

    class _MyPageState extends State<MyPage> with WidgetsBindingObserver {
      @override
      void initState() {
        super.initState();
        WidgetsBinding.instance.addObserver(this);
      }
    
      // This callback is invoked every time the platform brightness changes. 
      @override
      void didChangePlatformBrightness() {
        super.didChangePlatformBrightness();
    
        // Get the brightness.
        var brightness = View.of(context).platformDispatcher.platformBrightness;
      }
    
      @override
      void dispose() {
        WidgetsBinding.instance.removeObserver(this);
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) => Container();
    }
    
  • Setting listener in initState

    @override
    void initState() {
      super.initState();
    
      var dispatcher = SchedulerBinding.instance.platformDispatcher;
    
      // This callback is called every time the brightness changes. 
      dispatcher.onPlatformBrightnessChanged = () {
        var brightness = dispatcher.platformBrightness;
      };
    }
    
Lock answered 22/5, 2020 at 8:41 Comment(2)
Works great πŸ‘. But using this overrides the default method. After adding our custom onPlatformBrightnessChanged method, the theme does not change automatically. How do we also add the method to change the theme of the app? – Strep
window.onPlatformBrightnessChanged = () { WidgetsBinding.instance.handlePlatformBrightnessChanged(); if (AppStorage.instance.themeMode == ThemeMode.system) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { setState(() {}); }); } }; – Pazit
B
17

@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();
  }
}

Bechler answered 31/10, 2021 at 6:39 Comment(0)
D
2

Listen to changes using (from WidgetsBinding)

void didChangePlatformBrightness()

https://api.flutter.dev/flutter/widgets/WidgetsBindingObserver/didChangePlatformBrightness.html

Dynamic answered 5/12, 2019 at 17:46 Comment(0)
A
1

[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),
                ),
              ),
            ),
Archi answered 13/2, 2022 at 7:57 Comment(3)
If you read the question again, you will notice that the author has already tried your answer. So please suggest another solution. – Helprin
Is the author trying to change the color of AppBar? – Archi
This code is ok – Walz
S
1

There are 2 types of brightness:

  • MediaQueryData.platformBrightness: "The current brightness mode of the host platform"
  • MediaQueryData.brightness: "The overall theme brightness.". You probably want this.

If you have an in-app setting for users to configure the brightness differently/independently of the OS, platformBrightness doesn't work. You want your app brightness.

    // Doesn't always work, because platformBrightness is: "The current brightness mode of the host platform".
    final brightness_wrong_1 = MediaQuery.platformBrightnessOf(context);
    final brightness_wrong_2 = MediaQuery.of(context).platformBrightness;
    // If you use flutter hooks (doesn't work)
    final brightness_wrong_3 = usePlatformBrightness();
    // Works:
    final brightness = Theme.of(context).brightness;
Swartz answered 24/11, 2022 at 22:42 Comment(0)
A
0

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...

Absalom answered 6/10, 2019 at 21:2 Comment(0)
C
0

Just a 2024 update, the current version of Flutter lets you use the answers above with a bit less code:

Widget build(BuildContext context) {
  return MaterialApp(
    themeMode: ThemeMode.system,
    theme: ThemeData.light(),
    darkTheme: ThemeData.dark(),

    home: Scaffold( ...

That's all you need now. This will not only support Dark Mode, it will support switching when your OS switches Dark/Light Mode too, without any special work. This can be done in a StatelessWidget (Stateful is fine too, but, not required, and Dark Mode doesn't need to be communicated to Widget state, nor do you need to setup an Observable etc).

Chloroplast answered 18/7, 2024 at 19:18 Comment(0)

© 2022 - 2025 β€” McMap. All rights reserved.