Use floating toolbar on homepage

This commit is contained in:
chelsea 2026-01-12 20:27:58 +00:00 committed by Benoit Marty
parent fe4554703c
commit b4ee3b1dd6
4 changed files with 48 additions and 60 deletions

View file

@ -33,7 +33,6 @@ data class HomeState(
val eventSink: (HomeEvent) -> Unit, val eventSink: (HomeEvent) -> Unit,
) { ) {
val isBackHandlerEnabled = currentHomeNavigationBarItem != HomeNavigationBarItem.Chats || roomListState.spaceFiltersState is SpaceFiltersState.Selected val isBackHandlerEnabled = currentHomeNavigationBarItem != HomeNavigationBarItem.Chats || roomListState.spaceFiltersState is SpaceFiltersState.Selected
val displayActions = currentHomeNavigationBarItem == HomeNavigationBarItem.Chats
val displayRoomListFilters = currentHomeNavigationBarItem == HomeNavigationBarItem.Chats && roomListState.displayFilters val displayRoomListFilters = currentHomeNavigationBarItem == HomeNavigationBarItem.Chats && roomListState.displayFilters
val showNavigationBar = homeSpacesState.canCreateSpaces || homeSpacesState.spaceRooms.isNotEmpty() val showNavigationBar = homeSpacesState.canCreateSpaces || homeSpacesState.spaceRooms.isNotEmpty()
} }

View file

