You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Compose BottomNavigation主题启动不生效问题技术求助

问题:DataStore主题切换应用启动时BottomNavigation样式未更新

我用DataStore实现了一个简易主题切换应用,应用启动时文本颜色、背景色等样式可正常更新,但BottomNavigation的背景色与内容色保持默认状态,仅手动切换主题时才会更新。最后一张场景中BottomNavigation背景应为蓝色,但实际显示为默认黑色。

相关代码

MainActivity

class MainActivity: ComponentActivity() {

    private lateinit var appDataStore: AppDataStore

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        appDataStore = AppDataStore(applicationContext)
        setContent {
            val themeFlow = appDataStore.theme.collectAsState(initial = CustomTheme.A)
            AppTheme(theme = themeFlow.value) {
                MainScreen(dataStore = appDataStore)
            }
        }
    }
}

@Composable
fun MainScreen(dataStore: AppDataStore) {
    val navController = rememberNavController()
    Scaffold(bottomBar = {
        BottomNavigationBar(navController = navController)
    }) { paddingValues ->
        Box(modifier = Modifier.padding(bottom = paddingValues.calculateBottomPadding())) {
            Column(modifier = Modifier.fillMaxSize()) {
                NavigationGraph(navController = navController, dataStore = dataStore)
            }
        }
    }
}

@Composable
fun NavigationGraph(navController: NavHostController,
                    startDestination: String = "screenB",
                    dataStore: AppDataStore) {
    NavHost(navController, startDestination = startDestination) {
        composable(BottomNavigationModel.A.navigationPath) {
            val screenDataModel = ScreenDataModel(textId = R.string.screen_A)
            ScreenA(screenDataModel)
        }
        composable(BottomNavigationModel.B.navigationPath) {
            val screenDataModel = ScreenDataModel(textId = R.string.screen_B)
            ScreenB(screenDataModel, dataStore)
        }
        composable(BottomNavigationModel.C.navigationPath) {
            val screenDataModel = ScreenDataModel(textId = R.string.screen_C)
            ScreenC(screenDataModel)
        }
    }
}

Bottom Navigation Bar

@Composable
fun BottomNavigationBar(navController: NavController) {
    val items = listOf(BottomNavigationModel.A, BottomNavigationModel.B, BottomNavigationModel.C)
    BottomNavigation(backgroundColor = MaterialTheme.customColors.bottomNavigationBackgroundColor,
        contentColor = MaterialTheme.customColors.bottomNavigationContentColor) {
        val navBackStackEntry by navController.currentBackStackEntryAsState()
        val currentRoute = navBackStackEntry?.destination?.route
        items.forEach { item ->
            BottomNavigationItem(
                icon = { Icon(painterResource(id = item.iconId), contentDescription = "") },
                label = { Text(text = stringResource(id = item.titleId), fontSize = 10.sp) },
                selectedContentColor = MaterialTheme.customColors.bottomNavigationContentColor,
                unselectedContentColor = MaterialTheme.customColors.bottomNavigationContentColor.copy(alpha = 0.4f),
                alwaysShowLabel = true,
                selected = currentRoute == item.navigationPath,
                onClick = {
                    navController.navigate(item.navigationPath) {
                        navController.graph.startDestinationRoute?.let { navigationPath ->
                            popUpTo(navigationPath) {
                                saveState = true
                            }
                        }
                        launchSingleTop = true
                        restoreState = true
                    }
                }
            )
        }
    }
}

Theme

val LocalColors = staticCompositionLocalOf { ThemeACustomColors }

val MaterialTheme.customColors: CustomColors
    @Composable
    @ReadOnlyComposable
    get() = LocalColors.current

