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
CoordinatorLayoutcontains anAppBarLayoutfor containing aToolBar, aViewPager, and aFloatingActionButton. Comment out theCoordinatorLayoutand its children from your UI hierarchy and add aComposeViewto 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
ComposeViewyou just added and call thesetContentmethod on it. In the body of the method, set aScaffoldas 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. Thecontentlambda of theScaffoldalso receives an instance ofPaddingValuesthat should be applied to the content root. You can useModifier.paddingto apply the samePaddingValuesto 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
Scaffoldprovides to add more screen elements and migrate remaining child Views. You can use thetopBarslot to add aTopAppBar, and thefloatingActionButtonslot 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
TopAppBarScrollBehaviorto theTopAppBar. Connect the
NestedScrollConnectionviaModifier.nestedScrollon theScaffoldso 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