DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Compose ↔ View Interop — AndroidView and ComposeView

Compose ↔ View Interop — AndroidView and ComposeView

You don't need to rewrite your entire app to use Compose. Modern Android supports gradual migration through interop.

AndroidView: Embedding Views in Compose

Basic Pattern

AndroidView(
    factory = { context ->
        MapView(context).apply {
            // Initialize
        }
    },
    modifier = Modifier.fillMaxSize(),
    update = { mapView ->
        // Update when recomposed
    }
)
Enter fullscreen mode Exit fullscreen mode

Real Example: Google Maps

AndroidView(
    factory = { context ->
        MapView(context)
    },
    update = { mapView ->
        mapView.getMapAsync { googleMap ->
            googleMap.addMarker(
                MarkerOptions().position(LatLng(37.7749, -122.4194))
            )
        }
    },
    modifier = Modifier.fillMaxSize()
)
Enter fullscreen mode Exit fullscreen mode

WebView

AndroidView(
    factory = { context ->
        WebView(context).apply {
            settings.javaScriptEnabled = true
            loadUrl("https://example.com")
        }
    }
)
Enter fullscreen mode Exit fullscreen mode

ComposeView: Embedding Compose in Views

In a Fragment

class LegacyFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return ComposeView(requireContext()).apply {
            setContent {
                MyComposeScreen()
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

In XML Layout

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbarcompat.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/compose_content"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
</LinearLayout>
Enter fullscreen mode Exit fullscreen mode
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val composeView = findViewById<ComposeView>(R.id.compose_content)
        composeView.setContent {
            MyComposeContent()
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

AndroidViewBinding: Type-Safe Access

AndroidView(
    factory = { context ->
        MapBinding.inflate(LayoutInflater.from(context)).root
    },
    update = { view ->
        val binding = MapBinding.bind(view)
        binding.mapView.getMapAsync { /* ... */ }
    }
)
Enter fullscreen mode Exit fullscreen mode

State Sharing: View → Compose

class SharedViewModel : ViewModel() {
    val location = MutableLiveData<LatLng>()
}

// In Compose Fragment
class MapsComposeFragment : Fragment() {
    private val viewModel: SharedViewModel by viewModels()

    override fun onCreateView(inflater: LayoutInflater, ...): View {
        return ComposeView(requireContext()).apply {
            setContent {
                val location by viewModel.location.observeAsState()
                ComposeMapScreen(location)
            }
        }
    }
}

// In View Fragment
class MapsViewFragment : Fragment() {
    private val viewModel: SharedViewModel by viewModels()

    override fun onCreateView(inflater: LayoutInflater, ...): View {
        val binding = MapBinding.inflate(inflater)
        binding.mapView.getMapAsync { googleMap ->
            googleMap.setOnCameraIdleListener {
                viewModel.location.value = googleMap.cameraPosition.target
            }
        }
        return binding.root
    }
}
Enter fullscreen mode Exit fullscreen mode

Migration Strategy

  1. Start with new screens in Compose
  2. Add Compose gradual inside existing screens using ComposeView
  3. Keep shared ViewModel for state
  4. Use AndroidView sparingly (only for complex views like maps)
  5. Migrate screens incrementally — don't wait for 100% completion

Summary

  • AndroidView: Legacy View → Compose (factory + update pattern)
  • ComposeView: Compose → XML layout or Fragment
  • State sharing: ViewModel + LiveData/StateFlow
  • Migration: Gradual adoption, mixing both frameworks simultaneously

Your app doesn't need to be 100% Compose to benefit. Start interop today.


Interested in mobile app development? Check out 8 Android app templates on Gumroad!

Top comments (0)