I want to implement android 10 dark theme in my app , I have these following cases:
SYSTEM_DEFAULT, NIGHT_MODE, LIGHT_MODE
The problem is when I change theme from night or light to system_default from inside the app and it can not understand whether the system is in light mode or dark mode. so the theme won't be updated.
I've tried the dark theme by google
https://developer.android.com/guide/topics/ui/look-and-feel/darktheme
and implementing the configuration still won't be good for me because I don't want to recreate my activity if user changes day to system default when system default is day.
Is there anyway I could handle this?
when(id) {
NIGHT - > theme = Theme.NIGHT_MODE
DAY - > theme = Theme.LIGHT_MODE
SYSTEM_DEFAULT - > theme = Theme.SYSTEM_DEFAULT
}
context ? .clearCachedDrawables()
activity ? .recreate()
}
EDIT:
when (themeStatus) {
Theme.LIGHT_MODE ->
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
Theme.NIGHT_MODE ->
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
Theme.SYSTEM_DEFAULT ->
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
}
You don't need to set the theme in your activity and recreate it. It's done automatically if you've setup your app theme right.
To use the Dark in your app, you should extend the DayNight theme as your app theme.
<style name="AppTheme" parent="Theme.AppCompat.DayNight">
or
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
If you want for example a different color during dark mode, you should create a 'Android resource directory' called values-night with a 'resource file called colors.xml
In the colors.xml you can set a different color hex for one of your existing colors.
For example:
values/colors.xml contains
<color name="myColor">#000000</color>
values-night/colors.xml contains
<color name="myColor">#FFFFFF</color>
EDIT
You can switch between Dark/light mode in app by calling
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
or
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
or
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
Related
I am trying to change the color/style of the StatusBar based on the device being set to light or dark theme, but not having much luck.
The following XAML works once the app has loaded, but does not work on the splash screen:
<ContentPage.Behaviors>
<toolkit:StatusBarBehavior StatusBarColor="#F8F9FB" StatusBarStyle="DarkContent" />
</ContentPage.Behaviors>
Look of StatusBar during splashscreen:
Look of StatusBar once app has loaded:
As you can see, the XAML above does not seem to affect the StatusBar during the splashscreen, as it is still showing it's default purple color with white text. Any idea how to change the StatusBar while the app is showing the splashscreen?
The end goal is the set the statusbar color and icon colors accordingly based on the device being set to light or dark theme. For example, if the device is set to use the dark theme, the statusbar while showing the splashscreen and the app shell should be a dark background with light text/icons. When the device is set to use the light theme, the statusbar while showing the splashscreen and the app shell should be a light background with dark text/icons.
I have checked many many existing questions about this, but they all seem to be obsolete, as I am using API 33.
In the beginning, the status bar color on Android is set by the MainActivity which you are seeing in your first screenshot of the splash screen. The StatusBarBehavior only kicks in once the hosted MAUI app is loaded in the MainActivity.
UPDATE
In .NET MAUI, you can replace the Maui.SplashTheme theme which is set in the MainActivity class with your own Theme, e.g. MyAppTheme:
[Activity(Theme = "#style/MyAppTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
{
//...
}
You can create your own themes in order to set the color of the splash screen incl. the status bar, you will need to create one for Dark and one for Light mode separately.
Light Mode
In your MAUI project, navigate to the Platforms/Android/Resources/ folder and, if not present yet, add a values subfolder. In this folder, create two files (or edit the existing ones):
colors.xml
styles.xml
Attention: The styles.xml actually probably already exists and is simply hidden. If so, right-click on the values folder and select Add -> Existing Item and then select the styles.xml file in that location.
Then, in your colors.xml define some colors, e.g.:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#FF00FF</color>
<color name="colorPrimaryDark">#019FF5</color>
<color name="colorAccent">#019FF5</color>
</resources>
Next, create your own Theme in the styles.xml, inherit from Maui.SplashTheme and set your color for the android:windowSplashScreenBackground item:
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<style name="MyAppTheme" parent="Maui.SplashTheme">
<item name="android:windowSplashScreenBackground">#color/colorPrimary</item>
<item name="android:windowLightStatusBar">true</item>
</style>
</resources>
This will set up your splash screen color when the user is using the Light theme of Android.
Dark Mode
For Dark theme, you need to do essentially the same as for the Light theme, with a small difference.
In your MAUI project, add the following subfolder: Platforms/Android/Resources/values-night (note the -night suffix). In this folder, again create the two files:
colors.xml
styles.xml
In your colors.xml define some colors, e.g.:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#00FF00</color>
<color name="colorPrimaryDark">#019FF5</color>
<color name="colorAccent">#019FF5</color>
</resources>
Again, create your own Theme in the styles.xml, inherit from Maui.SplashTheme and set your color for the android:windowSplashScreenBackground item:
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<style name="MyAppTheme" parent="Maui.SplashTheme">
<item name="android:windowSplashScreenBackground">#color/colorPrimary</item>
<item name="android:windowLightStatusBar">false</item>
</style>
</resources>
This will set up your splash screen color when the user is using the Dark theme of Android.
Conclusion on Themes
Since Maui.SplashTheme inherits from Theme.MaterialComponents.DayNight you can manipulate any kind of property for the display of native Android views and windows, e.g. the status bar and the action bar. This simply requires that a values and a values-night folder exist under Platforms/Android/Resources/ each containing a colors.xml and a styles.xml file.
You can find the definition of the Maui.SplashTheme theme here: https://github.com/dotnet/maui/blob/main/src/Core/src/Platform/Android/Resources/values/styles.xml
More on Android themes:
https://developer.android.com/develop/ui/views/theming/darktheme
https://www.digitalocean.com/community/tutorials/android-daynight-theme-night-mode
Manipulate StatusBarColor at runtime
After the MainActivity is loaded and your MAUI app is hosted, you can still manipulate the StatusBarColor by calling Window.SetStatusBarColor() anytime, for example like this (note that you can do this at any other place and use inversion of control to set the color from within your shared context):
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
//make sure only supported APIs use it
if (Build.VERSION.SdkInt >= BuildVersionCodes.R)
{
if(AppInfo.RequestedTheme == AppTheme.Light)
{
Window.InsetsController?.SetSystemBarsAppearance((int)WindowInsetsControllerAppearance.LightStatusBars, (int)WindowInsetsControllerAppearance.LightStatusBars);
//set color
Window.SetStatusBarColor(Color.White);
}
else
{
Window.InsetsController?.SetSystemBarsAppearance(0, (int)WindowInsetsControllerAppearance.LightStatusBars);
Window.SetStatusBarColor(Color.Black);
}
}
}
That's how I am using it in one of my current projects. I was wondering how to change the status bar during the Splash screen as well and figured out that you need to change the Maui.SplashTheme for that.
Respond to theme changes
If you need to change the theme based on the system theme during runtime and respond to changes, see the following:
https://learn.microsoft.com/en-us/dotnet/maui/user-interface/system-theme-changes#react-to-theme-changes
Note: You will need to use inversion of control or conditional compilation for this, if you want to explicitly set the status bar color during runtime from within your shared code. You can use any IoC container for this or follow James Montemagno's YT video: https://youtu.be/JCgxK0pWjNg
If you are interested in letting the user choose the theme instead, checkout my blog article about Dark and Light themes with .NET MAUI:
https://ewerspej.hashnode.dev/implement-dark-mode-in-net-maui
I have two themes. One is:
<style name="ThemeDay" parent="Theme.AppCompat.Light.NoActionBar">
Other one is:
<style name="ThemeNight" parent="Theme.AppCompat.DayNight.NoActionBar">
I apply themes in every activity like this before super.onCreate()
if(GenelUtil.getNightMode()){
setTheme(R.style.ThemeNight);
}else{
setTheme(R.style.ThemeDay);
}
Theme is applied. But system doesnt behave like selected theme.
For example when system is light mode and app is dark mode, Navigation bar is still white, dialogs are still white etc..
But when I open instagram and switch to dark mode, system also behave like its in dark mode.
What do I do wrong here? How can I fix this?
DayNight theme is picking automatically dark or light params/colors basing on system settings (by default) or set/forced by you (e.g. using AppCompatDelegate.setDefaultNightMode())
when you have light theme in your system and you will use DayNight theme for your app then still not overriden colors will be same as in "light" theme
According to the docs, I only need to set android:forceDarkAllowed=true in my activity manifest and inherit theme from parent="Theme.MaterialComponents.DayNight". I tried that, but it didn't work.
Here's my manifest file:
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:forceDarkAllowed="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
And here's my styles.xml styles:
<style name="AppTheme" parent="Theme.MaterialComponents.Light">
...
</style>
<style name="AppTheme.Dark" parent="Theme.MaterialComponents.DayNight">
...
</style>
I tried to get the theme name of the activity by using this code below:
resources.getResourceName(
packageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA)
.applicationInfo.theme
)
It shows me com.example.name:style/AppTheme and not AppTheme.Dark. How can I make it so that when I run the application, the MainActivity automatically sets itself to use AppTheme.Dark (i.e. dark mode) using android:forceDarkAllowed?
That is not how dark mode works on Android 10.
First, Android does not magically append .Dark to your theme at any point, regardless of whether or not the user enabled dark mode. You have android:theme="#style/AppTheme". So, Android will use AppTheme all the time, and your AppTheme.Dark will not be used.
Second, the idea behind DayNight is that you use that as you base theme all the time, and Android will switch between normal and dark modes based on user request. So, if you switch AppTheme to extend DayNight, and get rid of AppTheme.Dark, you will be matching what the documentation calls for.
android:forceDarkAllowed is for cases where you are not in position to use a DayNight theme, and you want Android to try to automatically convert your UI to a dark theme. If you wanted to keep AppTheme extending Light instead of DayNight, this might work (I personally have not tried it with a MaterialComponents theme). But, even then, it will only be when the user has enabled dark mode (e.g., tapped on the notification shade tile).
For example, in this sample app, I use DayNight for my theme:
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.DayNight">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
</style>
</resources>
I have different definitions for those colors in res/values/ and res/values-night/. I do not use forceDarkAllowed. And, when the user toggles night mode, my UI goes from:
to:
How can I make it so that when I run the application, the MainActivity automatically sets itself to use AppTheme.Dark (i.e. dark mode) using android:forceDarkAllowed?
You don't.
If you want a dark theme all the time, use a regular theme (not Light, not DayNight).
If your app uses a dark theme (such as Theme.Material), Force Dark will NOT be applied. Similarly, if your app's theme inherits from a DayNight theme, Force Dark will NOT be applied, due to the automatic theme switching.
Dark theme is available in Android 10 (API level 29) and higher. Make sure that your device's API level is 29 + that your target SDK in the gradle is 29.
There are two ways to apply dark theme:
DayNight - custom-able theme - you control how dark/light theme will look like
Force dark - you control the light theme, and if the user chooses dark mode the system will convert your views to dark theme at runtime.
There are settings to allow dynamically setting day or night regardless of the current system setting.
Each of the options map directly to one of the AppCompat.DayNight
modes:
Light - MODE_NIGHT_NO
Dark - MODE_NIGHT_YES
System default - MODE_NIGHT_FOLLOW_SYSTEM
To switch the theme, call AppCompatDelegate.setDefaultNightMode().
https://developer.android.com/guide/topics/ui/look-and-feel/darktheme#change-themes
This would apply to setting a DayNight theme. If you are trying to take advantage of the Force Dark in Android 10, it applies to the .Light sub-themes. It will not be applied to any of the DayNight or inherently dark themes.
Apps must opt-in to Force Dark by setting
android:forceDarkAllowed="true" in the activity's theme. This
attribute is set on all of the system and AndroidX provided light
themes, such as Theme.Material.Light.
https://developer.android.com/guide/topics/ui/look-and-feel/darktheme#force-dark
Below that, the documentation also specifies how to exclude certain items, either in resources or code.
Force Dark can be controlled on specific views with the
android:forceDarkAllowed layout attribute or with
setForceDarkAllowed().
Beyond that, there are ways to detect the current system light / dark setting and respond to it using the options above, but that is for another post.
It's not a great solution for a dark theme and I apologize for any overlap with the accepted answer, but I do not agree with claiming something isn't possible because it isn't preferred.
Since Android 10, you can switch between dark mode and default light mode. I didn't make any closer research yet on this since it's a new topic. Is Dark mode color switching automatic by OS or is there any way to tell my App to switch different app-theme if Dark mode is turned on? Also dark mode is possibility for some Android 9 devices.
Because I made custom Dark theme with custom parameters and I set dark color for each of my color in resources (using custom attributes in attrs.xml and applied specific color resource to them inside theme in styles.xml). Same it is working for different color schemes of my App (blue, red, green for example). That way I can tell which color would be used for different views in my App and not system.
Only thing I need is to detect if dark mode is on/off in system. I can force user to turn on Dark mode in App settings (custom settings) but theme can be affected by system dark mode (turned on in Phone settings).
From official documentations:
In order to support Dark theme, you must set your app's theme (usually found in res/values/styles.xml) to inherit from a DayNight theme:
<style name="AppTheme" parent="Theme.AppCompat.DayNight">
You can also use MaterialComponents' dark theming:
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
This ties the app's main theme to the system-controlled night mode flags and gives the app a default Dark theme (when it is enabled).
It means that if you use a DayNight theme, the OS itself handles the app theme. if you want to force app uses dark theme check this documentation.
UPDATE:
To detect the device theme:
switch (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) {
case Configuration.UI_MODE_NIGHT_YES:
…
break;
case Configuration.UI_MODE_NIGHT_NO:
…
break;
}
I have an app in which the user can choose between the default theme and a dark theme. The manifest applies AppTheme to the <application>, while each activity checks the relevant flag and uses setTheme(AppTheme.Dark) if necessary.
This implementation works well for all my activities, but the preview window shown by the OS before the application launches uses the Manifest theme, i.e. the light one. So users get a sudden white screen before reaching the dark app.
Based on other SO answers, I gathered that the only customization we can do is to set a windowBackground drawable for the window to use (and a statusBarColor if needed).
Even though I have two different windowBackground set for each theme, I can't change the theme for the preview window since the Application class seems to load after it, and setting theme there makes no difference.
I'd like to avoid disabling the preview since it adds a noticeable delay without much indication that the app is launching. Is there any solution to this? I'm considering having a dark color for both themes in the worst case, like the Reddit app does.
Styles:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- primary and accent color attributes -->
<item name="android:windowBackground">#color/activityBackgroundLight</item>
</style>
<style name="AppTheme.Dark" parent="AppTheme">
<!-- primary and accent color attributes -->
<item name="android:windowBackground">#color/activityBackgroundDark</item>
</style>
Note: While I'm using colors here, I've already tried with a gradient drawable and it doesn't change anything.
UPDATE: I wasn't able to get any other method, so I went ahead and applied the dark theme by default on the SplashActivity. This means the preview window is always dark, while the check in the SplashActivity's onCreate is inverted (compared to other activities) and applies the light theme if needed.
Still looking for a solution that actually lets me switch at runtime.