Ensure that all the ModalBottomSheet can scroll.

This commit is contained in:
Benoit Marty 2026-04-27 10:23:56 +02:00
parent e51bd9bba6
commit babddd167b
21 changed files with 88 additions and 22 deletions

View file

@ -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,

View file

@ -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 = {

View file

@ -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(

View file

@ -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

View file

@ -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(

View file

@ -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,

View file

@ -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),

View file

@ -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) },

View file

@ -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(

View file

@ -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)
} }

View file

@ -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,

View file

@ -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),

View file

@ -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),

View file

@ -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(

View file

@ -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,

View file

@ -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",

View file

@ -67,6 +67,7 @@ fun AvatarActionBottomSheet(
}, },
modifier = modifier, modifier = modifier,
sheetState = sheetState, sheetState = sheetState,
scrollable = false,
) { ) {
AvatarActionBottomSheetContent( AvatarActionBottomSheetContent(
actions = actions, actions = actions,

View file

@ -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(
CreateDmConfirmationBottomSheetStateProvider::class @PreviewParameter(
) state: CreateDmConfirmationBottomSheetState) = ElementPreview { CreateDmConfirmationBottomSheetStateProvider::class
) state: CreateDmConfirmationBottomSheetState
) = ElementPreview {
CreateDmConfirmationBottomSheet( CreateDmConfirmationBottomSheet(
matrixUser = state.matrixUser, matrixUser = state.matrixUser,
isUserIdentityUnknown = state.isUserIdentityUnknown, isUserIdentityUnknown = state.isUserIdentityUnknown,
@ -166,7 +172,7 @@ data class CreateDmConfirmationBottomSheetState(
class CreateDmConfirmationBottomSheetStateProvider : PreviewParameterProvider<CreateDmConfirmationBottomSheetState> { class CreateDmConfirmationBottomSheetStateProvider : PreviewParameterProvider<CreateDmConfirmationBottomSheetState> {
override val values = sequenceOf( override val values = sequenceOf(
CreateDmConfirmationBottomSheetState(matrixUser = aMatrixUser(), isUserIdentityUnknown = false), CreateDmConfirmationBottomSheetState(matrixUser = aMatrixUser(), isUserIdentityUnknown = false),
CreateDmConfirmationBottomSheetState(matrixUser = aMatrixUser(), isUserIdentityUnknown = true), CreateDmConfirmationBottomSheetState(matrixUser = aMatrixUser(), isUserIdentityUnknown = true),
) )
} }

View file

@ -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

View file

@ -72,6 +72,7 @@ fun MediaDetailsBottomSheet(
ModalBottomSheet( ModalBottomSheet(
modifier = modifier, modifier = modifier,
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
scrollable = false,
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier

View file

@ -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),
) { ) {