androidx.compose.ui.viewinterop

In this page, you'll find documentation for types, properties, and functions available in the androidx.compose.ui.viewinterop package such as AndroidView.

If you're looking for guidance instead, check out the following Compose guides:

Classes

InteropView

A typealias for the platform's built-in View type, which may be hosted inside of a Compose UI hierarchy to allow for interoperability.

Cmn
android

Type aliases

InteropView
android

Top-level functions summary

Unit
@Composable
@UiComposable
<T : View> AndroidView(
    factory: (Context) -> T,
    modifier: Modifier,
    update: (T) -> Unit
)

Composes an Android View obtained from factory.

android
Unit
@Composable
@UiComposable
<T : View> AndroidView(
    factory: (Context) -> T,
    modifier: Modifier,
    onReset: ((T) -> Unit)?,
    onRelease: (T) -> Unit,
    update: (T) -> Unit
)

Composes an Android View obtained from factory.

android
Unit
@Composable
<T : ViewBinding> AndroidViewBinding(
    factory: (inflater: LayoutInflater, parent: ViewGroup, attachToParent: Boolean) -> T,
    modifier: Modifier,
    update: T.() -> Unit
)

Composes an Android layout resource in the presence of ViewBinding.

android
Unit
@Composable
<T : ViewBinding> AndroidViewBinding(
    factory: (inflater: LayoutInflater, parent: ViewGroup, attachToParent: Boolean) -> T,
    modifier: Modifier,
    onReset: (T.() -> Unit)?,
    onRelease: T.() -> Unit,
    update: T.() -> Unit
)

Composes an Android layout resource in the presence of ViewBinding.

android

Top-level properties summary

View.() -> Unit

An empty update block used by AndroidView.

android

Top-level functions

AndroidView

@Composable
@UiComposable
fun <T : View> AndroidView(
    factory: (Context) -> T,
    modifier: Modifier = Modifier,
    update: (T) -> Unit = NoOpUpdate
): Unit

Composes an Android View obtained from factory. The factory block will be called exactly once to obtain the View being composed, and it is also guaranteed to be invoked on the UI thread. Therefore, in addition to creating the View, the factory block can also be used to perform one-off initializations and View constant properties' setting. The update block can run multiple times (on the UI thread as well) due to recomposition, and it is the right place to set the new properties. Note that the block will also run once right after the factory block completes.

AndroidView is commonly needed for using Views that are infeasible to be reimplemented in Compose and there is no corresponding Compose API. Common examples for the moment are WebView, SurfaceView, AdView, etc.

This overload of AndroidView does not automatically pool or reuse Views. If placed inside of a reusable container (including inside a LazyRow or LazyColumn), the View instances will always be discarded and recreated if the composition hierarchy containing the AndroidView changes, even if its group structure did not change and the View could have conceivably been reused.

To opt-in for View reuse, call the overload of AndroidView that accepts an onReset callback, and provide a non-null implementation for this callback. Since it is expensive to discard and recreate View instances, reusing Views can lead to noticeable performance improvements — especially when building a scrolling list of AndroidViews. It is highly recommended to opt-in to View reuse when possible.

AndroidView will not clip its content to the layout bounds. Use View.setClipToOutline on the child View to clip the contents, if desired. Developers will likely want to do this with all subclasses of SurfaceView to keep its contents contained.

AndroidView has nested scroll interop capabilities if the containing view has nested scroll enabled. This means this Composable can dispatch scroll deltas if it is placed inside a container that participates in nested scroll. For more information on how to enable nested scroll interop:

import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.rememberScrollableState
import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.ViewCompat

Box(
    Modifier.fillMaxSize()
        .scrollable(
            rememberScrollableState {
                // view world deltas should be reflected in compose world
                // components that participate in nested scrolling
                it
            },
            Orientation.Vertical
        )
) {
    AndroidView({ context ->
        LayoutInflater.from(context).inflate(android.R.layout.activity_list_item, null).apply {
            // Nested Scroll Interop will be Enabled when
            // nested scroll is enabled for the root view
            ViewCompat.setNestedScrollingEnabled(this, true)
        }
    })
}
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.viewinterop.AndroidView

// Compose a TextView.
AndroidView({ context -> TextView(context).apply { text = "This is a TextView" } })
// Compose a View and update its size based on state. Note the modifiers.
var size by remember { mutableStateOf(20) }
AndroidView(::View, Modifier.clickable { size += 20 }.background(Color.Blue)) { view ->
    view.layoutParams = ViewGroup.LayoutParams(size, size)
}
Parameters
factory: (Context) -> T

The block creating the View to be composed.

modifier: Modifier = Modifier

