androidkotlinandroid-jetpack-composeandroid-livedata

java.lang.IllegalStateException: CompositionLocal LocalLifecycleOwner not present when observing LiveData as State


I am getting a IllegalStateException when observing a LiveData from view model as state within Activity onCreate. Its complaining about missing lifecycle owner, however as per my understanding observeAsState() should take lifecycle of its owner, in this case the activity.

Below is the crash log:

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:46)
    at androidx.compose.runtime.LazyValueHolder.readValue(ValueHolders.kt:48)
    at androidx.compose.runtime.CompositionLocalMapKt.read(CompositionLocalMap.kt:91)
    at androidx.compose.runtime.ComposerImpl.consume(Composer.kt:2366)
    at androidx.compose.runtime.livedata.LiveDataAdapterKt.observeAsState(LiveDataAdapter.kt:72)
    at com.example.testblockers.MainActivityKt.MyApp(MainActivity.kt:57)
    at com.example.testblockers.MainActivity$onCreate$1$1$1.invoke(MainActivity.kt:44)
    at com.example.testblockers.MainActivity$onCreate$1$1$1.invoke(MainActivity.kt:43)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
    at androidx.compose.material3.SurfaceKt$Surface$1.invoke(Surface.kt:129)
    at androidx.compose.material3.SurfaceKt$Surface$1.invoke(Surface.kt:113)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
    at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:362)
    at androidx.compose.material3.SurfaceKt.Surface-T9BRK9s(Surface.kt:110)
    at com.example.testblockers.MainActivity$onCreate$1$1.invoke(MainActivity.kt:43)
    at com.example.testblockers.MainActivity$onCreate$1$1.invoke(MainActivity.kt:42)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
    at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:362)
    at androidx.compose.material3.TextKt.ProvideTextStyle(Text.kt:261)
    at androidx.compose.material3.MaterialThemeKt$MaterialTheme$1.invoke(MaterialTheme.kt:81)
    at androidx.compose.material3.MaterialThemeKt$MaterialTheme$1.invoke(MaterialTheme.kt:80)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
    at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:362)
    at androidx.compose.material3.MaterialThemeKt.MaterialTheme(MaterialTheme.kt:73)
    at com.example.testblockers.ui.theme.ThemeKt.TestBlockersTheme(Theme.kt:65)
    at com.example.testblockers.MainActivity$onCreate$1.invoke(MainActivity.kt:42)
    at com.example.testblockers.MainActivity$onCreate$1.invoke(MainActivity.kt:41)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
    at androidx.compose.ui.platform.ComposeView.Content(ComposeView.android.kt:428)
    at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:252)
    at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:251)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
    at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:362)
    at androidx.compose.ui.platform.CompositionLocalsKt.ProvideCommonCompositionLocals(CompositionLocals.kt:186)

Here is the View Model:

class MainViewModel : ViewModel() {
    private val _counterLiveData = MutableLiveData<Int>()

    // Expose LiveData as immutable
    val counterLiveData: LiveData<Int>
        get() = _counterLiveData

    init {
        // Initialize LiveData with default value
        _counterLiveData.value = 0
    }

    fun incrementCounter() {
        _counterLiveData.value = (_counterLiveData.value ?: 0) + 1
    }
}

Here is the Activity where I am observing the Live Data:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            // Initialize ViewModel
            val viewModel: MainViewModel = viewModel()

            TestBlockersTheme {
                Surface(color = MaterialTheme.colorScheme.background) {
                    MyApp(viewModel)
                }
            }
        }
    }
}

@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyApp(viewModel: MainViewModel) {
    // Observe the counterLiveData from the ViewModel
    val counterState by viewModel.counterLiveData.observeAsState(initial = 0)

    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("Counter App") }
            )
        },
        content = {
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(16.dp),
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(
                    text = "Counter: $counterState",
                    style = MaterialTheme.typography.bodyMedium
                )
                Spacer(modifier = Modifier.height(16.dp))
                Button(onClick = { viewModel.incrementCounter() }) {
                    Text("Increment")
                }
            }
        }
    )
}

Below is the gradle dependencies:

dependencies {
    implementation("androidx.core:core-ktx:1.13.0")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
    implementation("androidx.activity:activity-compose:1.9.0")
    implementation(platform("androidx.compose:compose-bom:2023.03.00"))
    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.ui:ui-graphics")
    implementation("androidx.compose.ui:ui-tooling-preview")
    implementation("androidx.compose.material3:material3")
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.1.5")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
    androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
    androidTestImplementation("androidx.compose.ui:ui-test-junit4")
    debugImplementation("androidx.compose.ui:ui-tooling")
    debugImplementation("androidx.compose.ui:ui-test-manifest")
    // jsoup HTML parser library @ https://jsoup.org/
    implementation("org.jsoup:jsoup:1.17.2")
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0")
    implementation("androidx.compose.runtime:runtime-livedata:1.7.0-alpha07")
    implementation("androidx.lifecycle:lifecycle-runtime-compose:2.7.0")
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0")
}

Is anyone familiar with this issue?


Solution

  • You mix compose artifacts from different versions that are not compatible.

    Although you use the Compose BOM 2023.03.00, you explicitly specify version 1.7.0-alpha07 for androidx.compose.runtime:runtime-livedata. Just remove the version for runtime-livedata and it will fall back to the version determined by the BOM which will be 1.4.0 and your code will work again.