@ -13,6 +13,7 @@ package io.element.android.features.home.impl
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
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.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding import androidx.compose.foundation.layout.calculateStartPadding
@ -21,19 +22,25 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FabPosition
import androidx.compose.material3.FloatingToolbarDefaults.ScreenOffset
import androidx.compose.material3.HorizontalFloatingToolbar
import androidx.compose.material3.IconButton
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import dev.chrisbanes.haze.hazeEffect import dev.chrisbanes.haze.hazeEffect
import dev.chrisbanes.haze.hazeSource import dev.chrisbanes.haze.hazeSource
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
@ -59,14 +66,11 @@ import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.FloatingActionButton import io.element.android.libraries.designsystem.theme.components.FloatingActionButton
import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.NavigationBar
import io.element.android.libraries.designsystem.theme.components.NavigationBarIcon
import io.element.android.libraries.designsystem.theme.components.NavigationBarItem
import io.element.android.libraries.designsystem.theme.components.NavigationBarText
import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
@ -198,7 +202,7 @@ private fun HomeScaffold(
) )
) )
}, },
bottomBar = { floatingActionButton = {
if (state.showNavigationBar) { if (state.showNavigationBar) {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
HomeBottomBar( HomeBottomBar(
@ -222,13 +226,16 @@ private fun HomeScaffold(
state.eventSink(HomeEvent.SelectHomeNavigationBarItem(item)) state.eventSink(HomeEvent.SelectHomeNavigationBarItem(item))
} }
}, },
modifier = Modifier.hazeEffect( floatingActionButton = {
state = hazeState, when (state.currentHomeNavigationBarItem) {
style = HazeMaterials.thick(), HomeNavigationBarItem.Chats -> HomeFloatingActionButton(onStartChatClick, CommonStrings.action_create_a_room)
) HomeNavigationBarItem.Spaces -> HomeFloatingActionButton(onCreateSpaceClick, CommonStrings.action_create_space)
}
}
) )
} }
}, },
floatingActionButtonPosition = FabPosition.Center,
content = { padding -> content = { padding ->
when (state.currentHomeNavigationBarItem) { when (state.currentHomeNavigationBarItem) {
HomeNavigationBarItem.Chats -> { HomeNavigationBarItem.Chats -> {
@ -286,50 +293,51 @@ private fun HomeScaffold(
} }
} }
}, },
floatingActionButton = {
if (state.displayActions) {
FloatingActionButton(
onClick = onStartChatClick,
) {
Icon(
imageVector = CompoundIcons.Plus(),
contentDescription = stringResource(id = R.string.screen_roomlist_a11y_create_message),
)
}
}
},
snackbarHost = { SnackbarHost(snackbarHostState) }, snackbarHost = { SnackbarHost(snackbarHostState) },
) )
} }
@Composable
private fun HomeFloatingActionButton(
onClick: () -> Unit,
contentDescription: Int,
modifier: Modifier = Modifier,
) {
FloatingActionButton(onClick = onClick, modifier = modifier) {
Icon(
imageVector = CompoundIcons.Plus(),
contentDescription = stringResource(id = contentDescription),
)
}
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
@Composable @Composable
private fun HomeBottomBar( private fun HomeBottomBar(
currentHomeNavigationBarItem: HomeNavigationBarItem, currentHomeNavigationBarItem: HomeNavigationBarItem,
onItemClick: (HomeNavigationBarItem) -> Unit, onItemClick: (HomeNavigationBarItem) -> Unit,
floatingActionButton: @Composable () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
NavigationBar( HorizontalFloatingToolbar(
containerColor = Color.Transparent, expanded = true,
floatingActionButton = floatingActionButton,
modifier = modifier modifier = modifier
.padding(bottom = ScreenOffset)
.zIndex(1f),
) { ) {
HomeNavigationBarItem.entries.forEach { item -> HomeNavigationBarItem.entries.forEach { item ->
val isSelected = currentHomeNavigationBarItem == item val isSelected = currentHomeNavigationBarItem == item
NavigationBarItem( IconButton(
selected = isSelected, onClick = { onItemClick(item) },
onClick = { ) {
onItemClick(item) Column(horizontalAlignment = Alignment.CenterHorizontally) {
}, Icon(
icon = {
NavigationBarIcon(
imageVector = item.icon(isSelected), imageVector = item.icon(isSelected),
) contentDescription = stringResource(item.labelRes),
},
label = {
NavigationBarText(
text = stringResource(item.labelRes),
) )
} }
) }
} }
} }
} }

View file

@ -134,17 +134,13 @@ fun HomeTopBar(
) )
}, },
actions = { actions = {
when (selectedNavigationItem) { if (selectedNavigationItem == HomeNavigationBarItem.Chats) {
HomeNavigationBarItem.Chats -> RoomListMenuItems( RoomListMenuItems(
onToggleSearch = onToggleSearch, onToggleSearch = onToggleSearch,
onMenuActionClick = onMenuActionClick, onMenuActionClick = onMenuActionClick,
canReportBug = canReportBug, canReportBug = canReportBug,
spaceFiltersState = spaceFiltersState, spaceFiltersState = spaceFiltersState,
) )
HomeNavigationBarItem.Spaces -> SpacesMenuItems(
canCreateSpaces = canCreateSpaces,
onCreateSpace = onCreateSpace
)
} }
}, },
// We want a 16dp left padding for the navigationIcon : // We want a 16dp left padding for the navigationIcon :
@ -230,21 +226,6 @@ private fun RoomListMenuItems(
} }
} }
@Composable
private fun SpacesMenuItems(
canCreateSpaces: Boolean,
onCreateSpace: () -> Unit
) {
if (canCreateSpaces) {
IconButton(onClick = onCreateSpace) {
Icon(
imageVector = CompoundIcons.Plus(),
contentDescription = stringResource(CommonStrings.action_create_space)
)
}
}
}
@Composable @Composable
private fun SpaceFilterButton( private fun SpaceFilterButton(
spaceFiltersState: SpaceFiltersState, spaceFiltersState: SpaceFiltersState,

View file

@ -120,7 +120,7 @@ androidx_preference = "androidx.preference:preference:1.2.1"
androidx_webkit = "androidx.webkit:webkit:1.15.0" androidx_webkit = "androidx.webkit:webkit:1.15.0"
androidx_compose_bom = { module = "androidx.compose:compose-bom", version.ref = "compose_bom" } androidx_compose_bom = { module = "androidx.compose:compose-bom", version.ref = "compose_bom" }
androidx_compose_material3 = { module = "androidx.compose.material3:material3" } androidx_compose_material3 = { module = "androidx.compose.material3:material3", version = '1.5.0-alpha11' }
androidx_compose_material3_windowsizeclass = { module = "androidx.compose.material3:material3-window-size-class" } androidx_compose_material3_windowsizeclass = { module = "androidx.compose.material3:material3-window-size-class" }
androidx_compose_material3_adaptive = "androidx.compose.material3:material3-adaptive-android:1.0.0-alpha06" androidx_compose_material3_adaptive = "androidx.compose.material3:material3-adaptive-android:1.0.0-alpha06"
androidx_compose_ui = { module = "androidx.compose.ui:ui" } androidx_compose_ui = { module = "androidx.compose.ui:ui" }