The modifier to be applied to the layout.

update: (T) -> Unit = NoOpUpdate

A callback to be invoked after the layout is inflated and upon recomposition to update the information and state of the view.

AndroidView

@Composable
@UiComposable
fun <T : View> AndroidView(
    factory: (Context) -> T,
    modifier: Modifier = Modifier,
    onReset: ((T) -> Unit)? = null,
    onRelease: (T) -> Unit = NoOpUpdate,
    update: (T) -> Unit = NoOpUpdate
): Unit

Composes an Android View obtained from factory. The factory block will be called exactly once to obtain the View being composed, and it is also guaranteed to be invoked on the UI thread. Therefore, in addition to creating the View, the factory block can also be used to perform one-off initializations and View constant properties' setting. The update block can run multiple times (on the UI thread as well) due to recomposition, and it is the right place to set the new properties. Note that the block will also run once right after the factory block completes.

AndroidView is commonly needed for using Views that are infeasible to be reimplemented in Compose and there is no corresponding Compose API. Common examples for the moment are WebView, SurfaceView, AdView, etc.

By default, AndroidView does not automatically pool or reuse Views. If placed inside of a reusable container (including inside a LazyRow or LazyColumn), the View instances will always be discarded and recreated if the composition hierarchy containing the AndroidView changes, even if its group structure did not change and the View could have conceivably been reused.

Views are eligible for reuse if AndroidView is given a non-null onReset callback. Since it is expensive to discard and recreate View instances, reusing Views can lead to noticeable performance improvements — especially when building a scrolling list of AndroidViews. It is highly recommended to specify an onReset implementation and opt-in to View reuse when possible.

When onReset is specified, View instances may be reused when hosted inside of a container that supports reusable elements. Reuse occurs when compatible instances of AndroidView are inserted and removed during recomposition. Two instances of AndroidView are considered compatible if they are invoked with the same composable group structure. The most common scenario where this happens is in lazy layout APIs like LazyRow and LazyColumn, which can reuse layout nodes (and Views, in this case) between items when scrolling.

onReset is invoked on the UI thread when the View will be reused, signaling that the View should be prepared to appear in a new context in the composition hierarchy. This callback is invoked before update and may be used to reset any transient View state like animations or user input.

Note that onReset may not be immediately followed by a call to update. Compose may temporarily detach the View from the composition hierarchy if it is deactivated but not released from composition. This can happen if the View appears in a ReusableContentHost that is not currently active or inside of a movable content block that is being moved. If this happens, the View will be removed from its parent, but retained by Compose so that it may be reused if its content host becomes active again. If the View never becomes active again and is instead discarded entirely, the onReset callback will be invoked directly from this deactivated state when Compose releases the View.

If you need to observe whether the View is currently used in the composition hierarchy, you may observe whether it is attached via View.addOnAttachStateChangeListener. The View may also observe the lifecycle of its host via findViewTreeLifecycleOwner. The lifecycle returned by this function will match the LocalLifecycleOwner. Note that the lifecycle is not set and cannot be used until the View is attached.

When the View is removed from the composition permanently, onRelease will be invoked (also on the UI thread). Once this callback returns, Compose will never attempt to reuse the previous View instance regardless of whether an onReset implementation was provided. If the View is needed again in the future, a new instance will be created, with a fresh lifecycle that begins by calling the factory.

AndroidView will not clip its content to the layout bounds. Use View.setClipToOutline on the child View to clip the contents, if desired. Developers will likely want to do this with all subclasses of SurfaceView to keep its contents contained.

AndroidView has nested scroll interop capabilities if the containing view has nested scroll enabled. This means this Composable can dispatch scroll deltas if it is placed inside a container that participates in nested scroll. For more information on how to enable nested scroll interop:

import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.rememberScrollableState
import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.ViewCompat

Box(
    Modifier.fillMaxSize()
        .scrollable(
            rememberScrollableState {
                // view world deltas should be reflected in compose world
                // components that participate in nested scrolling
                it
            },
            Orientation.Vertical
        )
) {
    AndroidView({ context ->
        LayoutInflater.from(context).inflate(android.R.layout.activity_list_item, null).apply {
            // Nested Scroll Interop will be Enabled when
            // nested scroll is enabled for the root view
            ViewCompat.setNestedScrollingEnabled(this, true)
        }
    })
}
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.viewinterop.AndroidView

// Compose a TextView.
AndroidView({ context -> TextView(context).apply { text = "This is a TextView" } })
// Compose a View and update its size based on state. Note the modifiers.
var size by remember { mutableStateOf(20) }
AndroidView(::View, Modifier.clickable { size += 20 }.background(Color.Blue)) { view ->
    view.layoutParams = ViewGroup.LayoutParams(size, size)
}
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView

