如何在Jetpack Compose中解决WebView深色模式设置失效问题?
Jetpack Compose 中 WebView 深色模式生效方案
问题背景
尝试为WebView启用深色模式但未生效,setForceDark方法已被标记为废弃,在Android 11和13系统测试时,WebView可正常运行但深色模式未切换。以下是原始实现代码与Gradle配置:
原始代码
package com.blogspot.boltuix import android.annotation.SuppressLint import android.content.res.Configuration import android.os.Bundle import android.view.ViewGroup import android.webkit.WebView import android.webkit.WebViewClient import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.viewinterop.AndroidView import androidx.webkit.WebSettingsCompat import androidx.webkit.WebViewFeature class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { WebViewPage("https://www.boltuix.com/") } } } @SuppressLint("SetJavaScriptEnabled") @Composable fun WebViewPage(url: String){ val context = LocalContext.current val configuration = LocalConfiguration.current when (configuration.orientation) { Configuration.ORIENTATION_LANDSCAPE -> { Toast.makeText(context, "landscape", Toast.LENGTH_SHORT).show() } else -> { Toast.makeText(context, "portrait", Toast.LENGTH_SHORT).show() } } AndroidView(factory = { WebView(it).apply { layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) webViewClient = WebViewClient() settings.javaScriptEnabled = true settings.userAgentString = System.getProperty("http.agent") if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { WebSettingsCompat.setForceDark(settings, WebSettingsCompat.FORCE_DARK_ON) } loadUrl(url) } }, update = { it.loadUrl(url) }) }
原始App级Gradle配置
plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' } android { namespace 'com.blogspot.boltuix' compileSdk 33 defaultConfig { applicationId "com.blogspot.boltuix" minSdk 24 targetSdk 33 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { useSupportLibrary true } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } buildFeatures { compose true } composeOptions { kotlinCompilerExtensionVersion '1.2.0-beta01' } packagingOptions { resources { excludes += '/META-INF/{AL2.0,LGPL2.1}' } } } dependencies { implementation 'androidx.core:core-ktx:1.8.0' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.0' implementation 'androidx.activity:activity-compose:1.5.0' implementation "androidx.compose.ui:ui:1.3.0-alpha01" implementation "androidx.compose.ui:ui-tooling-preview:1.3.0-alpha01" implementation 'androidx.compose.material3:material3:1.0.0-alpha14' testImplementation 'junit:junit:4.13.2' implementation "androidx.webkit:webkit:1.5.0-beta01" androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation "androidx.compose.ui:ui-test-junit4:1.3.0-alpha01" debugImplementation "androidx.compose.ui:ui-tooling:1.3.0-alpha01" debugImplementation "androidx.compose.ui:ui-test-manifest:1.3.0-alpha01" }
问题分析
- 仅设置
FORCE_DARK_ON未配置深色模式转换策略,WebView无法正确将浅色网页转换为深色风格 - 未监听系统主题变化,AndroidView的
factory块仅执行一次,切换系统深色模式后WebView不会同步更新 - 使用了beta版本的webkit依赖,可能存在兼容性问题
有效解决方案
1. 升级Webkit依赖至稳定版
将Gradle中的webkit依赖替换为稳定版本:
implementation "androidx.webkit:webkit:1.6.0"
2. 配置App主题支持深色模式
在res/values/styles.xml中设置支持日夜切换的主题:
<style name="Theme.Boltuix" parent="Theme.Material3.DayNight.NoActionBar"> <!-- 自定义主题属性 --> </style>
3. 修改WebView实现,支持动态深色模式
在Compose中监听系统UI模式变化,动态更新WebView的深色配置,同时添加转换策略:
package com.blogspot.boltuix import android.annotation.SuppressLint import android.content.res.Configuration import android.os.Bundle import android.view.ViewGroup import android.webkit.WebView import android.webkit.WebViewClient import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.viewinterop.AndroidView import androidx.webkit.WebSettingsCompat import androidx.webkit.WebViewFeature class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { WebViewPage("https://www.boltuix.com/") } } } @SuppressLint("SetJavaScriptEnabled") @Composable fun WebViewPage(url: String) { val context = LocalContext.current val configuration = LocalConfiguration.current // 监听当前是否为深色模式 val isDarkMode = configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES // 保存WebView实例,避免重复创建 var webView by remember { mutableStateOf<WebView?>(null) } // 处理屏幕方向提示 when (configuration.orientation) { Configuration.ORIENTATION_LANDSCAPE -> { Toast.makeText(context, "横屏模式", Toast.LENGTH_SHORT).show() } else -> { Toast.makeText(context, "竖屏模式", Toast.LENGTH_SHORT).show() } } AndroidView( factory = { ctx -> WebView(ctx).apply { layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) webViewClient = WebViewClient() settings.javaScriptEnabled = true settings.userAgentString = System.getProperty("http.agent") // 配置深色模式及转换策略 setupDarkMode(isDarkMode) loadUrl(url) webView = this } }, update = { view -> // 主题变化时更新深色模式 view.setupDarkMode(isDarkMode) // 仅在URL变化时重新加载,避免不必要刷新 if (view.url != url) { view.loadUrl(url) } } ) // 销毁时清理WebView DisposableEffect(Unit) { onDispose { webView?.destroy() } } } // 扩展函数:配置WebView深色模式 private fun WebView.setupDarkMode(isDarkMode: Boolean) { if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { val forceDarkMode = if (isDarkMode) { WebSettingsCompat.FORCE_DARK_ON } else { WebSettingsCompat.FORCE_DARK_OFF } WebSettingsCompat.setForceDark(settings, forceDarkMode) } // 配置深色模式转换策略:优先使用网页自带深色主题,否则自动转换 if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)) { WebSettingsCompat.setForceDarkStrategy( settings, WebSettingsCompat.DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING ) } }
关键说明
- 使用
FORCE_DARK_ON/FORCE_DARK_OFF根据系统主题动态切换,替代固定的FORCE_DARK_ON - 添加
FORCE_DARK_STRATEGY配置,让WebView优先使用网页自带的深色主题,没有则自动转换网页颜色 - 在
update块中处理主题变化,确保切换系统深色模式时WebView同步更新 - 使用
DisposableEffect在Composable销毁时清理WebView,避免内存泄漏
内容的提问来源于stack exchange,提问作者Bolt UIX
相关产品推荐
相关产品推荐

