ComposePreference icon indicating copy to clipboard operation
ComposePreference copied to clipboard

rememberPreferenceState doesn't work with new androidx.lifecycle 2.8.0

Open mattiasbe opened this issue 1 year ago • 1 comments

Expected Behavior

Updating dependency from lifecycle 2.7.0 to 2.8.0 would hopefully "just work".

https://developer.android.com/jetpack/androidx/releases/lifecycle#version_28_2

Actual Behavior

As soon as a screen with preferences from this library is used with updated lifecycle dependency the app crashed with the following stack trace example 1 with direct usage of rememberPreferenceState:

 java.lang.IllegalStateException: CompositionLocal LocalLifecycleOwner not present
                                                                                                    	at androidx.lifecycle.compose.LocalLifecycleOwnerKt$LocalLifecycleOwner$1.invoke(LocalLifecycleOwner.kt:26)
                                                                                                    	at androidx.lifecycle.compose.LocalLifecycleOwnerKt$LocalLifecycleOwner$1.invoke(LocalLifecycleOwner.kt:25)
                                                                                                    	at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
                                                                                                    	at androidx.compose.runtime.LazyValueHolder.getCurrent(ValueHolders.kt:29)
                                                                                                    	at androidx.compose.runtime.LazyValueHolder.getValue(ValueHolders.kt:31)
                                                                                                    	at androidx.compose.runtime.CompositionLocalMapKt.read(CompositionLocalMap.kt:90)
                                                                                                    	at androidx.compose.runtime.ComposerImpl.consume(Composer.kt:2135)
                                                                                                    	at androidx.lifecycle.compose.FlowExtKt.collectAsStateWithLifecycle(FlowExt.kt:183)
                                                                                                    	at me.zhanghai.compose.preference.PreferenceStateKt.rememberPreferenceState(PreferenceState.kt:35)
...

Stack trace example 2 with indirect usage of rememberPreferenceState using a listPreference:

java.lang.IllegalStateException: CompositionLocal LocalLifecycleOwner not present
                                                                                                    	at androidx.lifecycle.compose.LocalLifecycleOwnerKt$LocalLifecycleOwner$1.invoke(LocalLifecycleOwner.kt:26)
                                                                                                    	at androidx.lifecycle.compose.LocalLifecycleOwnerKt$LocalLifecycleOwner$1.invoke(LocalLifecycleOwner.kt:25)
                                                                                                    	at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
                                                                                                    	at androidx.compose.runtime.LazyValueHolder.getCurrent(ValueHolders.kt:29)
                                                                                                    	at androidx.compose.runtime.LazyValueHolder.getValue(ValueHolders.kt:31)
                                                                                                    	at androidx.compose.runtime.CompositionLocalMapKt.read(CompositionLocalMap.kt:90)
                                                                                                    	at androidx.compose.runtime.ComposerImpl.consume(Composer.kt:2135)
                                                                                                    	at androidx.lifecycle.compose.FlowExtKt.collectAsStateWithLifecycle(FlowExt.kt:183)
                                                                                                    	at me.zhanghai.compose.preference.PreferenceStateKt.rememberPreferenceState(PreferenceState.kt:35)
                                                                                                    	at se.consat.telematics.travelito.ui.screens.ComposableSingletons$AppSettingsScreenKt$lambda-21$1$1$invoke$$inlined$listPreference$default$1.invoke(ListPreference.kt:283)
                                                                                                    	at se.consat.telematics.travelito.ui.screens.ComposableSingletons$AppSettingsScreenKt$lambda-21$1$1$invoke$$inlined$listPreference$default$1.invoke(ListPreference.kt:80)
                                                                                                    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:118)
                                                                                                    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                                                                                                    	at androidx.compose.foundation.lazy.LazyListIntervalContent$item$3.invoke(LazyListIntervalContent.kt:59)

Changing dependency back to 2.7.0 and it starts working fine again.

Steps to Reproduce the Problem

  1. Having in build.gradle.kts:
    val lifecycle_version = "2.7.0" // https://developer.android.com/jetpack/androidx/releases/lifecycle
    // ViewModel
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version")
    // ViewModel utilities for Compose
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version")
    // LiveData
    implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version")
    // Lifecycles only (without ViewModel or LiveData)
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version") //empty since 2.8.0+
    // Lifecycle utilities for Compose
    implementation("androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version")
    // Saved state module for ViewModel
    implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version")
    // Annotation processor
    //kapt("androidx.lifecycle:lifecycle-compiler:$lifecycle_version")
    // alternately - if using Java8, use the following instead of lifecycle-compiler
    implementation("androidx.lifecycle:lifecycle-common-java8:$lifecycle_version")
    // optional - helpers for implementing LifecycleOwner in a Service
    implementation("androidx.lifecycle:lifecycle-service:$lifecycle_version")
    // optional - ProcessLifecycleOwner provides a lifecycle for the whole application process
    implementation("androidx.lifecycle:lifecycle-process:$lifecycle_version")

Code example 1:

ProvidePreferenceLocals {
            Column(...) {
                val state: MutableState<Boolean?> =  rememberPreferenceState(SettingsKeys.KEY_FORCE_DARK_MODE, null)

Last line there gives the exception in example stack trace 1 above.

Code example 2:

@Composable
fun AppSettingsApp() {
    TravelitoTheme {
        ProvidePreferenceLocals(
            theme = preferenceTheme(categoryColor = MaterialTheme.colorScheme.primary)
        ) {
            AppSettingsScreen()
        }
    }
}

@Composable
fun AppSettingsScreen() {
Scaffold(...) {
LazyColumn(modifier = Modifier.fillMaxSize(), contentPadding = contentPadding) {
            listPreference(
                key = SettingsKeys.KEY_FORCE_DARK_MODE,
                icon = { Icon(ThemeIcons.DarkMode, "Theme") },
                defaultValue = null,
                values = listOf(true, false, null),//"Dark", "Light", "Follow System"),
                title = { Text(text = "Theme") },
                summary = { Text(text = stringResource(themeString(it as Boolean?))) },
                valueToText = { AnnotatedString(context.getString(themeString(it as Boolean?))) }
            )
..

  1. Change first line in build.gradle.kts version to 2.8.0

Specifications

  • Version:
  • Platform: Android 14, Google Pixel 7 Pro

mattiasbe avatar May 15 '24 11:05 mattiasbe

This is a version mismatch issue:

  • Compose 1.6 & Lifecycle 2.7: androidx.compose.ui.platform.LocalLifecycleOwner
  • Compose 1.7 & Lifecycle 2.8: androidx.lifecycle.compose.LocalLifecycleOwner

ComposePreference 1.0.0 depends on Lifecycle 2.7

SanmerDev avatar May 16 '24 09:05 SanmerDev

See https://stackoverflow.com/a/78490417 - latest stable Lifecycle (2.8.0) is incompatible with latest stable Compose. I can't depend on unstable Compose in library, so for now you'll need to downgrade Lifecycle on your side, or take one of the workarounds offered in the bug linked in that SO answer.

I'll keep this issue open for awareness, until we have a stable Compose version that's compatible with Lifecycle 2.8.0.

zhanghai avatar May 18 '24 22:05 zhanghai

Lifecycle 2.8.2 can be used with any version of Compose now, so closing this issue.

zhanghai avatar Jun 25 '24 08:06 zhanghai