feat: Implement a simple bottom navigation bar based UI

for facilitating multiple screens in further releases
This commit is contained in:
Nishant Mishra
2025-07-03 13:59:00 +05:30
parent a5da20e32c
commit 7b7d10a233
6 changed files with 164 additions and 88 deletions

View File

@@ -1,7 +1,16 @@
package org.nsh07.pomodoro.ui
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material3.ShortNavigationBar
import androidx.compose.material3.ShortNavigationBarItem
import androidx.compose.material3.ShortNavigationBarItemDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -10,10 +19,13 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.painterResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.ui.timerScreen.TimerScreen
import org.nsh07.pomodoro.ui.viewModel.UiViewModel
@@ -36,13 +48,33 @@ fun AppScreen(
}
}
TimerScreen(
uiState = uiState,
showBrandTitle = showBrandTitle,
progress = { progress },
resetTimer = viewModel::resetTimer,
skipTimer = viewModel::skipTimer,
toggleTimer = viewModel::toggleTimer,
modifier = modifier
)
val layoutDirection = LocalLayoutDirection.current
Scaffold(
bottomBar = {
ShortNavigationBar {
ShortNavigationBarItem(
selected = true,
onClick = { },
icon = { Icon(painterResource(R.drawable.hourglass_filled), null) },
label = { Text("Timer") },
colors = ShortNavigationBarItemDefaults.colors()
)
}
}
) { contentPadding ->
TimerScreen(
uiState = uiState,
showBrandTitle = showBrandTitle,
progress = { progress },
resetTimer = viewModel::resetTimer,
skipTimer = viewModel::skipTimer,
toggleTimer = viewModel::toggleTimer,
modifier = modifier.padding(
start = contentPadding.calculateStartPadding(layoutDirection),
end = contentPadding.calculateEndPadding(layoutDirection),
bottom = contentPadding.calculateBottomPadding()
)
)
}
}

View File

