I have implemented navigation using Android jetpack libraries and Material3 design, namely ModalNavigationDrawer
and ModalDrawerSheet
components. So far so good, however when rotating the screen, not all NavigationDrawerItem
s are visible, and scrolling is not enabled.
Shouldn't scrolling in ModalDrawerSheet
be always enabled? If not, how can I enable it?
val navigationItems = listOf(
MainNavigationScreen.Screen1,
MainNavigationScreen.Screen2,
MainNavigationScreen.Screen3,
...
)
val drawerState = rememberDrawerState(initialValue = DrawerValue.Open)
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
ModalDrawerSheet {
Spacer(Modifier.height(12.dp))
Image(
painter = painterResource(id = R.drawable.ic_top_bar_logo),
contentDescription = stringResource(id = R.string.app_name),
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.height(12.dp))
HorizontalDivider()
Spacer(Modifier.height(12.dp))
navigationItems.forEach {
NavigationDrawerItem(
label = { Text(stringResource(id = it.nameResourceId)) },
icon = {
Icon(painterResource(id = it.iconResourceId), null)
},
onClick = {},
selected = false,
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
)
}
Spacer(Modifier.height(12.dp))
}
}) {}
You can use the relevant scrolling modifiers (as documented in the scrolling docs - verticalScroll
or horizontalScroll
) on the ModalDrawerSheet
composable as you would normally:
ModalDrawerSheet(modifier = Modifier.verticalScroll(rememberScrollableState()) {
// Drawer content...
}
Alternatively, you can use the relevant lazy list composable (LazyColumn
or LazyRow
) as desired:
ModalDrawerSheet {
LazyColumn {
item { /* TODO */ }
items(...) { /* TODO */ }
}
}
Internally, the ModalDrawerSheet
composable uses a shared DrawerSheet
composable which then uses a Surface
consisting of a Column
:
@Composable
internal fun DrawerSheet(
drawerPredictiveBackState: DrawerPredictiveBackState?,
windowInsets: WindowInsets,
modifier: Modifier = Modifier,
drawerShape: Shape = RectangleShape,
drawerContainerColor: Color = DrawerDefaults.standardContainerColor,
drawerContentColor: Color = contentColorFor(drawerContainerColor),
drawerTonalElevation: Dp = DrawerDefaults.PermanentDrawerElevation,
drawerOffset: () -> Float = { 0F },
content: @Composable ColumnScope.() -> Unit
) {
val density = LocalDensity.current
val maxWidth = NavigationDrawerTokens.ContainerWidth
val maxWidthPx = with(density) { maxWidth.toPx() }
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
val predictiveBackDrawerContainerModifier =
if (drawerPredictiveBackState != null) {
Modifier.predictiveBackDrawerContainer(drawerPredictiveBackState, isRtl)
} else {
Modifier
}
Surface(
modifier =
modifier
.sizeIn(minWidth = MinimumDrawerWidth, maxWidth = maxWidth)
// Scale up the Surface horizontally in case the drawer offset it greater than zero.
// This is done to avoid showing a gap when the drawer opens and bounces when it's
// applied with a bouncy motion. Note that the content inside the Surface is scaled
// back down to maintain its aspect ratio (see below).
.horizontalScaleUp(
drawerOffset = drawerOffset,
drawerWidth = maxWidthPx,
isRtl = isRtl
)
.then(predictiveBackDrawerContainerModifier)
.fillMaxHeight(),
shape = drawerShape,
color = drawerContainerColor,
contentColor = drawerContentColor,
tonalElevation = drawerTonalElevation
) {
val predictiveBackDrawerChildModifier =
if (drawerPredictiveBackState != null)
Modifier.predictiveBackDrawerChild(drawerPredictiveBackState, isRtl)
else Modifier
Column(
Modifier.sizeIn(minWidth = MinimumDrawerWidth, maxWidth = maxWidth)
// Scale the content down in case the drawer offset is greater than one. The
// wrapping Surface is scaled up, so this is done to maintain the content's aspect
// ratio.
.horizontalScaleDown(
drawerOffset = drawerOffset,
drawerWidth = maxWidthPx,
isRtl = isRtl
)
.then(predictiveBackDrawerChildModifier)
.windowInsetsPadding(windowInsets),
content = content
)
}
}