2

I have a project with several flavors. Each of these flavors has its own configuration which is available as a json file in the assets folder in the respective project structure.

In the theme definition I read the JSON using Gson and cast it into a corresponding model.

My problem is now the following:

At runtime of the app this all works wonderfully but in the composable preview in Android Studio it unfortunately only works for a single flavor. As soon as I switch to another flavor in the build variant, the json-asset of the old variant continues to load. Since the configuration also contains assets that are only available in the respective flavors, this leads to a crash of the preview. I debugged the preview handling by throwing some exceptions during the casting and it seems, like if there'S something cached and not reset after build-variant change. A restart of Android Studio didn't also help so I don't quite know what to do about it.

Has anyone noticed a similar behavior and/or found a solution for it?

Here is some code to explain::

My theme definition:

object AppTheme {
    val colors: AppColors
        @Composable
        @ReadOnlyComposable
        get() = LocalAppColors.current
    val typography: AppTypography
        @Composable
        @ReadOnlyComposable
        get() = LocalAppTypography.current
    val configuration: ConfigurationC
       @Composable
        @ReadOnlyComposable
        get() = LocalAppConfiguration.current
}

private val LocalAppColors = staticCompositionLocalOf {
    lightAppColors
}

private val LocalAppTypography = staticCompositionLocalOf {
    appTypography
}

private val LocalAppConfiguration = staticCompositionLocalOf {
    ConfigurationC()
}

@Composable
fun AppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit,
) {
    val colors = if (darkTheme) darkAppColors else lightAppColors
    CompositionLocalProvider(
        LocalAppConfiguration provides ConfigurationC.init(LocalContext.current),
        LocalAppColors provides colors,
        LocalAppTypography provides typography,
    ) {
        MaterialTheme(
            colors = colors.materialColors,
            typography = typography.materialTypography,
            content = content,
        )
    }
}

A simple Preview:

@Composable
@Preview(name = "light", showBackground = true)
@Preview(name = "dark", showBackground = true, uiMode = UI_MODE_NIGHT_YES)
fun EnabledPreview() {

    AppTheme {

        Button.MyCustomButton(
            modifier = Modifier,
            title = "Custom Button",
            font = AppTheme.configuration.font.h1
            color = AppTheme.configuration.colors.text1
            enabled = enabled,
            onClick = {}
        )
    
    }
}
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
Thomas Cirksena
  • 717
  • 2
  • 8
  • 28

1 Answers1

0

I also encountered this problem while working on a White Label Solution project. You can solve it very elegantly with PreviewParameter

Define the ColorsProvider class:

class ColorsProvider: PreviewParameterProvider<AppColors> {
    override val values: Sequence<AppColors> = sequenceOf(
        darkAppColors,
        lightAppColors,
        // add more color options here if needed
}
@Preview(showBackground = true)
@Composable
fun GreetingTextPreview(
    @PreviewParameter(ColorsProvider::class) colors: AppColors
) {
    AppTheme(colors){}
}

Now you can use this parameter in any of your previews in this way:

@Composable
fun ComponentPreview(
    @PreviewParameter(ColorsProvider::class) colors: AppColors,
) {
    AppTheme(colors = colors) {
        // your preview content
    }
}
tasjapr
  • 632
  • 4
  • 13