Sparround

Compose ↔ View interop & migration

Both worlds coexist in one app — migration is incremental:

  • Compose inside Views: drop a ComposeView into the layout and fill it via setContent { }. In fragments you must set ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed — otherwise the composition outlives the fragment's view and leaks.
  • Views inside Compose: AndroidView(factory = { context -> MapView(context) }, update = { view -> ... }) — for components with no Compose equivalent (maps, WebView, ad SDKs, custom charts).
  • Theme bridging: adapter approaches keep Material themes in sync across XML ↔ Compose; keeping design tokens in one source matters.

Migration strategy (official guidance): bottom-up — small components/new screens in Compose first, then whole screens, navigation last. New features are written in Compose; old screens migrate as they are touched.

kotlin
// 1) Fragment-də ComposeView (View → Compose)
override fun onCreateView(...): View =
    ComposeView(requireContext()).apply {
        setViewCompositionStrategy(
            ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
        )
        setContent {
            BankTheme {
                TransferScreen()
            }
        }
    }

// 2) Compose-da köhnə custom view (Compose → View)
@Composable
fun SignatureBox(onSigned: (Bitmap) -> Unit) {
    AndroidView(
        factory = { context ->
            SignatureView(context).apply { listener = onSigned }
        },
        update = { view -> view.clearIfNeeded() }
    )
}

Interop in both directions

The expected shape of the migration answer: (1) no big bang — incremental; (2) where to start — design-system components (buttons, inputs) and new screens; (3) the risky spots — ComposeView inside RecyclerView items (performance), composition strategies in fragments, keeping two theme systems in sync; (4) the team side — code review + pairing for those new to Compose. Your situation (XML fluent, learning Compose) is an asset here: "my learning path mirrors the migration path".

🛠 Practice task

Wire up interop in both directions:

  • Add a ComposeView to a fragment. Run it without a ViewCompositionStrategy first and check LeakCanary; then set DisposeOnViewTreeLifecycleDestroyed.
  • Show a legacy View inside a Compose screen via AndroidView (e.g. a WebView or a simple custom view) and log when the update lambda runs.

Done when: you have a phased plan ready for "how would you migrate a large XML codebase".

📚 Sources and documentation