val urls =
    listOf(
        "https://developer.android.com/jetpack/compose",
        "https://google.github.io/accompanist/",
        "https://android-developers.googleblog.com/",
        "https://io.google/",
        // ...
    )
LazyVerticalGrid(columns = GridCells.Adaptive(512.dp)) {
    items(urls) { url ->
        AndroidView(
            factory = { context ->
                WebView(context).apply {
                    settings.javaScriptEnabled = true
                    webViewClient =
                        object : WebViewClient() {
                            // Optional overrides for WebViewClient
                        }
                }
            },
            modifier = Modifier.fillMaxWidth().aspectRatio(1f),
            update = { webView -> webView.loadUrl(url) },
            onReset = { webView ->
                webView.stopLoading()
                webView.loadUrl("about:blank")
                webView.clearHistory()
            }
        )
    }
}
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner

// Compose a View that needs to be cleaned up when removed from the UI
class LifecycleAwareView(context: Context) : View(context) {
    var lifecycle: Lifecycle? = null
        set(value) {
            field?.removeObserver(observer)
            value?.addObserver(observer)
            field = value
        }

    private val observer = LifecycleEventObserver { source, event ->
        // React to the event
    }
}
val lifecycle = LocalLifecycleOwner.current.lifecycle
AndroidView(
    factory = { context -> LifecycleAwareView(context) },
    update = { view -> view.lifecycle = lifecycle },
    onRelease = { view ->
        // Need to release the lifecycle to prevent a memory leak
        view.lifecycle = null
    }
)
Parameters
factory: (Context) -> T

The block creating the View to be composed.

modifier: Modifier = Modifier

The modifier to be applied to the layout.

onReset: ((T) -> Unit)? = null

A callback invoked as a signal that the view is about to be attached to the composition hierarchy in a different context than its original creation. This callback is invoked before update and should prepare the view for general reuse. If null or not specified, the AndroidView instance will not support reuse, and the View instance will always be discarded whenever the AndroidView is moved or removed from the composition hierarchy.

onRelease: (T) -> Unit = NoOpUpdate

A callback invoked as a signal that this view instance has exited the composition hierarchy entirely and will not be reused again. Any additional resources used by the View should be freed at this time.

update: (T) -> Unit = NoOpUpdate

A callback to be invoked after the layout is inflated and upon recomposition to update the information and state of the view.

AndroidViewBinding

@Composable
fun <T : ViewBinding> AndroidViewBinding(
    factory: (inflater: LayoutInflater, parent: ViewGroup, attachToParent: Boolean) -> T,
    modifier: Modifier = Modifier,
    update: T.() -> Unit = {}
): Unit

Composes an Android layout resource in the presence of ViewBinding. The binding is obtained from the factory block, which will be called exactly once to obtain the ViewBinding to be composed, and it is also guaranteed to be invoked on the UI thread. Therefore, in addition to creating the ViewBinding, the block can also be used to perform one-off initializations and View constant properties' setting. The update block can be run multiple times (on the UI thread as well) due to recomposition, and it is the right place to set View properties depending on state. When state changes, the block will be reexecuted to set the new properties. Note the block will also be ran once right after the factory block completes.

This overload of AndroidViewBinding does not automatically pool or reuse Views and their bindings. If placed inside of a reusable container (including inside a LazyRow or LazyColumn), the View instances and their bindings will always be discarded and recreated if the composition hierarchy containing the AndroidViewBinding changes, even if its group structure did not change and the View and its binding could have conceivably been reused.

To opt-in for View reuse, call the overload of AndroidViewBinding that accepts an onReset callback, and provide a non-null implementation for this callback. Since it is expensive to discard and recreate View instances, reusing Views can lead to noticeable performance improvements — especially when building a scrolling list of AndroidView. It is highly recommended to opt-in to View reuse when possible.

There is generally no need to opt-in for reuse when using an AndroidViewBinding to host a Fragment, since Fragments have their own view lifecycles and do not usually appear in contexts where the reuse offered by AndroidViewBinding would lead to measurable performance improvements.

import androidx.compose.ui.viewbinding.samples.databinding.SampleLayoutBinding
import androidx.compose.ui.viewinterop.AndroidViewBinding

// Inflates and composes sample_layout.xml and changes the color of the `second` View.
// The `second` View is part of sample_layout.xml.
AndroidViewBinding(SampleLayoutBinding::inflate) { second.setBackgroundColor(Color.GRAY) }
Parameters
factory: (inflater: LayoutInflater, parent: ViewGroup, attachToParent: Boolean) -> T

