I have an Android App that uses webview, and lately I'm trying to figure out how to add a dark theme by using the new @media (prefers-color-scheme: dark)
CSS syntax. I have the correct CSS written on my page, and if I open it in Chrome with the dark mode of Chrome turning on, it works. I also have my AppTheme
inheriting Theme.AppCompat.DayNight
, and my app shows dark loading dialog etc. when I turn on dark mode for the entire OS on my device. Even the auto-complete options for the <input>
elements become dark. But still, the webpage loaded with my webview doesn't turn dark. According to this page, webviews should support this feature, but I just can't get it to work. What am I missing here?
I also just found out that in API 29 there's this WebSettings.setForceDark()
method; could it be the thing I'm looking for? I hope to find a solution that works with lower API level though.
By the way, my current workaround is to inject a JavaScript interface like this:
webView.addJavascriptInterface(new JSInterface(), "jsInterface");
...
public class JSInterface {
@JavascriptInterface
public boolean isNightMode() {
int nightModeFlags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
return nightModeFlags == Configuration.UI_MODE_NIGHT_YES;
}
}
And then in my webpage, call the jsInterface.isNightMode()
method and dynamically load different CSS file based on the result. It certainly works and responses to the global dark mode setting as desired, but I still wonder if I can make prefers-color-scheme
work.
Use FORCE_DARK_AUTO mode WebView and all its parents can allow force dark using View. setForceDarkAllowed() . The default value is taken from the setForceDarkAllowed() attribute of the Android theme, which must also be set to true .
Open System Settings. Navigate to Developer Options. Scroll down the Hardware accelerated rendering section and enable the Override force-dark/Force dark mode* option.
Enhance webView performance - disable the WebView cache to make WebView much faster. Supercharging the Android WebView - cache critical assets that drastically reduce the load time.
Android Webview handles day/night mode a bit differently from the rest of the views. Setting your theme to dark will change the WebView components (scrollbar, zoom buttons etc.) to a dark mode version, but will not change the content it loaded.
To change the content you need to use the setForceDark method of the webview settings to make it change its contents as well. A compatibility version of this method can be found in the AndroidX webkit package.
Add the following dependency to your gradle build:
implementation 'androidx.webkit:webkit:1.3.0'
(1.3.0 is the minimum required version of this package. But higher versions should work as well.)
And add the following lines of code to your webview intitialization:
if(WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
WebSettingsCompat.setForceDark(myWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
}
The isFeatureSupported
check is there to make sure the Android System WebView version the user has installed on their device supports dark mode (since this can be updated or downgraded independently from the Android version through Google Play).
Note: The setForceDark
feature requires Android System WebView v76 or up to be installed on the running device.
The force dark feature for webview content has two so-called strategies:
User agent darkening: The webview will set its content to dark mode by automatically inverting or darkening colors of its content.
Theme based darkening: The webview will change to dark mode according to the theme of the content (which includes the @media (prefers-color-scheme: dark)
query).
To set which strategy the webview should use to apply force dark you can use the following code:
if(WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)) {
WebSettingsCompat.setForceDarkStrategy(myWebView.getSettings(), WebSettingsCompat.DARK_STRATEGY_WEB_THEME_DARKENING_ONLY);
}
Note: Strategy selection requires Android System WebView v83 or up to be installed on the running device. WebView versions that support setForceDark
but do not support strategy selections (v76 to v81) will use user agent darkening
The supported strategy options are:
How do Javascript checks work for darkened webviews?
The JavaScript call window.matchMedia('(prefers-color-scheme: dark)')
will match in both the user agent darkening and web theme darkening strategy.
I have my webview set to FORCE_DARK_AUTO and my app is running in a daynight theme, but somehow my webview doesn't apply dark mode automatically based on my app theme. Why does this happen?
It's because the FORCE_DARK_AUTO
setting value of the webview doesn't work based on themes (as noted in the documentation).
It checks for the Android 10 Force Dark feature (a "quick-fix" dark mode feature for apps. It's similarly named, but not directly related to the WebView force dark).
If you aren't using force dark but a app theme to handle dark mode (as recommended), you have to implement your own check for when to apply the webview's force dark feature. An example when using a DayNight theme:
int nightModeFlags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
if (nightModeFlags == Configuration.UI_MODE_NIGHT_YES) {
//Code to enable force dark using FORCE_DARK_ON and select force dark strategy
}
What I do, based on your question code, is to force based on current status:
int nightModeFlags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
if (nightModeFlags == Configuration.UI_MODE_NIGHT_YES) {
webSettings.setForceDark(WebSettings.FORCE_DARK_ON);
}
This way @media (prefers-color-scheme: dark)
works, but @media (prefers-color-scheme: light)
still doesn't work (tried using FORCE_DARK_OFF
and FORCE_DARK_AUTO
in an else)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With