CoordinatorLayout
is a ViewGroup
that enables complex, overlapping, and nested layouts. It's used as a container to enable specific Material Design interactions, such as expanding/collapsing toolbars and bottom sheets, for Views contained within it.
In Compose, the closest equivalent of a CoordinatorLayout
is a Scaffold
. A Scaffold
provides content slots for combining Material Components into common screen patterns and interactions. This page describes how you can migrate your CoordinatorLayout
implementation to use Scaffold
in Compose.
Migration steps
To migrate CoordinatorLayout
to Scaffold
, follow these steps:
In the snippet below, the
CoordinatorLayout
contains anAppBarLayout
for containing aToolBar
, aViewPager
, and aFloatingActionButton
. Comment out theCoordinatorLayout
and its children from your UI hierarchy and add aComposeView
to replace it.<!-- <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" />
In your Fragment or Activity, obtain a reference to the
ComposeView
you just added and call thesetContent
method on it. In the body of the method, set aScaffold
as its content:composeView.setContent { Scaffold(Modifier.fillMaxSize()) { contentPadding -> // Scaffold contents // ... } }
In the content of your
Scaffold
, add your screen's primary content within it. Because the primary content in the XML above is aViewPager2
, we'll use aHorizontalPager
, which is the Compose equivalent of it. Thecontent
lambda of theScaffold
also receives an instance ofPaddingValues
that should be applied to the content root. You can useModifier.padding
to apply the samePaddingValues
to theHorizontalPager
.composeView.setContent { Scaffold(Modifier.fillMaxSize()) { contentPadding -> val pagerState = rememberPagerState { 10 } HorizontalPager( state = pagerState, modifier = Modifier.padding(contentPadding) ) { /* Page contents */ } } }
Use other content slots that
Scaffold
provides to add more screen elements and migrate remaining child Views. You can use thetopBar
slot to add aTopAppBar
, and thefloatingActionButton
slot to provide aFloatingActionButton
.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 */ } } }
Common use cases
Collapse and expand toolbars
In the View system, to collapse and expand the toolbar with CoordinatorLayout
, you use an AppBarLayout
as a container for the toolbar. You can then specify a Behavior
through layout_behavior
in XML on the associated scrollable View (like RecyclerView
or NestedScrollView
) to declare how the toolbar collapses/expands as you scroll.
In Compose, you can achieve a similar effect through a TopAppBarScrollBehavior
. For example, to implement a collapsing/expanding toolbar so that the toolbar appears when you scroll up, follow these steps:
- Call
TopAppBarDefaults.enterAlwaysScrollBehavior()
to create aTopAppBarScrollBehavior
. - Provide the created
TopAppBarScrollBehavior
to theTopAppBar
. Connect the
NestedScrollConnection
viaModifier.nestedScroll
on theScaffold
so that the Scaffold can receive nested scroll events as the scrollable content scrolls up/down. This way, the contained app bar can appropriately collapse/expand as the content scrolls.// 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 */ // ... }
Customize the collapsing/expanding scroll effect
You can provide several parameters for enterAlwaysScrollBehavior
to customize the collapsing/expanding animation effect. TopAppBarDefaults
also provides other TopAppBarScrollBehavior
such as exitUntilCollapsedScrollBehavior
, which only expands the app bar when the content is scrolled all the way down.
To create a completely custom effect (for example, a parallax effect), you can also create your own NestedScrollConnection
and offset the toolbar manually as the content scrolls. See the Nested scroll sample on AOSP for a code example.
Drawers
With Views, you implement a navigation drawer by using a DrawerLayout
as the root view. In turn, your CoordinatorLayout
is a child view of the DrawerLayout
. The DrawerLayout
also contains another child view, such as a NavigationView
, to display the navigation options in the drawer.
In Compose, you can implement a navigation drawer using the ModalNavigationDrawer
composable. ModalNavigationDrawer
offers a drawerContent
slot for the drawer and a content
slot for the screen's content.
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 // ... } }
See Drawers to learn more.
Snackbars
Scaffold
provides a snackbarHost
slot, which can accept a SnackbarHost
composable to display a 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 // ... }
See Snackbars to learn more.
Learn more
For more information about migrating a CoordinatorLayout
to Compose, see the following resources:
- Material Components and layouts: Documentation on Material Design components that are supported in Compose, like
Scaffold
. - Migrating Sunflower to Jetpack Compose: A blog post that documents the migration journey from Views to Compose of the Sunflower sample app, which contains a
CoordinatorLayout
.
Recommended for you
- Note: link text is displayed when JavaScript is off
- Material Components and layouts
- Window insets in Compose
- Scroll