Use the right insets in the home screen UI (#7271)

* Use the right insets in the home screen UI

That's `WindowInsets.statusBar` for the `Scaffold` component, then `WindowInsets.navigationBar` for the `LazyColum.contentPadding`

* Apply it to other screens with long lists of items too

* Apply the insets to `PreferencePage` too

* Apply inset changes to other screens with scrollable content

* Add helper insets/padding values based on review suggestions.

* Use the newly added insets/padding values in several scaffold and scrollable columns

---------

Co-authored-by: ElementBot <android@element.io>
This commit is contained in:
Jorge Martin Espinosa
2026-07-27 18:17:14 +02:00
committed by GitHub
parent 0442fbd307
commit 77edfec61a
31 changed files with 208 additions and 73 deletions
@@ -19,6 +19,7 @@ import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.plus
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
@@ -65,6 +66,8 @@ import io.element.android.libraries.designsystem.theme.components.HorizontalFloa
import io.element.android.libraries.designsystem.theme.components.HorizontalFloatingToolbarSeparator
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.utils.lazyColumnContentPadding
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.core.RoomId
@@ -234,7 +237,15 @@ private fun HomeScaffold(
)
},
floatingActionButtonPosition = FabPosition.Center,
contentWindowInsets = scaffoldScrollableContentInsets,
content = { padding ->
val outerPadding = PaddingValues(
start = padding.calculateStartPadding(LocalLayoutDirection.current),
end = padding.calculateEndPadding(LocalLayoutDirection.current),
// Remove these two lines once https://issuetracker.google.com/issues/436432313 has been fixed
bottom = padding.calculateBottomPadding(),
top = padding.calculateTopPadding()
)
val contentPadding = PaddingValues(
bottom = 96.dp,
)
@@ -251,18 +262,10 @@ private fun HomeScaffold(
onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick,
onRoomClick = ::onRoomClick,
onCreateRoomClick = onStartChatClick,
contentPadding = contentPadding,
contentPadding = lazyColumnContentPadding + contentPadding,
modifier = Modifier
.padding(
PaddingValues(
start = padding.calculateStartPadding(LocalLayoutDirection.current),
end = padding.calculateEndPadding(LocalLayoutDirection.current),
// Remove these two lines once https://issuetracker.google.com/issues/436432313 has been fixed
bottom = padding.calculateBottomPadding(),
top = padding.calculateTopPadding()
)
)
.consumeWindowInsets(padding)
.padding(outerPadding)
.consumeWindowInsets(outerPadding)
.hazeSource(state = hazeState)
)
SpaceFiltersView(roomListState.spaceFiltersState)
@@ -271,8 +274,8 @@ private fun HomeScaffold(
HomeSpacesView(
modifier = Modifier
.fillMaxSize()
.padding(padding)
.consumeWindowInsets(padding)
.padding(outerPadding)
.consumeWindowInsets(outerPadding)
.hazeSource(state = hazeState),
contentPadding = contentPadding,
state = state.homeSpacesState,
@@ -13,9 +13,15 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
@@ -153,7 +159,8 @@ private fun RoomListSearchContent(
}
},
)
}
},
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Bottom),
) { padding ->
Column(
modifier = Modifier
@@ -167,6 +174,7 @@ private fun RoomListSearchContent(
LazyColumn(
state = lazyListState,
modifier = Modifier.weight(1f),
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
) {
items(
items = state.results,
@@ -15,9 +15,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
@@ -45,6 +43,8 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet
import io.element.android.libraries.designsystem.theme.components.SearchField
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.lazyColumnContentPadding
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.matrix.api.spaces.SpaceServiceFilter
import io.element.android.libraries.matrix.ui.model.getAvatarData
import io.element.android.libraries.ui.strings.CommonStrings
@@ -75,9 +75,7 @@ fun SpaceFiltersView(
}
if (sheetState.isVisible || isSelecting) {
ModalBottomSheet(
modifier = modifier
.systemBarsPadding()
.navigationBarsPadding(),
modifier = modifier,
sheetState = sheetState,
onDismissRequest = {
if (state is SpaceFiltersState.Selecting) {
@@ -85,6 +83,7 @@ fun SpaceFiltersView(
}
},
scrollable = false,
contentWindowInsets = { scaffoldScrollableContentInsets },
) {
Box(
modifier = Modifier
@@ -113,7 +112,7 @@ private fun SpaceFiltersBottomSheetContent(
modifier: Modifier = Modifier
) {
Column(
modifier = modifier.padding(vertical = 16.dp)
modifier = modifier.padding(top = 16.dp)
) {
Text(
text = stringResource(R.string.screen_roomlist_your_spaces),
@@ -131,7 +130,9 @@ private fun SpaceFiltersBottomSheetContent(
placeholder = stringResource(CommonStrings.action_search),
)
Spacer(modifier = Modifier.height(16.dp))
LazyColumn {
LazyColumn(
contentPadding = lazyColumnContentPadding,
) {
items(filters) { filter ->
SpaceFilterItem(
filter = filter,
@@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.plus
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
@@ -73,6 +74,8 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.lazyColumnContentPadding
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.ImmutableList
@@ -94,7 +97,8 @@ fun KnockRequestsListView(
.padding(padding)
.consumeWindowInsets(padding),
)
}
},
contentWindowInsets = scaffoldScrollableContentInsets,
)
}
@@ -132,7 +136,7 @@ private fun KnockRequestsListContent(
onAcceptClick = ::onAcceptClick,
onDeclineClick = ::onDeclineClick,
onBanClick = ::onBanClick,
contentPadding = PaddingValues(bottom = bottomPaddingInPixels.toDp()),
contentPadding = lazyColumnContentPadding + PaddingValues(bottom = bottomPaddingInPixels.toDp()),
)
}
}
@@ -9,7 +9,7 @@
package io.element.android.features.licenses.impl.details
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.plus
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -25,6 +25,8 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.lazyColumnContentPadding
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -41,9 +43,10 @@ fun DependenciesDetailsView(
navigationIcon = { BackButton(onClick = onBack) },
)
},
contentWindowInsets = scaffoldScrollableContentInsets,
) { contentPadding ->
LazyColumn(
modifier = Modifier.padding(contentPadding),
contentPadding = contentPadding + lazyColumnContentPadding,
) {
val licenses = licenseItem.licenses.orEmpty() +
licenseItem.unknownLicenses.orEmpty()
@@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.plus
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -34,6 +35,8 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextField
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.lazyColumnContentPadding
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.ui.strings.CommonStrings
@OptIn(ExperimentalMaterial3Api::class)
@@ -52,10 +55,11 @@ fun DependencyLicensesListView(
navigationIcon = { BackButton(onClick = onBackClick) },
)
},
contentWindowInsets = scaffoldScrollableContentInsets,
) { contentPadding ->
Column(
modifier = Modifier
.padding(contentPadding)
.padding(contentPadding + lazyColumnContentPadding)
.padding(horizontal = 16.dp)
) {
if (state.licenses.isSuccess()) {
@@ -72,7 +76,9 @@ fun DependencyLicensesListView(
modifier = Modifier.fillMaxWidth(),
)
}
LazyColumn {
LazyColumn(
contentPadding = lazyColumnContentPadding,
) {
when (state.licenses) {
is AsyncData.Failure -> item {
Text(
@@ -39,6 +39,8 @@ import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.lazyColumnContentPadding
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
/**
* https://www.figma.com/file/o9p34zmiuEpZRyvZXJZAYL/FTUE?type=design&node-id=604-60817
@@ -59,7 +61,8 @@ fun ChangeAccountProviderView(
title = {},
navigationIcon = { BackButton(onClick = onBackClick) }
)
}
},
contentWindowInsets = scaffoldScrollableContentInsets,
) { padding ->
Box(
modifier = Modifier
@@ -71,6 +74,7 @@ fun ChangeAccountProviderView(
Column(
modifier = Modifier
.verticalScroll(state = rememberScrollState())
.padding(lazyColumnContentPadding)
) {
IconTitleSubtitleMolecule(
modifier = Modifier.padding(top = 16.dp, bottom = 32.dp, start = 16.dp, end = 16.dp),
@@ -44,6 +44,8 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.lazyColumnContentPadding
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.matrix.api.auth.OAuthDetails
import io.element.android.libraries.permissions.api.localnetwork.LocalNetworkPermissionDialogView
import io.element.android.libraries.ui.strings.CommonStrings
@@ -71,7 +73,8 @@ fun ChooseAccountProviderView(
title = {},
navigationIcon = { BackButton(onClick = onBackClick) }
)
}
},
contentWindowInsets = scaffoldScrollableContentInsets,
) { padding ->
Box(
modifier = Modifier
@@ -84,6 +87,7 @@ fun ChooseAccountProviderView(
modifier = Modifier
.fillMaxSize()
.verticalScroll(state = rememberScrollState())
.padding(lazyColumnContentPadding)
) {
IconTitleSubtitleMolecule(
modifier = Modifier.padding(top = 16.dp, bottom = 32.dp, start = 16.dp, end = 16.dp),
@@ -19,7 +19,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@@ -27,7 +26,6 @@ import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.MaterialTheme
@@ -112,6 +110,7 @@ import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.HideKeyboardWhenDisposed
import io.element.android.libraries.designsystem.utils.KeepScreenOn
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.core.EventId
@@ -226,7 +225,7 @@ fun MessagesView(
},
content = {
Scaffold(
contentWindowInsets = WindowInsets.statusBars,
contentWindowInsets = scaffoldScrollableContentInsets,
topBar = {
if (state.timelineState.timelineMode is Timeline.Mode.Thread) {
ThreadTopBar(
@@ -10,9 +10,13 @@ package io.element.android.features.messages.impl.pinned.list
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.plus
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
@@ -47,6 +51,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.ui.strings.CommonStrings
@@ -90,7 +95,8 @@ fun PinnedMessagesListView(
.padding(padding)
.consumeWindowInsets(padding),
)
}
},
contentWindowInsets = scaffoldScrollableContentInsets,
)
}
@@ -206,11 +212,12 @@ private fun PinnedMessagesListLoaded(
onEmojiReactionClick = { _, _ -> },
onVerifiedUserSendFailureClick = {}
)
LazyColumn(
modifier = modifier.fillMaxSize(),
state = rememberLazyListState(),
reverseLayout = true,
contentPadding = PaddingValues(vertical = 8.dp),
contentPadding = WindowInsets.navigationBars.asPaddingValues() + PaddingValues(vertical = 8.dp),
) {
items(
items = state.timelineItems,
@@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.plus
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
@@ -54,6 +55,8 @@ import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.lazyColumnContentPadding
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.matrix.api.core.UserId
@@ -129,14 +132,15 @@ fun ThreadsListView(
},
navigationIcon = {
BackButton(onBackClick)
}
},
windowInsets = scaffoldScrollableContentInsets,
)
}
) { padding ->
val lazyListState = rememberLazyListState()
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = padding,
contentPadding = padding + lazyColumnContentPadding,
state = lazyListState,
) {
itemsIndexed(state.threads, key = { _, row -> row.item.threadId }) { index, row ->
@@ -19,16 +19,15 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.statusBars
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
private val INDICATOR_VERTICAL_PADDING = 6.dp
@@ -47,7 +46,7 @@ fun ConnectivityIndicatorContainer(
// Needed to get valid UI previews
24.dp
} else {
WindowInsets.statusBars.asPaddingValues().calculateTopPadding() + INDICATOR_VERTICAL_PADDING
scaffoldScrollableContentInsets.asPaddingValues().calculateTopPadding() + INDICATOR_VERTICAL_PADDING
}
val target = if (isIndicatorVisible.targetState) statusBarTopPadding else 0.dp
val topWindowInset by animateDpAsState(
@@ -78,6 +78,8 @@ import io.element.android.libraries.designsystem.theme.components.ListItemStyle
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.lazyColumnContentPadding
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.core.RoomAlias
@@ -135,12 +137,14 @@ fun RoomDetailsView(
)
},
snackbarHost = { SnackbarHost(snackbarHostState) },
contentWindowInsets = scaffoldScrollableContentInsets,
) { padding ->
Column(
modifier = Modifier
.padding(padding)
.verticalScroll(rememberScrollState())
.consumeWindowInsets(padding)
.padding(lazyColumnContentPadding)
) {
leaveRoomView()
@@ -50,6 +50,8 @@ import io.element.android.libraries.designsystem.theme.components.SegmentedButto
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.lazyColumnContentPadding
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
import io.element.android.libraries.matrix.api.room.RoomMember
@@ -78,7 +80,8 @@ fun RoomMemberListView(
onBackClick = navigator::exitRoomMemberList,
onInviteClick = navigator::openInviteMembers,
)
}
},
contentWindowInsets = scaffoldScrollableContentInsets,
) { padding ->
Column(
modifier = Modifier
@@ -115,7 +118,11 @@ private fun RoomMemberList(
onSelectedSectionChange: (SelectedSection) -> Unit,
onSelectUser: (RoomMember) -> Unit,
) {
LazyColumn(modifier = Modifier.fillMaxWidth(), state = rememberLazyListState()) {
LazyColumn(
modifier = Modifier.fillMaxWidth(),
state = rememberLazyListState(),
contentPadding = lazyColumnContentPadding,
) {
stickyHeader {
Column {
AnimatedVisibility(visible = showBannedSection) {
@@ -52,6 +52,8 @@ import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.lazyColumnContentPadding
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.ImmutableList
@@ -76,7 +78,8 @@ fun RoomDirectoryView(
.padding(padding)
.consumeWindowInsets(padding)
)
}
},
contentWindowInsets = scaffoldScrollableContentInsets,
)
}
@@ -127,7 +130,10 @@ private fun RoomDirectoryRoomList(
onReachedLoadMore: () -> Unit,
modifier: Modifier = Modifier,
) {
LazyColumn(modifier = modifier) {
LazyColumn(
modifier = modifier,
contentPadding = lazyColumnContentPadding,
) {
items(roomDescriptions) { roomDescription ->
RoomDirectoryRoomRow(
roomDescription = roomDescription,
@@ -18,7 +18,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -228,7 +227,7 @@ private fun RoomMemberActionsBottomSheet(
enabledValues = setOf(SheetValue.Hidden, SheetValue.Expanded),
)
ModalBottomSheet(
modifier = Modifier.systemBarsPadding(),
modifier = Modifier.statusBarsPadding(),
sheetState = bottomSheetState,
onDismissRequest = {
coroutineScope.launch {
@@ -54,6 +54,8 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.lazyColumnContentPadding
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.ImmutableList
@@ -78,14 +80,16 @@ fun SecurityAndPrivacyView(
state.eventSink(SecurityAndPrivacyEvent.Save)
},
)
}
},
contentWindowInsets = scaffoldScrollableContentInsets,
) { padding ->
Column(
modifier = Modifier
.padding(padding)
.imePadding()
.verticalScroll(rememberScrollState())
.consumeWindowInsets(padding),
.consumeWindowInsets(padding)
.padding(lazyColumnContentPadding),
verticalArrangement = Arrangement.spacedBy(32.dp),
) {
if (state.showRoomAccessSection) {
@@ -47,6 +47,8 @@ import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.OnVisibleRangeChangeEffect
import io.element.android.libraries.designsystem.utils.lazyColumnContentPadding
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.matrix.ui.components.SelectedRoom
import io.element.android.libraries.matrix.ui.model.SelectRoomInfo
import io.element.android.libraries.matrix.ui.model.getAvatarData
@@ -101,7 +103,8 @@ fun AddRoomToSpaceView(
)
}
)
}
},
contentWindowInsets = scaffoldScrollableContentInsets,
) { paddingValues ->
Column(
Modifier
@@ -130,7 +133,9 @@ fun AddRoomToSpaceView(
OnVisibleRangeChangeEffect(lazyListState) { visibleRange ->
state.eventSink(AddRoomToSpaceEvent.UpdateSearchVisibleRange(visibleRange))
}
LazyColumn {
LazyColumn(
contentPadding = lazyColumnContentPadding,
) {
items(rooms, key = { it.roomId }) { roomInfo ->
RoomListItem(
roomInfo = roomInfo,
@@ -82,6 +82,8 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.lazyColumnContentPadding
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
import io.element.android.libraries.matrix.api.room.RoomInfo
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
@@ -157,6 +159,7 @@ fun SpaceView(
}
}
},
contentWindowInsets = scaffoldScrollableContentInsets,
content = { padding ->
Box(
modifier = Modifier.padding(padding)
@@ -251,7 +254,10 @@ private fun SpaceViewContent(
onAddRoomClick: () -> Unit,
modifier: Modifier = Modifier,
) {
LazyColumn(modifier.fillMaxSize()) {
LazyColumn(
modifier = modifier.fillMaxSize(),
contentPadding = lazyColumnContentPadding,
) {
val spaceInfo = state.spaceInfo
item(key = "space_header") {
AnimatedVisibility(
@@ -43,6 +43,8 @@ import io.element.android.libraries.designsystem.theme.components.ListItemStyle
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.lazyColumnContentPadding
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.ui.strings.CommonStrings
@@ -62,11 +64,13 @@ fun SpaceSettingsView(
topBar = {
SpaceSettingsTopBar(onBackClick = onBackClick)
},
contentWindowInsets = scaffoldScrollableContentInsets,
) { padding ->
Column(
modifier = Modifier
.padding(padding)
.verticalScroll(rememberScrollState())
.padding(lazyColumnContentPadding)
) {
SpaceInfoSection(
roomId = state.roomId,
@@ -10,7 +10,10 @@ package io.element.android.features.startchat.impl.components
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -76,7 +79,9 @@ fun UserListView(
)
}
if (!state.isSearchActive && state.recentDirectRooms.isNotEmpty()) {
LazyColumn {
LazyColumn(
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
) {
item {
ListSectionHeader(
title = stringResource(id = CommonStrings.common_suggestions),
@@ -45,6 +45,7 @@ import io.element.android.libraries.designsystem.theme.components.ListSectionHea
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.ui.components.CreateDmConfirmationBottomSheet
import io.element.android.libraries.matrix.ui.components.MatrixUserRow
@@ -71,7 +72,8 @@ fun StartChatView(
if (!state.userListState.isSearchActive) {
CreateRoomRootViewTopBar(onCloseClick = onCloseClick)
}
}
},
contentWindowInsets = scaffoldScrollableContentInsets,
) { paddingValues ->
Column(
modifier = Modifier
@@ -10,13 +10,11 @@ package io.element.android.libraries.designsystem.components.preferences
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.layout.plus
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -34,6 +32,8 @@ import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.lazyColumnContentPadding
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
@Composable
fun PreferencePage(
@@ -46,9 +46,8 @@ fun PreferencePage(
Scaffold(
modifier = modifier
.fillMaxSize()
.systemBarsPadding()
.imePadding(),
contentWindowInsets = WindowInsets.statusBars,
contentWindowInsets = scaffoldScrollableContentInsets,
topBar = {
PreferenceTopAppBar(
title = title,
@@ -56,12 +55,12 @@ fun PreferencePage(
)
},
snackbarHost = snackbarHost,
content = {
content = { contentPadding ->
Column(
modifier = Modifier
.padding(it)
.consumeWindowInsets(it)
.verticalScroll(state = rememberScrollState())
.padding(contentPadding + lazyColumnContentPadding)
.consumeWindowInsets(contentPadding + lazyColumnContentPadding)
) {
content()
}
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.designsystem.utils
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import io.element.android.libraries.designsystem.theme.components.Scaffold
/**
* Content insets meant to be used for [Column] or [LazyColumn] components that are used as the main content of a screen,
* to avoid overlapping with navigation bars, IME or cut-out display parts, but ignoring the status bar.
*
* Counterpart of [scaffoldScrollableContentInsets] for [Column] or [LazyColumn] components.
*/
val lazyColumnContentPadding @Composable
get() = WindowInsets.safeDrawing
.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom)
.asPaddingValues()
/**
* Content insets meant to be used for [Scaffold] components that contain some content that is scrollable like [Column] or [LazyColumn],
* and this content should handle its own content padding. The insets will only apply the top padding to avoid overlapping with the status bar,
* while ignoring navigation bars, IME or cut-out display parts.
*
* Counterpart of [lazyColumnContentPadding] for [Scaffold] components.
*/
val scaffoldScrollableContentInsets @Composable
get() = WindowInsets.safeDrawing
.only(WindowInsetsSides.Top)
@@ -14,11 +14,14 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
@@ -67,6 +70,7 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.SegmentedButton
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.timeline.Timeline
@@ -125,6 +129,7 @@ fun MediaGalleryView(
},
)
},
contentWindowInsets = scaffoldScrollableContentInsets,
) { paddingValues ->
Column(
modifier = Modifier
@@ -320,6 +325,7 @@ private fun MediaGalleryFilesList(
val presenterFactories = LocalMediaItemPresenterFactories.current
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
) {
items(
items = files,
@@ -396,6 +402,7 @@ private fun MediaGalleryImageGrid(
columns = GridCells.Adaptive(80.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
) {
items(
items = imagesAndVideos,
@@ -53,6 +53,8 @@ import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.OnVisibleRangeChangeEffect
import io.element.android.libraries.designsystem.utils.lazyColumnContentPadding
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.ui.components.SelectedRoom
import io.element.android.libraries.matrix.ui.model.SelectRoomInfo
@@ -133,7 +135,8 @@ fun RoomSelectView(
)
}
)
}
},
contentWindowInsets = scaffoldScrollableContentInsets,
) { paddingValues ->
Column(
Modifier
@@ -149,7 +152,10 @@ fun RoomSelectView(
resultState = state.resultState,
showBackButton = false,
) { summaries ->
LazyColumn(state = lazyListState) {
LazyColumn(
state = lazyListState,
contentPadding = lazyColumnContentPadding,
) {
item {
SelectedRoomsHelper(
selectedRooms = state.selectedRooms,
@@ -11,14 +11,12 @@ package io.element.android.libraries.troubleshoot.impl.history
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
@@ -50,6 +48,7 @@ import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.scaffoldScrollableContentInsets
import io.element.android.libraries.push.api.history.PushHistoryItem
import io.element.android.libraries.troubleshoot.impl.R
import io.element.android.libraries.ui.strings.CommonStrings
@@ -68,7 +67,7 @@ fun PushHistoryView(
.fillMaxSize()
.systemBarsPadding()
.imePadding(),
contentWindowInsets = WindowInsets.statusBars,
contentWindowInsets = scaffoldScrollableContentInsets,
topBar = {
TopAppBar(
navigationIcon = {