Ensure that all the ModalBottomSheet can scroll.

This commit is contained in:
Benoit Marty
2026-04-27 10:23:56 +02:00
parent bf57223d05
commit 09ff3294d5
21 changed files with 88 additions and 22 deletions
@@ -95,7 +95,8 @@ internal fun SelectParentSpaceOptions(
sheetState.hide(coroutineScope) { sheetState.hide(coroutineScope) {
displaySelectSpaceBottomSheet = false displaySelectSpaceBottomSheet = false
} }
} },
scrollable = false,
) { ) {
SelectParentSpaceBottomSheet( SelectParentSpaceBottomSheet(
spaces = spaces, spaces = spaces,
@@ -11,6 +11,8 @@ package io.element.android.features.home.impl.roomlist
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -43,6 +45,7 @@ fun RoomListContextMenu(
) { ) {
ModalBottomSheet( ModalBottomSheet(
onDismissRequest = { eventSink(RoomListEvent.HideContextMenu) }, onDismissRequest = { eventSink(RoomListEvent.HideContextMenu) },
scrollable = false,
) { ) {
RoomListModalBottomSheetContent( RoomListModalBottomSheetContent(
contextMenu = contextMenu, contextMenu = contextMenu,
@@ -91,7 +94,9 @@ private fun RoomListModalBottomSheetContent(
onReportRoomClick: () -> Unit, onReportRoomClick: () -> Unit,
) { ) {
Column( Column(
modifier = Modifier.fillMaxWidth() modifier = Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState())
) { ) {
ListItem( ListItem(
headlineContent = { headlineContent = {
@@ -13,6 +13,8 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@@ -45,6 +47,7 @@ fun RoomListDeclineInviteMenu(
) { ) {
ModalBottomSheet( ModalBottomSheet(
onDismissRequest = { eventSink(RoomListEvent.HideDeclineInviteMenu) }, onDismissRequest = { eventSink(RoomListEvent.HideDeclineInviteMenu) },
scrollable = false,
) { ) {
RoomListDeclineInviteMenuContent( RoomListDeclineInviteMenuContent(
roomName = menu.roomSummary.name?.toSafeLength( roomName = menu.roomSummary.name?.toSafeLength(
@@ -81,7 +84,8 @@ private fun RoomListDeclineInviteMenuContent(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(all = 16.dp), .padding(all = 16.dp)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
Text( Text(
@@ -81,7 +81,8 @@ fun SpaceFiltersView(
if (state is SpaceFiltersState.Selecting) { if (state is SpaceFiltersState.Selecting) {
state.eventSink(SpaceFiltersEvent.Selecting.Cancel) state.eventSink(SpaceFiltersEvent.Selecting.Cancel)
} }
} },
scrollable = false,
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
@@ -262,6 +262,7 @@ private fun InvitePeopleConfirmModal(
ModalBottomSheet( ModalBottomSheet(
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
dragHandle = null, dragHandle = null,
scrollable = false,
) { ) {
IconTitleSubtitleMolecule( IconTitleSubtitleMolecule(
title = simplePluralStringResource( title = simplePluralStringResource(
@@ -156,6 +156,7 @@ fun ActionListView(
sheetState = sheetState, sheetState = sheetState,
onDismissRequest = ::onDismiss, onDismissRequest = ::onDismiss,
modifier = modifier, modifier = modifier,
scrollable = false,
) { ) {
ActionListViewContent( ActionListViewContent(
state = state, state = state,
@@ -75,6 +75,7 @@ fun ResolveVerifiedUserSendFailureView(
.navigationBarsPadding(), .navigationBarsPadding(),
sheetState = sheetState, sheetState = sheetState,
onDismissRequest = ::dismiss, onDismissRequest = ::dismiss,
scrollable = true,
) { ) {
IconTitleSubtitleMolecule( IconTitleSubtitleMolecule(
modifier = Modifier.padding(24.dp), modifier = Modifier.padding(24.dp),
@@ -13,6 +13,8 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -74,7 +76,8 @@ internal fun AttachmentsBottomSheet(
sheetState = rememberModalBottomSheetState( sheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true skipPartiallyExpanded = true
), ),
onDismissRequest = { isVisible = false } onDismissRequest = { isVisible = false },
scrollable = false,
) { ) {
AttachmentSourcePickerMenu( AttachmentSourcePickerMenu(
state = state, state = state,
@@ -97,6 +100,7 @@ private fun AttachmentSourcePickerMenu(
modifier = Modifier modifier = Modifier
.navigationBarsPadding() .navigationBarsPadding()
.imePadding() .imePadding()
.verticalScroll(rememberScrollState())
) { ) {
ListItem( ListItem(
modifier = Modifier.clickable { state.eventSink(MessageComposerEvent.PickAttachmentSource.PhotoFromCamera) }, modifier = Modifier.clickable { state.eventSink(MessageComposerEvent.PickAttachmentSource.PhotoFromCamera) },
@@ -50,7 +50,8 @@ fun CustomReactionBottomSheet(
ModalBottomSheet( ModalBottomSheet(
onDismissRequest = ::onDismiss, onDismissRequest = ::onDismiss,
sheetState = sheetState, sheetState = sheetState,
modifier = modifier modifier = modifier,
scrollable = false,
) { ) {
val presenter = remember { val presenter = remember {
EmojiPickerPresenter( EmojiPickerPresenter(
@@ -90,7 +90,8 @@ fun ReactionSummaryView(
if (state.target != null) { if (state.target != null) {
ModalBottomSheet( ModalBottomSheet(
onDismissRequest = ::onDismiss, onDismissRequest = ::onDismiss,
modifier = modifier modifier = modifier,
scrollable = false,
) { ) {
ReactionSummaryViewContent(summary = state.target) ReactionSummaryViewContent(summary = state.target)
} }
@@ -57,7 +57,8 @@ internal fun ReadReceiptBottomSheet(
sheetState.hide() sheetState.hide()
state.eventSink(ReadReceiptBottomSheetEvent.Dismiss) state.eventSink(ReadReceiptBottomSheetEvent.Dismiss)
} }
} },
scrollable = false,
) { ) {
ReadReceiptBottomSheetContent( ReadReceiptBottomSheetContent(
state = state, state = state,
@@ -153,6 +153,7 @@ private fun ChangeOwnRoleBottomSheet(
.navigationBarsPadding(), .navigationBarsPadding(),
sheetState = sheetState, sheetState = sheetState,
onDismissRequest = ::dismiss, onDismissRequest = ::dismiss,
scrollable = true,
) { ) {
Text( Text(
modifier = Modifier.padding(14.dp), modifier = Modifier.padding(14.dp),
@@ -17,6 +17,8 @@ import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -224,9 +226,12 @@ private fun RoomMemberActionsBottomSheet(
onDismiss() onDismiss()
} }
}, },
scrollable = false,
) { ) {
Column( Column(
modifier = Modifier.padding(vertical = 16.dp) modifier = Modifier
.padding(vertical = 16.dp)
.verticalScroll(rememberScrollState())
) { ) {
Avatar( Avatar(
avatarData = user.getAvatarData(size = AvatarSize.RoomListManageUser), avatarData = user.getAvatarData(size = AvatarSize.RoomListManageUser),
@@ -13,8 +13,10 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -52,11 +54,13 @@ fun JoinRoomByAddressView(
onDismissRequest = { onDismissRequest = {
state.eventSink(JoinRoomByAddressEvent.Dismiss) state.eventSink(JoinRoomByAddressEvent.Dismiss)
}, },
scrollable = false,
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(all = 16.dp), .padding(all = 16.dp)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
RoomAddressField( RoomAddressField(
@@ -14,6 +14,8 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -38,11 +40,13 @@ fun SimpleModalBottomSheet(
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
modifier = modifier, modifier = modifier,
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
scrollable = false,
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(16.dp), .padding(16.dp)
.verticalScroll(rememberScrollState()),
) { ) {
Text( Text(
title, title,
@@ -10,10 +10,13 @@ package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.BottomSheetDefaults import androidx.compose.material3.BottomSheetDefaults
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@@ -42,10 +45,15 @@ import io.element.android.libraries.designsystem.preview.sheetStateForPreview
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
/**
* For parameter [scrollable], set it to true if the content of the sheet does not already contain a scrollable component, such as a LazyColumn,
* to avoid nested scroll issues. In this case, the content will be wrapped in a Column with verticalScroll.
*/
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun ModalBottomSheet( fun ModalBottomSheet(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
scrollable: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
sheetState: SheetState = rememberModalBottomSheetState(), sheetState: SheetState = rememberModalBottomSheetState(),
shape: Shape = BottomSheetDefaults.ExpandedShape, shape: Shape = BottomSheetDefaults.ExpandedShape,
@@ -79,8 +87,17 @@ fun ModalBottomSheet(
scrimColor = scrimColor, scrimColor = scrimColor,
dragHandle = dragHandle, dragHandle = dragHandle,
contentWindowInsets = contentWindowInsets, contentWindowInsets = contentWindowInsets,
content = content, ) {
) if (scrollable) {
Column(
modifier = Modifier.verticalScroll(rememberScrollState()),
) {
content()
}
} else {
content()
}
}
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@@ -112,6 +129,7 @@ private fun ContentToPreview() {
) { ) {
ModalBottomSheet( ModalBottomSheet(
onDismissRequest = {}, onDismissRequest = {},
scrollable = false,
) { ) {
Text( Text(
text = "Sheet Content", text = "Sheet Content",
@@ -67,6 +67,7 @@ fun AvatarActionBottomSheet(
}, },
modifier = modifier, modifier = modifier,
sheetState = sheetState, sheetState = sheetState,
scrollable = false,
) { ) {
AvatarActionBottomSheetContent( AvatarActionBottomSheetContent(
actions = actions, actions = actions,
@@ -14,6 +14,8 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.material3.rememberModalBottomSheetState
@@ -74,11 +76,13 @@ fun CreateDmConfirmationBottomSheet(
modifier = modifier, modifier = modifier,
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
scrollable = false,
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(top = 24.dp, bottom = 16.dp, start = 16.dp, end = 16.dp), .padding(top = 24.dp, bottom = 16.dp, start = 16.dp, end = 16.dp)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
if (isUserIdentityUnknown) { if (isUserIdentityUnknown) {
@@ -148,9 +152,11 @@ fun CreateDmConfirmationBottomSheet(
@PreviewsDayNight @PreviewsDayNight
@Composable @Composable
internal fun CreateDmConfirmationBottomSheetPreview(@PreviewParameter( internal fun CreateDmConfirmationBottomSheetPreview(
@PreviewParameter(
CreateDmConfirmationBottomSheetStateProvider::class CreateDmConfirmationBottomSheetStateProvider::class
) state: CreateDmConfirmationBottomSheetState) = ElementPreview { ) state: CreateDmConfirmationBottomSheetState
) = ElementPreview {
CreateDmConfirmationBottomSheet( CreateDmConfirmationBottomSheet(
matrixUser = state.matrixUser, matrixUser = state.matrixUser,
isUserIdentityUnknown = state.isUserIdentityUnknown, isUserIdentityUnknown = state.isUserIdentityUnknown,
@@ -59,7 +59,8 @@ fun MediaDeleteConfirmationBottomSheet(
ModalBottomSheet( ModalBottomSheet(
modifier = modifier, modifier = modifier,
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
scrollable = false,
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
@@ -72,6 +72,7 @@ fun MediaDetailsBottomSheet(
ModalBottomSheet( ModalBottomSheet(
modifier = modifier, modifier = modifier,
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
scrollable = false,
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
@@ -12,6 +12,8 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -37,11 +39,13 @@ fun CaptionWarningBottomSheet(
ModalBottomSheet( ModalBottomSheet(
modifier = modifier, modifier = modifier,
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
scrollable = false,
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp), .padding(horizontal = 16.dp)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp),
) { ) {