Add Jetpack Compose to your existing Android XML base and Navigation Component
25-Jun-2024 - Sophoun
As an Android developer, I love what's new in Android like, Jetpack libraries and Kotlin multiplatform.
For me, the Jetpack Compose is interesting and has a lot of benefits that it gives to us. But to get those benefits we have to write or restructure our project in different ways to support it.
But today I will show you how I integrate Jetpack Compose to my existing XML base project and using Navigation Component.
To use Jetpack Compose you need to add several dependencies to your app.
Inside your app/build.gradle
file add the dependencies below inside your dependencies
:
// Jetpack compose def composeBom = platform('androidx.compose:compose-bom:2024.06.00') implementation composeBom androidTestImplementation composeBom implementation "androidx.activity:activity-compose:1.9.0" // Material Design 3 implementation 'androidx.compose.material3:material3' // Android Studio Preview support implementation 'androidx.compose.ui:ui-tooling-preview' debugImplementation 'androidx.compose.ui:ui-tooling'/
2024.06.00
update to the latest one it should be fine.Still inside your app/build.gradle
, let enable the compose feature:
android { buildFeatures { compose true } }
After all these, you have Compose your project.
After all setup, you're ready to use compose. Just using ComposeView
in side your overridden onCreateView
the method like below:
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return ComposeView(requireContext()).apply { setContent { Scaffold { paddingValues -> Box( modifier = Modifier .padding(top = paddingValues.calculateTopPadding()) ) { Text(text = "Hello Compose") } } } } }
Doing so, seems to be correct right? But after you run the compiler will start yelling at you something like,
java.lang.IllegalStateException: ViewTreeLifecycleOwner not found from android.widget.FrameLayout{2c1459d V.E...... ......ID 0,0-1080,2340 #7f0a0378 app:id/nav_host_fragment} at androidx.compose.ui.platform.WindowRecomposer_androidKt.createLifecycleAwareWindowRecomposer(...
The error above shows you that, the nav_host_fragment
doesn't implement lifecycle aware.
To make this work you have to wrap your nav_host_fragment
with a layout that implements lifecycle aware like below.
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <com.example.widget.FrameLayoutLifecycle android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.fragment.app.FragmentContainerView android:id="@+id/nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent" app:defaultNavHost="true" /> </com.example.widget.FrameLayoutLifecycle> </layout>
Yeah, but not so fast.
As you see in the code above I used the FrameLayoutLifecycle
, it's a custom layout that we need to extend from the existing view and implement lifecycle.
package com.example.widget import android.content.Context import android.util.AttributeSet import android.widget.FrameLayout import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.setViewTreeLifecycleOwner import androidx.savedstate.SavedStateRegistry import androidx.savedstate.SavedStateRegistryController import androidx.savedstate.SavedStateRegistryOwner class FrameLayoutLifecycle : FrameLayout, LifecycleOwner, SavedStateRegistryOwner { constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super( context, attrs, defStyle ) private val lifecycleRegistry = LifecycleRegistry(this) private val savedStateRegistryController = SavedStateRegistryController.create(this) override val lifecycle: Lifecycle get() = lifecycleRegistry override val savedStateRegistry: SavedStateRegistry get() = savedStateRegistryController.savedStateRegistry override fun onAttachedToWindow() { super.onAttachedToWindow() setViewTreeLifecycleOwner(this) savedStateRegistryController.performAttach() savedStateRegistryController.performRestore(null) lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE) lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START) lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME) } override fun onDetachedFromWindow() { super.onDetachedFromWindow() lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE) lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP) } }
Above you have to implement LifecycleOwner
and SavedStateRegistryOwner
and create a lifecycle object to pass to it.
After that, you need to handle the lifecycle event in the onAttachedToWindow
method ON_CREATE
, ON_START
and ON_RESUME
also performAttach
to the state too.
Then in the onDetachedFromWindow
you must handle the event ON_PAUSE
and ON_STOP
to prevent the leaking.
After everything is set, you're ready to go.
The technology is moving so fast which makes your knowledge and project deprecate really quick too. So keep learning and share your knowledge to keep it up to date. :)
See you!!!