Cómo migrar CoordinatorLayout a Compose

CoordinatorLayout es un ViewGroup que habilita funciones complejas, superpuestas y diseños anidados. Se usa como contenedor para habilitar recursos específicos de Material Design como las hojas inferiores y las barras de herramientas de expansión o contracción, para los objetos View que contiene.

En Compose, el equivalente más cercano de un CoordinatorLayout es un Scaffold. Un Scaffold proporciona espacios de contenido para combinar componentes de Material en patrones y interacciones de pantalla comunes. En esta página, se describe cómo puedes migrar tu implementación de CoordinatorLayout para usar Scaffold en Compose.

Pasos de la migración

Para migrar CoordinatorLayout a Scaffold, sigue estos pasos:

  1. En el siguiente fragmento, CoordinatorLayout contiene un AppBarLayout para que contiene un ToolBar, un ViewPager y un FloatingActionButton. Comentar el CoordinatorLayout y sus elementos secundarios de la jerarquía de la IU y agregar un ComposeView para reemplazarlo

    <!--  <androidx.coordinatorlayout.widget.CoordinatorLayout-->
    <!--      android:id="@+id/coordinator_layout"-->
    <!--      android:layout_width="match_parent"-->
    <!--      android:layout_height="match_parent"-->
    <!--      android:fitsSystemWindows="true">-->
    
    <!--    <androidx.compose.ui.platform.ComposeView-->
    <!--        android:id="@+id/compose_view"-->
    <!--        android:layout_width="match_parent"-->
    <!--        android:layout_height="match_parent"-->
    <!--        app:layout_behavior="@string/appbar_scrolling_view_behavior" />-->
    
    <!--    <com.google.android.material.appbar.AppBarLayout-->
    <!--        android:id="@+id/app_bar_layout"-->
    <!--        android:layout_width="match_parent"-->
    <!--        android:layout_height="wrap_content"-->
    <!--        android:fitsSystemWindows="true"-->
    <!--        android:theme="@style/Theme.Sunflower.AppBarOverlay">-->
    
        <!-- AppBarLayout contents here -->
    
    <!--    </com.google.android.material.appbar.AppBarLayout>-->
    
    <!--  </androidx.coordinatorlayout.widget.CoordinatorLayout>-->
    
    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/compose_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    
  2. En tu fragmento o actividad, obtén una referencia al ComposeView que acabas de agregar y llama al método setContent en él. En el cuerpo del método, establece un Scaffold como su contenido:

    composeView.setContent {
        Scaffold(Modifier.fillMaxSize()) { contentPadding ->
            // Scaffold contents
            // ...
        }
    }

  3. En el contenido de tu Scaffold, agrega el contenido principal de tu pantalla dentro que la modifica. Como el contenido principal del XML anterior es un ViewPager2, usaremos un HorizontalPager, que es el equivalente de Compose. La lambda content de Scaffold también recibe una instancia de PaddingValues que debería se aplican a la raíz del contenido. Puedes usar Modifier.padding para aplicar el mismo PaddingValues a HorizontalPager.

    composeView.setContent {
        Scaffold(Modifier.fillMaxSize()) { contentPadding ->
            val pagerState = rememberPagerState {
                10
            }
            HorizontalPager(
                state = pagerState,
                modifier = Modifier.padding(contentPadding)
            ) { /* Page contents */ }
        }
    }

  4. Usa otros espacios de contenido que proporciona Scaffold para agregar más elementos de pantalla. y migrar las vistas secundarias restantes. Puedes usar el espacio topBar para agregar una TopAppBar y el espacio floatingActionButton para proporcionar un FloatingActionButton.

    composeView.setContent {
        Scaffold(
            Modifier.fillMaxSize(),
            topBar = {
                TopAppBar(
                    title = {
                        Text("My App")
                    }
                )
            },
            floatingActionButton = {
                FloatingActionButton(
                    onClick = { /* Handle click */ }
                ) {
                    Icon(
                        Icons.Filled.Add,
                        contentDescription = "Add Button"
                    )
                }
            }
        ) { contentPadding ->
            val pagerState = rememberPagerState {
                10
            }
            HorizontalPager(
                state = pagerState,
                modifier = Modifier.padding(contentPadding)
            ) { /* Page contents */ }
        }
    }

Casos de uso comunes

Cómo contraer y expandir barras de herramientas

