0

I want to implement a switch for toggling dark mode in my application. After investigating multiple sources on how to do this correctly, I came across this one-line solution:

AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);

Unfortunately, in my case this only changes the configuration to light mode and doesn't update UI colors. Here's my code:

binding.toggleDarkMode.setOnCheckedChangeListener { _, isChecked ->
   if (isChecked) {
      AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
      activity?.recreate()
   }
}

I'd also like to mention that I have separate theme files for light and dark mode. Light theme extends Theme.Material3.Light.NoActionBar and dark theme extends Theme.Material3.Dark.NoActionBar. Could anyone tell me what could be the problem?

Bravo
  • 1,944
  • 4
  • 29
  • 53

2 Answers2

1

That line you posted explicitly sets the theme to light mode

AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)

or to put it another way, "not night mode".

If you want it to toggle, you need to get the current mode (light or dark) and set it to the other one:

// check the mode
val isDarkMode = AppCompatDelegate.getDefaultNightMode() == NIGHT_MODE_YES
// set the other
AppCompatDelegate.setDefaultNightMode(if (isDarkMode) NIGHT_MODE_NO else NIGHT_MODE_YES)

or if you're relying on the switch state, just do if (isChecked) instead, no need to get the current mode.


There are more than two modes btw, it's recommended that you handle the "follow the system" one too but that's up to you - more info about the whole thing here from one of the developers.

It also gets into the fact you need to call setDefaultNightMode whenever your app starts, as early as possible, so it can be themed correctly - e.g. in a custom Application class, although if you're doing a single-activity app you could stick it in onCreate there too. Otherwise it would need to go in every Activity, just in case that's the one that's created first when the app is opened/restored.

But what this means is you have to store the light/dark setting so it can be read when the app starts - it doesn't "stick" to the last thing you set it to. You could use SharedPreferences for this, and if your switch is an actual Preference that's all handled automatically! But if it's like a toggle on a normal app screen, you'll have to store it yourself

edit I knew there was something I wanted to mention - you don't need to do activity?.recreate(), setDefaultNightMode will handle that for you if it's required (i.e. if there's been a change)

cactustictacs
  • 17,935
  • 2
  • 14
  • 25
  • Thanks for your answer, the problem is that after calling MODE_NIGHT_NO I see that the uiMode of my config successfully changes to UI_MODE_NIGHT_NO, but components colors do not switch to light theme, so basically nothing visible happens on the UI – Bravo Nov 11 '22 at 19:47
  • @Bravo if you're not seeing any change at all anywhere, that sounds like an issue with your themes. Your themes usually have to inherit from `Theme.AppCompat.DayNight` or `Theme.MaterialComponents.DayNight` rather than a light or dark theme specifically (https://developer.android.com/develop/ui/views/theming/darktheme#support-dark-theme) so see if Material 3 has a `DayNight` one – cactustictacs Nov 11 '22 at 20:04
  • I've changed to Theme.Material3.DayNight, but unfortunately it still doesn't work for me. Although, I should mention that the text color indeed changes to light mode, but buttons, background, etc stays DARK mode – Bravo Nov 12 '22 at 00:10
  • @Bravo that sounds like parts of your UI are overriding the defaults in the theme, like styling is being applied on another level - out of the box the `MaterialComponents.DayNight` theme has the standard light theme as well as a standard dark variant, so if you're not even getting the normal light theme then check your basic attrs like ``colorPrimary`` and `colorBackground` aren't being overridden with the wrong colours in the light variant – cactustictacs Nov 12 '22 at 00:37
  • I know you're using Material 3, but here's the current way to do things, so maybe start here https://material.io/blog/android-dark-theme-tutorial and make sure you've got the basics down, night-qualified resources in the right place, etc. Maybe start with the original theme and start adding your customisation in bit by bit. I haven't used Material 3, but maybe this helps too: https://github.com/material-components/material-components-android/blob/master/docs/theming/Color.md#using-dynamic-colors (that and the *Custom colors* section below it). That's all I can advise really – cactustictacs Nov 12 '22 at 00:41
  • I've just double-checked all my styling files - everything is correct. For testing purposes I removed all the theme overriden values and used plain Theme.Material3.DayNight but the same result is produced. One more important thing to mention - when I toggle dark mode on system level (in settings), then everything works as expected, so this means that my files are in the right place with correct values – Bravo Nov 12 '22 at 08:19
  • @Bravo are you using an `AppCompatActivity`? – cactustictacs Nov 12 '22 at 19:11
  • Yes, I'm using AppCompatActivity and my navigation is fragment-based (just one Activity class) – Bravo Nov 13 '22 at 22:17
  • @Bravo I'm not sure then, sorry - I'm guessing you're using the AndroidX support `Fragment` class too (I'm not sure if it matters). Also since this behaviour is being triggered by a `CheckedChangedListener`, and those fire when the checked state is *set*, make sure the Activity recreation isn't resetting that switch to off and causing it to default to dark mode or anything. (Personally I tend to prefer click listeners instead, because those only respond to user interactions). I don't know what else to suggest really - see if a simple button setting a specific mode works and go from there? – cactustictacs Nov 13 '22 at 22:54
  • 1
    Thank you for your help and instructions @cactustictacs. I'll investigate further and let you know if I'll be able to fix this issue – Bravo Nov 17 '22 at 09:42
  • 1
    Found the problem... please have a look at the answer I posted – Bravo Nov 17 '22 at 12:44
0

So basically my phone's OS was overriding my app's styles on a MUI device. Here's the complete answer: Can't disable Night Mode in my application even with values-night

Bravo
  • 1,944
  • 4
  • 29
  • 53
  • 1
    Oh that sucks. I know MIUI can use force-dark but I've never seen an issue with stuff that actually uses dark themes, maybe that disabled `forceDarkAllowed` attribute is a good one to add in general. Thanks for letting me know! – cactustictacs Nov 17 '22 at 16:13