As an Android developer, I appreciate new advancements like Jetpack libraries and Kotlin Multiplatform.
For me, Jetpack Compose is interesting and offers many benefits. However, to leverage these benefits, we often need to write or restructure our projects differently to support it.
Today, I will show you how I integrate Jetpack Compose into my existing XML-based project, utilizing the Navigation Component.
To use Jetpack Compose you need to add several dependencies to your app.
Inside your app/build.gradle file, add the following dependencies within your dependencies block:
kotlin// 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. Updating to the latest version should be fine.Still within your app/build.gradle file, enable the Compose feature:
kotlinandroid { buildFeatures { compose true } }
After these steps, you will have Compose enabled in your project.
After all the setup, you're ready to use Compose. Simply use ComposeView inside your overridden onCreateView method, as shown below:
kotlinoverride 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 correct, right? However, after you run it, the compiler will likely produce an error similar to:
kotlinjava.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 indicates that nav_host_fragment does not implement LifecycleOwner.
To make this work, you need to wrap your nav_host_fragment with a layout that implements LifecycleOwner, as shown below.
xml
However, there's a bit more to it.
As you can see in the code above, I used FrameLayoutLifecycle, which is a custom layout that extends an existing view and implements LifecycleOwner.
kotlinpackage 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 need to implement LifecycleOwner and SavedStateRegistryOwner and create a lifecycle object to pass to them.
After that, you need to handle the lifecycle events ON_CREATE, ON_START, and ON_RESUME in the onAttachedToWindow method, and also performAttach to the state.
Then, in onDetachedFromWindow, you must handle the ON_PAUSE and ON_STOP events to prevent memory leaks.
Once everything is set up, you're ready to go.
Technology is evolving rapidly, which can quickly make your knowledge and projects obsolete. Therefore, keep learning and sharing your knowledge to stay up-to-date. :)
See you!