En el sistema de View, para contraer y expandir la barra de herramientas con CoordinatorLayout, haz lo siguiente: usa un AppBarLayout como contenedor de la barra de herramientas. Luego, puedes especificar De Behavior a layout_behavior en XML en el elemento desplazable asociado (como RecyclerView o NestedScrollView) para declarar cómo se muestra se contrae o se expande a medida que te desplazas.

En Compose, puedes lograr un efecto similar con un TopAppBarScrollBehavior. Por ejemplo, para implementar una interfaz de contracción o expansión barra de herramientas de modo que aparezca cuando te desplaces hacia arriba, sigue estos pasos:

  1. Llama a TopAppBarDefaults.enterAlwaysScrollBehavior() para crear un TopAppBarScrollBehavior
  2. Proporciona el TopAppBarScrollBehavior creado a TopAppBar.
  3. Conecta el NestedScrollConnection a través de Modifier.nestedScroll en el Scaffold para que Scaffold pueda recibir eventos de desplazamiento anidados como el elemento el contenido desplazable se desplaza hacia arriba o hacia abajo. De esta manera, la barra de la app contenida se contraen o expanden de manera adecuada a medida que se desplaza el contenido.

    // 1. Create the TopAppBarScrollBehavior
    val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
    
    Scaffold(
        topBar = {
            TopAppBar(
                title = {
                    Text("My App")
                },
                // 2. Provide scrollBehavior to TopAppBar
                scrollBehavior = scrollBehavior
            )
        },
        // 3. Connect the scrollBehavior.nestedScrollConnection to the Scaffold
        modifier = Modifier
            .fillMaxSize()
            .nestedScroll(scrollBehavior.nestedScrollConnection)
    ) { contentPadding ->
        /* Contents */
        // ...
    }

Cómo personalizar el efecto de desplazamiento de colapso o expansión

Puedes proporcionar varios parámetros para enterAlwaysScrollBehavior para lo siguiente: Personalizar el efecto de la animación que se contrae/expande. TopAppBarDefaults también proporciona otros TopAppBarScrollBehavior, como exitUntilCollapsedScrollBehavior, que solo expande la barra de la app cuando se desplaza todo el contenido hacia abajo.

Para crear un efecto completamente personalizado (por ejemplo, un efecto de paralaje), puedes También puedes crear tu propio NestedScrollConnection y desplazar la barra de herramientas manualmente como se desplaza el contenido. Consulta el ejemplo de desplazamiento anidado en AOSP para ver un ejemplo de código.

Paneles laterales

Con Views, implementas un panel lateral de navegación con un DrawerLayout como vista raíz. A su vez, tu CoordinatorLayout es una vista secundaria de DrawerLayout. DrawerLayout también contiene otra vista secundaria, como un NavigationView, para mostrar las opciones de navegación en el panel lateral.

En Compose, puedes implementar un panel lateral de navegación con el elemento componible ModalNavigationDrawer. ModalNavigationDrawer ofrece un Ranura drawerContent para el panel lateral y una ranura content para la pantalla contenido.

ModalNavigationDrawer(
    drawerContent = {
        ModalDrawerSheet {
            Text("Drawer title", modifier = Modifier.padding(16.dp))
            HorizontalDivider()
            NavigationDrawerItem(
                label = { Text(text = "Drawer Item") },
                selected = false,
                onClick = { /*TODO*/ }
            )
            // ...other drawer items
        }
    }
) {
    Scaffold(Modifier.fillMaxSize()) { contentPadding ->
        // Scaffold content
        // ...
    }
}

Consulta Paneles laterales para obtener más información.

Barras de notificaciones

Scaffold proporciona un espacio snackbarHost, que puede aceptar un elemento componible SnackbarHost para mostrar un Snackbar.

val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
Scaffold(
    snackbarHost = {
        SnackbarHost(hostState = snackbarHostState)
    },
    floatingActionButton = {
        ExtendedFloatingActionButton(
            text = { Text("Show snackbar") },
            icon = { Icon(Icons.Filled.Image, contentDescription = "") },
            onClick = {
                scope.launch {
                    snackbarHostState.showSnackbar("Snackbar")
                }
            }
        )
    }
) { contentPadding ->
    // Screen content
    // ...
}

Consulta Snackbars para obtener más información.

Más información

Si quieres obtener más información para migrar un objeto CoordinatorLayout a Compose, consulta el siguiente vínculo: los siguientes recursos: