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
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
The solution was downgrading the version of the voyager I used, and that solved the theme change problem