@Composable
fun AppTheme(
    theme: CustomTheme = CustomTheme.A,
    content: @Composable () -> Unit
) {
    val colors = when (theme) {
        CustomTheme.B -> ThemeBCustomColors
        CustomTheme.C -> ThemeCCustomColors
        CustomTheme.D -> ThemeDCustomColors
        else -> ThemeACustomColors
    }

    CompositionLocalProvider(LocalColors provides colors) {
        MaterialTheme(
            colors = colors.material,
            typography = Typography,
            shapes = Shapes,
            content = content
        )
    }

    val useDarkIcons = when (theme) {
        CustomTheme.C, CustomTheme.D -> false
        else -> true
    }

    val systemUiController = rememberSystemUiController()
    systemUiController.setStatusBarColor(colors.statusBarColor, useDarkIcons)
}

Gradle (Project)

buildscript {
    ext {
        compose_ui_version = '1.2.0'
    }
}// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    id 'com.android.application' version '7.4.2' apply false
    id 'com.android.library' version '7.4.2' apply false
    id 'org.jetbrains.kotlin.android' version '1.7.0' apply false
}

Gradle (Module)

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}

android {
    namespace 'com.example.themeapp'
    compileSdk 33

    defaultConfig {
        applicationId "com.example.themeapp"
        minSdk 21
        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'
    }
    packagingOptions {
        resources {
            excludes += '/META-INF/{AL2.0,LGPL2.1}'
        }
    }
}

dependencies {
    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
    implementation 'androidx.activity:activity-compose:1.3.1'
    implementation "androidx.compose.ui:ui:$compose_ui_version"
    implementation "androidx.navigation:navigation-compose:2.5.3"
    implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version"
    implementation 'androidx.compose.material:material:1.2.0'
    implementation "com.google.accompanist:accompanist-systemuicontroller:0.27.0"
    implementation("androidx.datastore:datastore-preferences:1.0.0")
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
    androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version"
    debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version"
    debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version"
}

场景说明

  • Theme A - 默认主题
  • Theme B - 已保存主题
  • 底部导航栏背景应为蓝色(实际显示默认黑色)

问题原因

启动时主题状态从DataStore加载完成后触发AppTheme重组,但BottomNavigationBar仅依赖navController的栈状态变化,未响应主题状态更新,导致样式未同步。

解决方案

在BottomNavigationBar中添加主题颜色作为重组触发键,确保主题变化时强制重组:

@Composable
fun BottomNavigationBar(navController: NavController) {
    val items = listOf(BottomNavigationModel.A, BottomNavigationModel.B, BottomNavigationModel.C)
    // 添加key,当自定义颜色变化时强制重组
    key(MaterialTheme.customColors) {
        BottomNavigation(
            backgroundColor = MaterialTheme.customColors.bottomNavigationBackgroundColor,
            contentColor = MaterialTheme.customColors.bottomNavigationContentColor
        ) {
            val navBackStackEntry by navController.currentBackStackEntryAsState()
            val currentRoute = navBackStackEntry?.destination?.route
            items.forEach { item ->
                BottomNavigationItem(
                    icon = { Icon(painterResource(id = item.iconId), contentDescription = "") },
                    label = { Text(text = stringResource(id = item.titleId), fontSize = 10.sp) },
                    selectedContentColor = MaterialTheme.customColors.bottomNavigationContentColor,
                    unselectedContentColor = MaterialTheme.customColors.bottomNavigationContentColor.copy(alpha = 0.4f),
                    alwaysShowLabel = true,
                    selected = currentRoute == item.navigationPath,
                    onClick = {
                        navController.navigate(item.navigationPath) {
                            navController.graph.startDestinationRoute?.let { navigationPath ->
                                popUpTo(navigationPath) {
                                    saveState = true
                                }
                            }
                            launchSingleTop = true
                            restoreState = true
                        }
                    }
                )
            }
        }
    }
}

同时可检查CustomTheme对应的颜色配置,确保ThemeBCustomColors中的bottomNavigationBackgroundColor确实设置为预期的蓝色,避免颜色定义错误导致的问题。


内容的提问来源于stack exchange,提问作者apphitect

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 00:24:54