The block creating the ViewBinding to be composed.

modifier: Modifier = Modifier

The modifier to be applied to the layout.

update: T.() -> Unit = {}

The callback to be invoked after the layout is inflated and upon recomposition to update the information and state of the binding

AndroidViewBinding

@Composable
fun <T : ViewBinding> AndroidViewBinding(
    factory: (inflater: LayoutInflater, parent: ViewGroup, attachToParent: Boolean) -> T,
    modifier: Modifier = Modifier,
    onReset: (T.() -> Unit)? = null,
    onRelease: T.() -> Unit = {},
    update: T.() -> Unit = {}
): Unit

Composes an Android layout resource in the presence of ViewBinding. The binding is obtained from the factory block, which will be called exactly once to obtain the ViewBinding to be composed, and it is also guaranteed to be invoked on the UI thread. Therefore, in addition to creating the ViewBinding, the block can also be used to perform one-off initializations and View constant properties' setting. The update block can be run multiple times (on the UI thread as well) due to recomposition, and it is the right place to set View properties depending on state. When state changes, the block will be reexecuted to set the new properties. Note the block will also be ran once right after the factory block completes.

This overload includes support for View reuse, which behaves in the same way as it does for AndroidView. Namely, Views and their binding instances are only eligible for reuse if a non-null onReset callback is provided. It is expensive to discard and recreate View instances, so opting-in to View reuse can lead to noticeable performance improvements — especially when AndroidViewBinding is used in a scrolling list. It is highly recommended to specify an onReset implementation and opt-in to View reuse when possible.

When onReset is specified, View instances and their bindings may be reused when hosted inside of a container that supports reusable elements. Reuse occurs when compatible instances of AndroidViewBinding are inserted and removed during recomposition. Two instances of AndroidViewBinding are considered compatible if they are invoked with the same composable group structure. The most common scenario where this happens is in lazy layout APIs like LazyRow and LazyColumn, which can reuse layout nodes (and, in this case, Views and their bindings as well) between items when scrolling.

onReset is invoked on the UI thread when the View and its binding will be reused, signaling that the View and binding should be prepared to appear in a new context in the composition hierarchy. This callback is invoked before update and may be used to reset any transient View state like animations or user input.

Note that onReset may not be immediately followed by a call to update. Compose may temporarily detach the View from the composition hierarchy if it is deactivated but not released from composition. This can happen if the View appears in a ReusableContentHost that is not currently active or inside of a movable content block that is being moved. If this happens, the View will be removed from its parent, but retained by Compose so that it may be reused if its content host becomes active again. If the View never becomes active again and is instead discarded entirely, the onReset callback will be invoked directly from this deactivated state when Compose releases the View and its binding.

When the View is removed from the composition permanently, onRelease will be invoked (also on the UI thread). Once this callback returns, Compose will never attempt to reuse the previous View or binding instance regardless of whether an onReset implementation was provided. If the View is needed again in the future, a new instance will be created, with a fresh lifecycle that begins by calling the factory.

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewbinding.samples.databinding.SampleButtonLayoutBinding
import androidx.compose.ui.viewinterop.AndroidViewBinding

@Composable
fun MyButton(label: String, action: () -> Unit, modifier: Modifier = Modifier) {
    AndroidViewBinding(
        SampleButtonLayoutBinding::inflate,
        modifier = modifier,
        onReset = {
            // Null out the OnClickListener to avoid leaking the `action` lambda.
            myButton.setOnClickListener(null)
        },
        update = {
            myButton.text = label
            myButton.setOnClickListener { action() }
        }
    )
}
Parameters
factory: (inflater: LayoutInflater, parent: ViewGroup, attachToParent: Boolean) -> T

The block creating the ViewBinding to be composed.

modifier: Modifier = Modifier

The modifier to be applied to the layout.

onReset: (T.() -> Unit)? = null

A callback invoked as a signal that the view is about to be attached to the composition hierarchy in a different context than its original creation. This callback is invoked before update and should prepare the view for general reuse. If null or not specified, the AndroidViewBinding instance will not support reuse, and the View and its binding will always be discarded whenever the AndroidViewBinding is moved or removed from the composition hierarchy.

onRelease: T.() -> Unit = {}

A callback invoked as a signal that this view and its binding have exited the composition hierarchy entirely and will not be reused again. Any additional resources used by the binding should be freed at this time.

update: T.() -> Unit = {}

The callback to be invoked after the layout is inflated and upon recomposition to update the information and state of the binding.

Top-level properties

NoOpUpdate

val NoOpUpdateView.() -> Unit

An empty update block used by AndroidView.