androidandroid-jetpack-composethemescompose-recomposition

Theme Change Not Reflecting in Jetpack Compose App After Switching in ThemeScreen


I'm working on an Android app using Jetpack Compose and trying to implement a dynamic theme change functionality. The theme is set using a custom PlanstyTheme function, and users can select the theme from a ThemeSelectionScreen. The theme selection is supposed to update the app's appearance immediately, but the change doesn't seem to take effect after switching the theme in ThemeScreen.

Here's a snippet of my MainActivity:

class MainActivity : ComponentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    enableEdgeToEdge()
    val preferences: AppPreferences by inject()
    setContent {
        val viewmodel: MainScreenModel by inject()
        val isOnBoardingDone by preferences.isOnboardingComplete.collectAsState(initial = false)
        val appTheme by viewmodel.appTheme.collectAsState(initial = AppTheme.SYSTEM)
        val accentColor by viewmodel.accentColor.collectAsState(initial = MaterialTheme.colorScheme.primaryContainer)

        Log.i("theme name Main", appTheme.name)
        Log.i("accent theme name Main", "${accentColor.value}")

        PlanstyTheme(appTheme = appTheme, accentColor = accentColor) {
            if (isOnBoardingDone) {
                Navigator(MainContainer)
            } else {
                Navigator(OnboardingScreen)
            }
        }
    }
}

and below is my Theme composable Method

    @Composable
fun PlanstyTheme(
    appTheme: AppTheme,
    accentColor: Color,
    content: @Composable () -> Unit
) {
    val dark = appTheme == AppTheme.DARK || (appTheme == AppTheme.SYSTEM && isSystemInDarkTheme())

    val colorScheme = when {
        dark -> darkScheme.copy(primary = accentColor)
        else -> lightScheme.copy(primary = accentColor)
    }

    CompositionLocalProvider(
        localColorScheme provides colorScheme
    ) {
        MaterialTheme(
            colorScheme = colorScheme,
            content = content
        )
    }

    // Handle system UI colors
    val view = LocalView.current
    if (!view.isInEditMode) {
        SideEffect {
            val window = (view.context as Activity).window
            window.statusBarColor = Color.Transparent.toArgb()
            WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !dark
        }
    }

and i will paste the code from my datastore too

    class AppPreferences(private val context: Context) {

    // to make sure there's only one instance
    companion object {
        private val Context.appPreference: DataStore<Preferences> by preferencesDataStore("preferences")

        //keys
        val ONBOARDING_KEY = booleanPreferencesKey("onboarding")
        val POINTS_KEY = intPreferencesKey("points")

        private val THEME_KEY = stringPreferencesKey("theme")
        private val ACCENT_COLOR_KEY = intPreferencesKey("accent_color")
    }

    // themes and accents
    suspend fun saveTheme(theme: AppTheme) {
        context.appPreference.edit { preferences ->
            preferences[THEME_KEY] = theme.name
        }
        Log.i("datastore theme","$theme")
    }

    fun getTheme() = context.appPreference.data.map { preferences ->

       val theme = preferences[THEME_KEY] ?: AppTheme.SYSTEM.name
        Log.i("get datastore theme","$theme")
        AppTheme.valueOf(theme)

    }

    suspend fun saveAccentColor(color: Color) {
        context.appPreference.edit { preferences ->
            preferences[ACCENT_COLOR_KEY] = color.toArgb()
        }
    }

    fun getAccentColor() = context.appPreference.data.map { preferences ->
        preferences[ACCENT_COLOR_KEY]?.let { Color(it) } ?: Color.Gray
    }
}
enum class AppTheme {
    SYSTEM, LIGHT, DARK
}

there are other codes too where i update the theme in my viewmodel to datastore if you need to see that i can edit an provide the code

Note: I use voyager for navigation and screenmodel and i also notice when i chane themes the status bar change but the whole Ui doesn't

light mode screenshot! notice the status bardark mode screenshot! Notice the status bar

as requested I'll add the code for the MainScreenModel below

class MainScreenModel(private val preferences: AppPreferences) : ScreenModel {

val appTheme = preferences.getTheme()

val accentColor = preferences.getAccentColor()}

when i close and reopen the app, the Theme then changes if you have any suggestion I'd really appreciate it i've been at this all day


Solution

  • The solution was downgrading the version of the voyager I used, and that solved the theme change problem