Settings tab in navigation drawer

This commit is contained in:
Sad Ellie 2023-02-26 14:13:02 +04:00
parent df44a33415
commit 9d665823ba
11 changed files with 122 additions and 39 deletions

View File

@ -21,6 +21,7 @@ package com.sadellie.unitto
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Calculate import androidx.compose.material.icons.filled.Calculate
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.SwapHoriz import androidx.compose.material.icons.filled.SwapHoriz
import androidx.compose.material3.DrawerValue import androidx.compose.material3.DrawerValue
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -35,6 +36,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
@ -76,9 +78,29 @@ internal fun UnittoApp() {
TopLevelDestinations.Calculator to Icons.Default.Calculate, TopLevelDestinations.Calculator to Icons.Default.Calculate,
TopLevelDestinations.Converter to Icons.Default.SwapHoriz TopLevelDestinations.Converter to Icons.Default.SwapHoriz
) )
val additionalTabs = listOf(
TopLevelDestinations.Settings to Icons.Default.Settings
)
val navBackStackEntry by navController.currentBackStackEntryAsState() val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute by remember(navBackStackEntry?.destination) { val currentRoute: TopLevelDestinations? by remember(navBackStackEntry?.destination) {
derivedStateOf { navBackStackEntry?.destination?.route } derivedStateOf {
val hierarchyRoutes = navBackStackEntry?.destination?.hierarchy?.map { it.route }
?: emptySequence()
(mainTabs + additionalTabs)
.map { it.first }
.firstOrNull {
hierarchyRoutes.contains(it.route)
}
}
}
val gesturesEnabled: Boolean by remember(navBackStackEntry?.destination) {
derivedStateOf {
// Will be true for routes like
// [null, calculator_route, settings_graph, settings_route, themes_route]
// We disable drawer drag gesture when we are too deep
navController.backQueue.size <= 4
}
} }
Themmo( Themmo(
@ -88,18 +110,19 @@ internal fun UnittoApp() {
) { ) {
val statusBarColor = when (currentRoute) { val statusBarColor = when (currentRoute) {
// Match text field container color // Match text field container color
TopLevelDestinations.Calculator.route -> MaterialTheme.colorScheme.surfaceVariant TopLevelDestinations.Calculator -> MaterialTheme.colorScheme.surfaceVariant
else -> MaterialTheme.colorScheme.background else -> MaterialTheme.colorScheme.background
} }
val navigationBarColor = MaterialTheme.colorScheme.background val navigationBarColor = MaterialTheme.colorScheme.background
ModalNavigationDrawer( ModalNavigationDrawer(
drawerState = drawerState, drawerState = drawerState,
gesturesEnabled = true, gesturesEnabled = gesturesEnabled,
drawerContent = { drawerContent = {
UnittoDrawerSheet( UnittoDrawerSheet(
modifier = Modifier, modifier = Modifier,
mainTabs = mainTabs, mainTabs = mainTabs,
additionalTabs = additionalTabs,
currentDestination = currentRoute currentDestination = currentRoute
) { ) {
drawerScope.launch { drawerState.close() } drawerScope.launch { drawerState.close() }

View File

@ -19,6 +19,7 @@
package com.sadellie.unitto package com.sadellie.unitto
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import com.sadellie.unitto.feature.calculator.navigation.calculatorScreen import com.sadellie.unitto.feature.calculator.navigation.calculatorScreen
@ -50,10 +51,20 @@ internal fun UnittoNavigation(
navController = navController, navController = navController,
startDestination = startDestination startDestination = startDestination
) { ) {
fun navigateToSettings() {
navController.navigateToSettings {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
converterScreen( converterScreen(
navigateToLeftScreen = navController::navigateToLeftSide, navigateToLeftScreen = navController::navigateToLeftSide,
navigateToRightScreen = navController::navigateToRightSide, navigateToRightScreen = navController::navigateToRightSide,
navigateToSettings = navController::navigateToSettings, navigateToSettings = ::navigateToSettings,
navigateToMenu = openDrawer, navigateToMenu = openDrawer,
viewModel = converterViewModel viewModel = converterViewModel
) )
@ -75,12 +86,13 @@ internal fun UnittoNavigation(
settingGraph( settingGraph(
settingsViewModel = settingsViewModel, settingsViewModel = settingsViewModel,
themmoController = themmoController, themmoController = themmoController,
navController = navController navController = navController,
menuButtonClick = openDrawer
) )
calculatorScreen( calculatorScreen(
navigateToMenu = openDrawer, navigateToMenu = openDrawer,
navigateToSettings = navController::navigateToSettings navigateToSettings = ::navigateToSettings
) )
epochScreen(navigateToMenu = openDrawer) epochScreen(navigateToMenu = openDrawer)

View File

@ -38,6 +38,11 @@ sealed class TopLevelDestinations(
route = "epoch_route", route = "epoch_route",
name = R.string.epoch_converter name = R.string.epoch_converter
) )
object Settings : TopLevelDestinations(
route = "settings_graph",
name = R.string.settings_screen
)
} }
val TOP_LEVEL_DESTINATIONS: Map<TopLevelDestinations, Int> by lazy { val TOP_LEVEL_DESTINATIONS: Map<TopLevelDestinations, Int> by lazy {

View File

@ -21,12 +21,16 @@ package com.sadellie.unitto.core.ui.common
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Calculate
import androidx.compose.material3.Divider
import androidx.compose.material3.ModalDrawerSheet import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.NavigationDrawerItemDefaults import androidx.compose.material3.NavigationDrawerItemDefaults
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sadellie.unitto.core.base.TopLevelDestinations import com.sadellie.unitto.core.base.TopLevelDestinations
@ -34,7 +38,8 @@ import com.sadellie.unitto.core.base.TopLevelDestinations
fun UnittoDrawerSheet( fun UnittoDrawerSheet(
modifier: Modifier, modifier: Modifier,
mainTabs: List<Pair<TopLevelDestinations, ImageVector>>, mainTabs: List<Pair<TopLevelDestinations, ImageVector>>,
currentDestination: String?, additionalTabs: List<Pair<TopLevelDestinations, ImageVector>>,
currentDestination: TopLevelDestinations?,
onItemClick: (String) -> Unit onItemClick: (String) -> Unit
) { ) {
ModalDrawerSheet( ModalDrawerSheet(
@ -49,9 +54,41 @@ fun UnittoDrawerSheet(
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding), modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding),
destination = destination, destination = destination,
icon = icon, icon = icon,
selected = destination.route == currentDestination, selected = destination == currentDestination,
onClick = onItemClick
)
}
Divider(Modifier.padding(28.dp, 16.dp))
additionalTabs.forEach { (destination, icon) ->
UnittoDrawerItem(
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding),
destination = destination,
icon = icon,
selected = destination == currentDestination,
onClick = onItemClick onClick = onItemClick
) )
} }
} }
} }
@Preview
@Composable
private fun PreviewUnittoDrawerSheet() {
UnittoDrawerSheet(
modifier = Modifier,
mainTabs = listOf(
TopLevelDestinations.Calculator to Icons.Default.Calculate,
TopLevelDestinations.Calculator to Icons.Default.Calculate,
TopLevelDestinations.Settings to Icons.Default.Calculate
),
additionalTabs = listOf(
TopLevelDestinations.Calculator to Icons.Default.Calculate,
TopLevelDestinations.Calculator to Icons.Default.Calculate,
TopLevelDestinations.Calculator to Icons.Default.Calculate
),
currentDestination = TopLevelDestinations.Settings,
onItemClick = {}
)
}

View File

@ -29,16 +29,16 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
/** /**
* Commonly used LargeTopAppBar with scroll behavior. * Template screen. Uses [Scaffold] and [LargeTopAppBar]
* *
* @param title Text that is displayed in top bar. * @param title See [LargeTopAppBar]
* @param navigateUpAction Action when user click arrow button at the top. * @param navigationIcon See [LargeTopAppBar]
* @param content Content that can be scrolled. Don't forget to use padding values. * @param content See [Scaffold]
*/ */
@Composable @Composable
fun UnittoLargeTopAppBar( fun UnittoScreenWithLargeTopBar(
title: String, title: String,
navigateUpAction: () -> Unit, navigationIcon: @Composable () -> Unit,
content: @Composable (PaddingValues) -> Unit content: @Composable (PaddingValues) -> Unit
) { ) {
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior( val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
@ -52,9 +52,7 @@ fun UnittoLargeTopAppBar(
title = { title = {
Text(text = title) Text(text = title)
}, },
navigationIcon = { navigationIcon = navigationIcon,
NavigateUpButton { navigateUpAction() }
},
scrollBehavior = scrollBehavior scrollBehavior = scrollBehavior
) )
}, },

View File

@ -46,7 +46,8 @@ import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.sadellie.unitto.core.base.BuildConfig import com.sadellie.unitto.core.base.BuildConfig
import com.sadellie.unitto.core.ui.R import com.sadellie.unitto.core.ui.R
import com.sadellie.unitto.core.ui.common.UnittoLargeTopAppBar import com.sadellie.unitto.core.ui.common.NavigateUpButton
import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar
import com.sadellie.unitto.core.ui.openLink import com.sadellie.unitto.core.ui.openLink
@Composable @Composable
@ -60,9 +61,9 @@ internal fun AboutScreen(
var aboutItemClick: Int by rememberSaveable { mutableStateOf(0) } var aboutItemClick: Int by rememberSaveable { mutableStateOf(0) }
var showDialog: Boolean by rememberSaveable { mutableStateOf(false) } var showDialog: Boolean by rememberSaveable { mutableStateOf(false) }
UnittoLargeTopAppBar( UnittoScreenWithLargeTopBar(
title = stringResource(R.string.about_unitto), title = stringResource(R.string.about_unitto),
navigateUpAction = navigateUpAction navigationIcon = { NavigateUpButton(navigateUpAction) }
) { padding -> ) { padding ->
LazyColumn(contentPadding = padding) { LazyColumn(contentPadding = padding) {
// CURRENCY RATE NOTE // CURRENCY RATE NOTE

View File

@ -49,7 +49,8 @@ import com.sadellie.unitto.core.base.SEPARATORS
import com.sadellie.unitto.core.base.TOP_LEVEL_DESTINATIONS import com.sadellie.unitto.core.base.TOP_LEVEL_DESTINATIONS
import com.sadellie.unitto.core.ui.R import com.sadellie.unitto.core.ui.R
import com.sadellie.unitto.core.ui.common.Header import com.sadellie.unitto.core.ui.common.Header
import com.sadellie.unitto.core.ui.common.UnittoLargeTopAppBar import com.sadellie.unitto.core.ui.common.MenuButton
import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar
import com.sadellie.unitto.core.ui.common.UnittoListItem import com.sadellie.unitto.core.ui.common.UnittoListItem
import com.sadellie.unitto.core.ui.openLink import com.sadellie.unitto.core.ui.openLink
import com.sadellie.unitto.feature.settings.components.AlertDialogWithList import com.sadellie.unitto.feature.settings.components.AlertDialogWithList
@ -60,7 +61,7 @@ import com.sadellie.unitto.feature.settings.navigation.unitsGroupRoute
@Composable @Composable
internal fun SettingsScreen( internal fun SettingsScreen(
viewModel: SettingsViewModel, viewModel: SettingsViewModel,
navigateUpAction: () -> Unit, menuButtonClick: () -> Unit,
navControllerAction: (String) -> Unit navControllerAction: (String) -> Unit
) { ) {
val mContext = LocalContext.current val mContext = LocalContext.current
@ -69,9 +70,9 @@ internal fun SettingsScreen(
mutableStateOf(DialogState.NONE) mutableStateOf(DialogState.NONE)
} }
UnittoLargeTopAppBar( UnittoScreenWithLargeTopBar(
title = stringResource(R.string.settings_screen), title = stringResource(R.string.settings_screen),
navigateUpAction = navigateUpAction navigationIcon = { MenuButton(menuButtonClick) }
) { padding -> ) { padding ->
LazyColumn(contentPadding = padding) { LazyColumn(contentPadding = padding) {

View File

@ -31,9 +31,10 @@ import androidx.compose.material.icons.filled.Palette
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import com.sadellie.unitto.core.ui.common.UnittoLargeTopAppBar import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar
import com.sadellie.unitto.core.ui.common.UnittoListItem import com.sadellie.unitto.core.ui.common.UnittoListItem
import com.sadellie.unitto.core.ui.R import com.sadellie.unitto.core.ui.R
import com.sadellie.unitto.core.ui.common.NavigateUpButton
import io.github.sadellie.themmo.ThemingMode import io.github.sadellie.themmo.ThemingMode
import io.github.sadellie.themmo.ThemmoController import io.github.sadellie.themmo.ThemmoController
@ -43,9 +44,9 @@ internal fun ThemesScreen(
themmoController: ThemmoController, themmoController: ThemmoController,
viewModel: SettingsViewModel viewModel: SettingsViewModel
) { ) {
UnittoLargeTopAppBar( UnittoScreenWithLargeTopBar(
title = stringResource(R.string.theme_setting), title = stringResource(R.string.theme_setting),
navigateUpAction = navigateUpAction navigationIcon = { NavigateUpButton(navigateUpAction) }
) { paddingValues -> ) { paddingValues ->
LazyColumn(contentPadding = paddingValues) { LazyColumn(contentPadding = paddingValues) {
item { item {

View File

@ -37,7 +37,8 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sadellie.unitto.core.ui.R import com.sadellie.unitto.core.ui.R
import com.sadellie.unitto.core.ui.common.UnittoLargeTopAppBar import com.sadellie.unitto.core.ui.common.NavigateUpButton
import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar
import com.sadellie.unitto.core.ui.openLink import com.sadellie.unitto.core.ui.openLink
import com.sadellie.unitto.data.licenses.ALL_LIBRARIES import com.sadellie.unitto.data.licenses.ALL_LIBRARIES
@ -53,9 +54,9 @@ internal fun ThirdPartyLicensesScreen(
) { ) {
val mContext = LocalContext.current val mContext = LocalContext.current
UnittoLargeTopAppBar( UnittoScreenWithLargeTopBar(
title = stringResource(R.string.third_party_licenses), title = stringResource(R.string.third_party_licenses),
navigateUpAction = navigateUpAction navigationIcon = { NavigateUpButton(navigateUpAction) }
) { padding -> ) { padding ->
LazyColumn( LazyColumn(
verticalArrangement = Arrangement.spacedBy(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp),

View File

@ -48,8 +48,9 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sadellie.unitto.core.ui.common.Header import com.sadellie.unitto.core.ui.common.Header
import com.sadellie.unitto.core.ui.common.UnittoLargeTopAppBar import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar
import com.sadellie.unitto.core.ui.R import com.sadellie.unitto.core.ui.R
import com.sadellie.unitto.core.ui.common.NavigateUpButton
import org.burnoutcrew.reorderable.ReorderableItem import org.burnoutcrew.reorderable.ReorderableItem
import org.burnoutcrew.reorderable.detectReorder import org.burnoutcrew.reorderable.detectReorder
import org.burnoutcrew.reorderable.detectReorderAfterLongPress import org.burnoutcrew.reorderable.detectReorderAfterLongPress
@ -61,9 +62,9 @@ internal fun UnitGroupsScreen(
viewModel: SettingsViewModel, viewModel: SettingsViewModel,
navigateUpAction: () -> Unit navigateUpAction: () -> Unit
) { ) {
UnittoLargeTopAppBar( UnittoScreenWithLargeTopBar(
title = stringResource(R.string.unit_groups_setting), title = stringResource(R.string.unit_groups_setting),
navigateUpAction = navigateUpAction navigationIcon = { NavigateUpButton(navigateUpAction) }
) { paddingValues -> ) { paddingValues ->
val shownUnits = viewModel.shownUnitGroups.collectAsState() val shownUnits = viewModel.shownUnitGroups.collectAsState()

View File

@ -21,8 +21,10 @@ package com.sadellie.unitto.feature.settings.navigation
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.NavOptionsBuilder
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.navigation import androidx.navigation.compose.navigation
import com.sadellie.unitto.core.base.TopLevelDestinations
import com.sadellie.unitto.feature.settings.AboutScreen import com.sadellie.unitto.feature.settings.AboutScreen
import com.sadellie.unitto.feature.settings.SettingsScreen import com.sadellie.unitto.feature.settings.SettingsScreen
import com.sadellie.unitto.feature.settings.SettingsViewModel import com.sadellie.unitto.feature.settings.SettingsViewModel
@ -31,15 +33,15 @@ import com.sadellie.unitto.feature.settings.ThirdPartyLicensesScreen
import com.sadellie.unitto.feature.settings.UnitGroupsScreen import com.sadellie.unitto.feature.settings.UnitGroupsScreen
import io.github.sadellie.themmo.ThemmoController import io.github.sadellie.themmo.ThemmoController
const val settingsGraph = "settings_graph" private val settingsGraph: String by lazy { TopLevelDestinations.Settings.route }
private const val settingsRoute = "settings_route" private const val settingsRoute = "settings_route"
internal const val themesRoute = "themes_route" internal const val themesRoute = "themes_route"
internal const val unitsGroupRoute = "units_group_route" internal const val unitsGroupRoute = "units_group_route"
internal const val thirdPartyRoute = "third_party_route" internal const val thirdPartyRoute = "third_party_route"
internal const val aboutRoute = "about_route" internal const val aboutRoute = "about_route"
fun NavController.navigateToSettings() { fun NavController.navigateToSettings(builder: NavOptionsBuilder.() -> Unit) {
navigate(settingsRoute) navigate(settingsRoute, builder)
} }
fun NavController.navigateToUnitGroups() { fun NavController.navigateToUnitGroups() {
@ -49,13 +51,14 @@ fun NavController.navigateToUnitGroups() {
fun NavGraphBuilder.settingGraph( fun NavGraphBuilder.settingGraph(
settingsViewModel: SettingsViewModel, settingsViewModel: SettingsViewModel,
themmoController: ThemmoController, themmoController: ThemmoController,
navController: NavHostController navController: NavHostController,
menuButtonClick: () -> Unit
) { ) {
navigation(settingsRoute, settingsGraph) { navigation(settingsRoute, settingsGraph) {
composable(settingsRoute) { composable(settingsRoute) {
SettingsScreen( SettingsScreen(
viewModel = settingsViewModel, viewModel = settingsViewModel,
navigateUpAction = { navController.navigateUp() } menuButtonClick = menuButtonClick
) { route -> navController.navigate(route) } ) { route -> navController.navigate(route) }
} }