@@ -31,7 +31,6 @@ import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.motionScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
@@ -94,89 +93,84 @@ fun TimerScreen(
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
}
Scaffold(
topBar = {
TopAppBar(
title = {
AnimatedContent(
if (!showBrandTitle) uiState.timerMode else TimerMode.BRAND,
transitionSpec = {
slideInVertically(
Column(modifier = modifier) {
TopAppBar(
title = {
AnimatedContent(
if (!showBrandTitle) uiState.timerMode else TimerMode.BRAND,
transitionSpec = {
slideInVertically(
animationSpec = motionScheme.slowSpatialSpec(),
initialOffsetY = { (-it * 1.25).toInt() }
).togetherWith(
slideOutVertically(
animationSpec = motionScheme.slowSpatialSpec(),
initialOffsetY = { (-it * 1.25).toInt() }
).togetherWith(
slideOutVertically(
animationSpec = motionScheme.slowSpatialSpec(),
targetOffsetY = { (it * 1.25).toInt() }
)
targetOffsetY = { (it * 1.25).toInt() }
)
}
) {
when (it) {
TimerMode.BRAND ->
Text(
"Tomato",
style = TextStyle(
fontFamily = robotoFlexTitle,
fontSize = 32.sp,
lineHeight = 32.sp,
color = colorScheme.onErrorContainer
),
textAlign = TextAlign.Center,
modifier = Modifier.width(210.dp)
)
TimerMode.FOCUS ->
Text(
"Focus",
style = TextStyle(
fontFamily = robotoFlexTitle,
fontSize = 32.sp,
lineHeight = 32.sp,
color = colorScheme.onPrimaryContainer
),
textAlign = TextAlign.Center,
modifier = Modifier.width(210.dp)
)
TimerMode.SHORT_BREAK -> Text(
"Short Break",
style = TextStyle(
fontFamily = robotoFlexTitle,
fontSize = 32.sp,
lineHeight = 32.sp,
color = colorScheme.onTertiaryContainer
),
textAlign = TextAlign.Center,
modifier = Modifier.width(210.dp)
)
TimerMode.LONG_BREAK -> Text(
"Long Break",
style = TextStyle(
fontFamily = robotoFlexTitle,
fontSize = 32.sp,
lineHeight = 32.sp,
color = colorScheme.onTertiaryContainer
),
textAlign = TextAlign.Center,
modifier = Modifier.width(210.dp)
)
}
)
}
},
subtitle = {},
titleHorizontalAlignment = Alignment.CenterHorizontally
)
},
modifier = modifier.fillMaxSize()
) { insets ->
) {
when (it) {
TimerMode.BRAND ->
Text(
"Tomato",
style = TextStyle(
fontFamily = robotoFlexTitle,
fontSize = 32.sp,
lineHeight = 32.sp,
color = colorScheme.onErrorContainer
),
textAlign = TextAlign.Center,
modifier = Modifier.width(210.dp)
)
TimerMode.FOCUS ->
Text(
"Focus",
style = TextStyle(
fontFamily = robotoFlexTitle,
fontSize = 32.sp,
lineHeight = 32.sp,
color = colorScheme.onPrimaryContainer
),
textAlign = TextAlign.Center,
modifier = Modifier.width(210.dp)
)
TimerMode.SHORT_BREAK -> Text(
"Short Break",
style = TextStyle(
fontFamily = robotoFlexTitle,
fontSize = 32.sp,
lineHeight = 32.sp,
color = colorScheme.onTertiaryContainer
),
textAlign = TextAlign.Center,
modifier = Modifier.width(210.dp)
)
TimerMode.LONG_BREAK -> Text(
"Long Break",
style = TextStyle(
fontFamily = robotoFlexTitle,
fontSize = 32.sp,
lineHeight = 32.sp,
color = colorScheme.onTertiaryContainer
),
textAlign = TextAlign.Center,
modifier = Modifier.width(210.dp)
)
}
}
},
subtitle = {},
titleHorizontalAlignment = Alignment.CenterHorizontally
)
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize()
.padding(insets)
modifier = Modifier.fillMaxSize()
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Box(contentAlignment = Alignment.Center) {
@@ -423,6 +417,13 @@ fun TimerScreenPreview() {
timeStr = "03:34", nextTimeStr = "5:00", timerMode = TimerMode.FOCUS, timerRunning = true
)
TomatoTheme {
TimerScreen(uiState, false, { 0.3f }, {}, {}, {})
TimerScreen(
uiState,
false,
{ 0.3f },
{},
{},
{}
)
}
}

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M320,800h320v-120q0,-66 -47,-113t-113,-47q-66,0 -113,47t-47,113v120ZM480,440q66,0 113,-47t47,-113v-120L320,160v120q0,66 47,113t113,47ZM200,880q-17,0 -28.5,-11.5T160,840q0,-17 11.5,-28.5T200,800h40v-120q0,-61 28.5,-114.5T348,480q-51,-32 -79.5,-85.5T240,280v-120h-40q-17,0 -28.5,-11.5T160,120q0,-17 11.5,-28.5T200,80h560q17,0 28.5,11.5T800,120q0,17 -11.5,28.5T760,160h-40v120q0,61 -28.5,114.5T612,480q51,32 79.5,85.5T720,680v120h40q17,0 28.5,11.5T800,840q0,17 -11.5,28.5T760,880L200,880ZM480,800ZM480,160Z" />
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M200,880q-17,0 -28.5,-11.5T160,840q0,-17 11.5,-28.5T200,800h40v-120q0,-61 28.5,-114.5T348,480q-51,-32 -79.5,-85.5T240,280v-120h-40q-17,0 -28.5,-11.5T160,120q0,-17 11.5,-28.5T200,80h560q17,0 28.5,11.5T800,120q0,17 -11.5,28.5T760,160h-40v120q0,61 -28.5,114.5T612,480q51,32 79.5,85.5T720,680v120h40q17,0 28.5,11.5T800,840q0,17 -11.5,28.5T760,880L200,880Z" />
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M480,800q-33,0 -56.5,-23.5T400,720q0,-33 23.5,-56.5T480,640q33,0 56.5,23.5T560,720q0,33 -23.5,56.5T480,800ZM480,560q-33,0 -56.5,-23.5T400,480q0,-33 23.5,-56.5T480,400q33,0 56.5,23.5T560,480q0,33 -23.5,56.5T480,560ZM480,320q-33,0 -56.5,-23.5T400,240q0,-33 23.5,-56.5T480,160q33,0 56.5,23.5T560,240q0,33 -23.5,56.5T480,320Z" />
</vector>

View File

@@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="#000000"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M433,880q-27,0 -46.5,-18T363,818l-9,-66q-13,-5 -24.5,-12T307,725l-62,26q-25,11 -50,2t-39,-32l-47,-82q-14,-23 -8,-49t27,-43l53,-40q-1,-7 -1,-13.5v-27q0,-6.5 1,-13.5l-53,-40q-21,-17 -27,-43t8,-49l47,-82q14,-23 39,-32t50,2l62,26q11,-8 23,-15t24,-12l9,-66q4,-26 23.5,-44t46.5,-18h94q27,0 46.5,18t23.5,44l9,66q13,5 24.5,12t22.5,15l62,-26q25,-11 50,-2t39,32l47,82q14,23 8,49t-27,43l-53,40q1,7 1,13.5v27q0,6.5 -2,13.5l53,40q21,17 27,43t-8,49l-48,82q-14,23 -39,32t-50,-2l-60,-26q-11,8 -23,15t-24,12l-9,66q-4,26 -23.5,44T527,880h-94ZM482,620q58,0 99,-41t41,-99q0,-58 -41,-99t-99,-41q-59,0 -99.5,41T342,480q0,58 40.5,99t99.5,41Z" />
</vector>