Skip to content
This repository has been archived by the owner on Nov 12, 2024. It is now read-only.

Make bottom navigation bar float above content #1825

Merged
merged 1 commit into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion common/ui/compose/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ kotlin {
api(projects.common.ui.resources.strings)
api(libs.lyricist.library)

implementation(libs.haze.haze)
api(libs.haze.haze)
implementation(libs.haze.materials)
api(libs.coil.compose)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,11 @@ fun HazeScaffold(
containerColor: Color = MaterialTheme.colorScheme.background,
contentColor: Color = contentColorFor(containerColor),
contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets,
hazeState: HazeState = remember { HazeState() },
blurTopBar: Boolean = false,
blurBottomBar: Boolean = false,
content: @Composable (PaddingValues) -> Unit,
) {
val hazeState = remember { HazeState() }

NestedScaffold(
modifier = modifier,
topBar = {
Expand Down
2 changes: 2 additions & 0 deletions ui/root/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ kotlin {
implementation(projects.core.analytics)
implementation(projects.common.ui.compose)

implementation(libs.haze.materials)

implementation(projects.domain)
implementation(projects.data.traktauth)

Expand Down
116 changes: 104 additions & 12 deletions ui/root/src/commonMain/kotlin/app/tivi/home/Home.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,26 @@
package app.tivi.home

import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxHeight
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.safeContent
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Subscriptions
Expand All @@ -23,13 +32,17 @@ import androidx.compose.material.icons.filled.Weekend
import androidx.compose.material.icons.outlined.VideoLibrary
import androidx.compose.material.icons.outlined.Weekend
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBarDefaults
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.NavigationBarItemDefaults
import androidx.compose.material3.NavigationDrawerItem
import androidx.compose.material3.NavigationRail
import androidx.compose.material3.NavigationRailItem
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.VerticalDivider
import androidx.compose.material3.contentColorFor
import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
Expand All @@ -39,8 +52,12 @@ import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import app.tivi.common.compose.HazeScaffold
import app.tivi.common.compose.LocalStrings
Expand All @@ -56,7 +73,12 @@ import com.slack.circuit.overlay.ContentWithOverlays
import com.slack.circuit.runtime.Navigator
import com.slack.circuit.runtime.screen.Screen
import com.slack.circuitx.gesturenavigation.GestureNavigationDecoration
import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.hazeChild
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import dev.chrisbanes.haze.materials.HazeMaterials

@OptIn(ExperimentalHazeMaterialsApi::class)
@Composable
internal fun Home(
backStack: SaveableBackStack,
Expand All @@ -75,20 +97,33 @@ internal fun Home(
val strings = LocalStrings.current
val navigationItems = remember(strings) { buildNavigationItems(strings) }

val hazeState = remember { HazeState() }

HazeScaffold(
bottomBar = {
if (navigationType == NavigationType.BOTTOM_NAVIGATION) {
HomeNavigationBar(
selectedNavigation = rootScreen,
navigationItems = navigationItems,
onNavigationSelected = {
navigator.resetRootIfDifferent(it, saveState = true, restoreState = true)
},
modifier = Modifier.fillMaxWidth(),
)
Box {
HomeNavigationBar(
selectedNavigation = rootScreen,
navigationItems = navigationItems,
onNavigationSelected = {
navigator.resetRootIfDifferent(it, saveState = true, restoreState = true)
},
modifier = Modifier
.padding(horizontal = 24.dp)
.padding(bottom = 8.dp)
.windowInsetsPadding(WindowInsets.navigationBars)
.hazeChild(
state = hazeState,
style = HazeMaterials.regular(),
shape = MaterialTheme.shapes.extraLarge,
)
.fillMaxWidth(),
)
}
}
},
blurBottomBar = true,
hazeState = hazeState,
modifier = modifier,
) {
Row(modifier = Modifier.fillMaxSize()) {
Expand Down Expand Up @@ -130,19 +165,71 @@ internal fun Home(
}
}

@Composable
fun FloatingNavigationBar(
modifier: Modifier = Modifier,
shape: Shape = MaterialTheme.shapes.extraLarge,
containerColor: Color = NavigationBarDefaults.containerColor,
contentColor: Color = MaterialTheme.colorScheme.contentColorFor(containerColor),
tonalElevation: Dp = NavigationBarDefaults.Elevation,
content: @Composable RowScope.() -> Unit,
) {
Surface(
color = containerColor,
contentColor = contentColor,
tonalElevation = tonalElevation,
shape = shape,
border = BorderStroke(
width = 0.5.dp,
brush = Brush.verticalGradient(
colors = listOf(
MaterialTheme.colorScheme.surfaceVariant,
MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f),
),
),
),
modifier = modifier,
) {
Row(
modifier = Modifier
.padding(horizontal = 8.dp)
.fillMaxWidth()
.height(80.dp)
.selectableGroup(),
horizontalArrangement = Arrangement.spacedBy(8.dp),
content = content,
)
}
}

@Composable
private fun HomeNavigationBar(
selectedNavigation: Screen,
navigationItems: List<HomeNavigationItem>,
onNavigationSelected: (Screen) -> Unit,
modifier: Modifier = Modifier,
) {
NavigationBar(
FloatingNavigationBar(
modifier = modifier,
containerColor = Color.Transparent,
windowInsets = WindowInsets.navigationBars,
) {
val colors = NavigationBarItemDefaults.colors(
selectedIconColor = MaterialTheme.colorScheme.onPrimaryContainer,
selectedTextColor = MaterialTheme.colorScheme.onPrimaryContainer,
unselectedIconColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
unselectedTextColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
)

for (item in navigationItems) {
val isSelected = selectedNavigation == item.screen
val scale by animateFloatAsState(
targetValue = if (isSelected) 1f else 0.95f,
animationSpec = spring(
stiffness = Spring.StiffnessLow,
dampingRatio = Spring.DampingRatioLowBouncy,
),
)

NavigationBarItem(
icon = {
HomeNavigationItemIcon(
Expand All @@ -152,7 +239,12 @@ private fun HomeNavigationBar(
},
label = { Text(text = item.label) },
selected = selectedNavigation == item.screen,
colors = colors,
onClick = { onNavigationSelected(item.screen) },
modifier = Modifier.graphicsLayer {
scaleX = scale
scaleY = scale
},
)
}
}
Expand Down
Loading