Skip to content

Commit

Permalink
feat: Improve settings page and collapsible top bar
Browse files Browse the repository at this point in the history
- Modified the settings page to use a new style for the header, including a scaling effect.
- Updated the collapsible top bar to use `Animatable` for height adjustments, allowing for smoother transitions.
- Added support for derived state in the collapsible top bar to avoid unnecessary recompositions.
- Improved the scroll connection logic in the collapsible top bar.
- Improved the layout of the settings page, adjusting padding and font weight.
  • Loading branch information
BobbyESP committed Jan 31, 2025
1 parent 741f0e4 commit 657d681
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.lerp
import androidx.compose.ui.util.lerp
import com.bobbyesp.metadator.R
import com.bobbyesp.metadator.core.presentation.common.LocalNavController
import com.bobbyesp.metadator.core.presentation.common.Route
Expand All @@ -42,7 +43,6 @@ import com.bobbyesp.ui.components.topbar.ColumnWithCollapsibleTopBar
fun SettingsPage(
onBackPressed: () -> Unit
) {

val navController = LocalNavController.current
var collapseFraction by remember { mutableFloatStateOf(0f) }

Expand Down Expand Up @@ -75,48 +75,49 @@ fun SettingsPage(
)
ColumnWithCollapsibleTopBar(
topBarContent = {
IconButton(
onClick = onBackPressed,
modifier = Modifier
.align(Alignment.BottomStart)
.padding(horizontal = 12.dp, vertical = 4.dp)
) {
Icon(
imageVector = Icons.Rounded.ArrowBackIosNew,
contentDescription = stringResource(id = com.bobbyesp.ui.R.string.back)
)
}
IconButton(
onClick = onBackPressed,
modifier = Modifier
.align(Alignment.BottomStart)
.padding(horizontal = 12.dp, vertical = 4.dp)
) {
Icon(
imageVector = Icons.Rounded.ArrowBackIosNew,
contentDescription = stringResource(id = com.bobbyesp.ui.R.string.back)
)
}

Text(
text = stringResource(id = R.string.settings),
fontSize = lerp(
MaterialTheme.typography.titleLarge.fontSize,
MaterialTheme.typography.displaySmall.fontSize,
collapseFraction
),
textAlign = TextAlign.Center,
fontWeight = FontWeight.SemiBold,
modifier = Modifier
.align(Alignment.Center)
.padding(horizontal = 16.dp)
)
},
Text(
text = stringResource(id = R.string.settings),
style = MaterialTheme.typography.displaySmall,
textAlign = TextAlign.Center,
fontWeight = FontWeight.Medium,
modifier = Modifier
.align(Alignment.Center)
.padding(horizontal = 16.dp)
.graphicsLayer {
val scale = lerp(0.7f, 1f, collapseFraction)
scaleX = scale
scaleY = scale
}
)
},
collapseFraction = {
collapseFraction = it
},
contentPadding = PaddingValues(horizontal = 16.dp),
contentPadding = PaddingValues(horizontal = 32.dp),
contentHorizontalAlignment = Alignment.CenterHorizontally,
contentVerticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier
.fillMaxSize()
.safeDrawingPadding()
) {
SettingsGroup(
modifier = Modifier.padding(horizontal = 16.dp), items = mainSettingsGroup
modifier = Modifier, items = mainSettingsGroup
)

SettingsGroup(
modifier = Modifier.padding(horizontal = 16.dp), items = infoSettingsGroup
modifier = Modifier, items = infoSettingsGroup
)

Text(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.bobbyesp.ui.components.topbar

import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.foundation.ScrollState
Expand All @@ -25,8 +26,11 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
Expand Down Expand Up @@ -64,61 +68,62 @@ fun ColumnWithCollapsibleTopBar(
val density = LocalDensity.current
val coroutineScope = rememberCoroutineScope()

val isInLandscapeOrientation = isDeviceInLandscape()
val minTopBarHeight = remember { with(density) { minTopBarHeight.toPx() } }
val maxTopBarHeight = remember {
with(density) {
if (isInLandscapeOrientation) {
maxTopBarHeightLandscape.toPx()
} else maxTopBarHeight.toPx()
}
val isInLandscapeOrientation by rememberUpdatedState(newValue = isDeviceInLandscape())

val minHeightPx = with(density) { minTopBarHeight.toPx() }
val maxHeightPx = with(density) {
if (isInLandscapeOrientation) maxTopBarHeightLandscape.toPx() else maxTopBarHeight.toPx()
}

val topBarHeight = remember {
Animatable(
initialValue = if (collapsedByDefault || isInLandscapeOrientation) {
minHeightPx
} else maxHeightPx
)
}
val topBarHeight = rememberAnimatable(
initialValue = if (collapsedByDefault || isInLandscapeOrientation) {
minTopBarHeight
} else maxTopBarHeight
)

LaunchedEffect(isInLandscapeOrientation) {
if (isInLandscapeOrientation) {
topBarHeight.snapTo(minTopBarHeight)
topBarHeight.snapTo(minHeightPx)
}
}

LaunchedEffect(topBarHeight.value) {
collapseFraction(
(topBarHeight.value - minTopBarHeight) / (maxTopBarHeight - minTopBarHeight)
)
// Using derivedStateOf to avoid unnecessary recompositions
val collapseFractionState by remember {
derivedStateOf {
(topBarHeight.value - minHeightPx) / (maxHeightPx - minHeightPx)
}
}

LaunchedEffect(collapseFractionState) {
collapseFraction(collapseFractionState)
}

val topBarScrollConnection = remember {
return@remember object : NestedScrollConnection {
override fun onPreScroll(
available: Offset,
source: NestedScrollSource
): Offset {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
val previousHeight = topBarHeight.value
val newHeight =
(previousHeight + available.y - contentScrollState.value).coerceIn(
minTopBarHeight,
maxTopBarHeight
)
val newHeight = (previousHeight + available.y)
.coerceIn(minHeightPx, maxHeightPx)

coroutineScope.launch {
topBarHeight.snapTo(newHeight)
}

return Offset(0f, newHeight - previousHeight)
}

override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
val threshold = (maxHeightPx - minHeightPx)
coroutineScope.launch {
val threshold = (maxTopBarHeight - minTopBarHeight)
topBarHeight.animateTo(
targetValue = if (topBarHeight.value < threshold) minTopBarHeight else maxTopBarHeight,
targetValue = if (topBarHeight.value < threshold) minHeightPx else maxHeightPx,
animationSpec = spring(stiffness = Spring.StiffnessLow)
)
}

return super.onPostFling(consumed, available)
return Velocity.Zero
}
}
}
Expand All @@ -142,7 +147,6 @@ fun ColumnWithCollapsibleTopBar(
verticalArrangement = contentVerticalArrangement
) {
content()

Spacer(modifier = Modifier.height(200.dp))
}
}
Expand Down

0 comments on commit 657d681

Please sign in to comment.