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