Обновлена навигация
This commit is contained in:
@@ -3,6 +3,7 @@ package com.github.nullptroma.wallenc.presentation
|
|||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
|
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.Icons
|
||||||
import androidx.compose.material.icons.rounded.Menu
|
import androidx.compose.material.icons.rounded.Menu
|
||||||
@@ -19,14 +20,18 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
import androidx.navigation.toRoute
|
import androidx.navigation.toRoute
|
||||||
import com.github.nullptroma.wallenc.presentation.navigation.NavBarItem
|
import com.github.nullptroma.wallenc.presentation.navigation.NavBarItemData
|
||||||
import com.github.nullptroma.wallenc.presentation.navigation.rememberNavigationState
|
import com.github.nullptroma.wallenc.presentation.navigation.rememberNavigationState
|
||||||
import com.github.nullptroma.wallenc.presentation.screens.main.MainRoute
|
import com.github.nullptroma.wallenc.presentation.screens.main.MainRoute
|
||||||
import com.github.nullptroma.wallenc.presentation.screens.main.MainScreen
|
import com.github.nullptroma.wallenc.presentation.screens.main.MainScreen
|
||||||
|
import com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault.LocalVaultRoute
|
||||||
|
import com.github.nullptroma.wallenc.presentation.screens.main.screens.remotes.RemoteVaultsRoute
|
||||||
import com.github.nullptroma.wallenc.presentation.screens.settings.SettingsRoute
|
import com.github.nullptroma.wallenc.presentation.screens.settings.SettingsRoute
|
||||||
import com.github.nullptroma.wallenc.presentation.screens.settings.SettingsScreen
|
import com.github.nullptroma.wallenc.presentation.screens.settings.SettingsScreen
|
||||||
import com.github.nullptroma.wallenc.presentation.theme.WallencTheme
|
import com.github.nullptroma.wallenc.presentation.theme.WallencTheme
|
||||||
@@ -46,59 +51,75 @@ fun WallencUi() {
|
|||||||
fun WallencNavRoot() {
|
fun WallencNavRoot() {
|
||||||
val navState = rememberNavigationState()
|
val navState = rememberNavigationState()
|
||||||
|
|
||||||
val topLevelScreenRoutes = rememberSaveable {
|
val mainNavState = rememberNavigationState()
|
||||||
|
val mainScreenRoutes = rememberSaveable {
|
||||||
|
mutableMapOf(
|
||||||
|
LocalVaultRoute::class.qualifiedName!! to LocalVaultRoute(),
|
||||||
|
RemoteVaultsRoute::class.qualifiedName!! to RemoteVaultsRoute()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val topLevelRoutes = rememberSaveable {
|
||||||
mutableMapOf(
|
mutableMapOf(
|
||||||
MainRoute::class.qualifiedName!! to MainRoute(),
|
MainRoute::class.qualifiedName!! to MainRoute(),
|
||||||
SettingsRoute::class.qualifiedName!! to SettingsRoute("Base settings")
|
SettingsRoute::class.qualifiedName!! to SettingsRoute("Base settings")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Все пункты меню верхнего уровня
|
|
||||||
val topLevelNavBarItems = remember {
|
val topLevelNavBarItems = remember {
|
||||||
listOf(
|
mapOf(
|
||||||
NavBarItem("Main", MainRoute::class.qualifiedName!!, Icons.Rounded.Menu),
|
MainRoute::class.qualifiedName!! to NavBarItemData(
|
||||||
NavBarItem("Settings", SettingsRoute::class.qualifiedName!!, Icons.Rounded.Settings)
|
R.string.nav_label_main, MainRoute::class.qualifiedName!!, Icons.Rounded.Menu
|
||||||
|
),
|
||||||
|
SettingsRoute::class.qualifiedName!! to NavBarItemData(
|
||||||
|
R.string.nav_label_settings,
|
||||||
|
SettingsRoute::class.qualifiedName!!,
|
||||||
|
Icons.Rounded.Settings
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
|
||||||
bottomBar = {
|
|
||||||
NavigationBar {
|
|
||||||
val navBackStackEntry by navState.navHostController.currentBackStackEntryAsState()
|
|
||||||
val currentRoute = navBackStackEntry?.destination?.route
|
|
||||||
topLevelNavBarItems.forEach {
|
|
||||||
val routeClassName = it.screenRouteClass
|
|
||||||
|
|
||||||
NavigationBarItem(
|
Scaffold(bottomBar = {
|
||||||
icon = {
|
NavigationBar(modifier = Modifier.height(64.dp)) {
|
||||||
Icon(
|
val navBackStackEntry by navState.navHostController.currentBackStackEntryAsState()
|
||||||
it.icon,
|
val currentRoute = navBackStackEntry?.destination?.route
|
||||||
contentDescription = it.name
|
topLevelNavBarItems.forEach {
|
||||||
)
|
val routeClassName = it.key
|
||||||
},
|
val navBarItemData = it.value
|
||||||
label = { Text(it.name) },
|
NavigationBarItem(icon = {
|
||||||
selected = currentRoute?.startsWith(routeClassName) == true,
|
if (navBarItemData.icon != null) Icon(
|
||||||
onClick = {
|
navBarItemData.icon,
|
||||||
var route = topLevelScreenRoutes[it.screenRouteClass]
|
contentDescription = stringResource(navBarItemData.nameStringResourceId)
|
||||||
if(route == null)
|
|
||||||
throw NoSuchElementException("Screen route of type ${it.screenRouteClass} no found")
|
|
||||||
if(currentRoute?.startsWith(routeClassName) != true)
|
|
||||||
navState.navigationTo(route)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
label = { Text(stringResource(navBarItemData.nameStringResourceId)) },
|
||||||
|
selected = currentRoute?.startsWith(routeClassName) == true,
|
||||||
|
onClick = {
|
||||||
|
var route = topLevelRoutes[navBarItemData.screenRouteClass]
|
||||||
|
if (route == null)
|
||||||
|
throw NullPointerException("Route $route not found")
|
||||||
|
if (currentRoute?.startsWith(routeClassName) != true) navState.navigationTo(
|
||||||
|
route
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}) { innerPaddings ->
|
}
|
||||||
NavHost(navState.navHostController, startDestination = topLevelScreenRoutes[MainRoute::class.qualifiedName]!!) {
|
}) { innerPaddings ->
|
||||||
|
NavHost(
|
||||||
|
navState.navHostController,
|
||||||
|
startDestination = topLevelRoutes[MainRoute::class.qualifiedName]!!
|
||||||
|
) {
|
||||||
composable<MainRoute>(enterTransition = {
|
composable<MainRoute>(enterTransition = {
|
||||||
fadeIn(tween(200))
|
fadeIn(tween(200))
|
||||||
}, exitTransition = {
|
}, exitTransition = {
|
||||||
fadeOut(tween(200))
|
fadeOut(tween(200))
|
||||||
}) {
|
}) {
|
||||||
MainScreen(Modifier.padding(innerPaddings), onSettingsRoute = { settingsRoute ->
|
MainScreen(
|
||||||
topLevelScreenRoutes[settingsRoute::class.qualifiedName!!] = settingsRoute
|
modifier = Modifier.padding(innerPaddings),
|
||||||
navState.navigationTo(settingsRoute)
|
navState = mainNavState,
|
||||||
})
|
routes = mainScreenRoutes
|
||||||
|
)
|
||||||
}
|
}
|
||||||
composable<SettingsRoute>(enterTransition = {
|
composable<SettingsRoute>(enterTransition = {
|
||||||
fadeIn(tween(200))
|
fadeIn(tween(200))
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ package com.github.nullptroma.wallenc.presentation.navigation
|
|||||||
|
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
|
||||||
data class NavBarItem(val name: String, val screenRouteClass: String, val icon: ImageVector)
|
data class NavBarItemData(val nameStringResourceId: Int, val screenRouteClass: String, val icon: ImageVector?)
|
||||||
@@ -6,4 +6,4 @@ import kotlinx.serialization.Serializable
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@Parcelize
|
@Parcelize
|
||||||
class MainRoute: ScreenRoute()
|
open class MainRoute: ScreenRoute()
|
||||||
@@ -1,39 +1,108 @@
|
|||||||
package com.github.nullptroma.wallenc.presentation.screens.main
|
package com.github.nullptroma.wallenc.presentation.screens.main
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.imePadding
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.NavigationBar
|
||||||
|
import androidx.compose.material3.NavigationBarItem
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextField
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.github.nullptroma.wallenc.presentation.screens.settings.SettingsRoute
|
import androidx.navigation.compose.NavHost
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
|
import com.github.nullptroma.wallenc.presentation.R
|
||||||
|
import com.github.nullptroma.wallenc.presentation.navigation.NavBarItemData
|
||||||
|
import com.github.nullptroma.wallenc.presentation.navigation.NavigationState
|
||||||
|
import com.github.nullptroma.wallenc.presentation.navigation.rememberNavigationState
|
||||||
|
import com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault.LocalVaultRoute
|
||||||
|
import com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault.LocalVaultScreen
|
||||||
|
import com.github.nullptroma.wallenc.presentation.screens.main.screens.remotes.RemoteVaultsRoute
|
||||||
|
import com.github.nullptroma.wallenc.presentation.screens.main.screens.remotes.RemoteVaultsScreen
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@androidx.compose.runtime.Composable
|
@androidx.compose.runtime.Composable
|
||||||
fun MainScreen(modifier: Modifier = Modifier,
|
fun MainScreen(
|
||||||
viewModel: MainViewModel = hiltViewModel(),
|
modifier: Modifier = Modifier,
|
||||||
onSettingsRoute: (SettingsRoute) -> Unit) {
|
viewModel: MainViewModel = hiltViewModel(),
|
||||||
val state = viewModel.stateFlow
|
navState: NavigationState = rememberNavigationState(),
|
||||||
var text by rememberSaveable { mutableStateOf("") }
|
routes: MutableMap<String, MainRoute> = rememberSaveable {
|
||||||
val focusManager = LocalFocusManager.current
|
mutableMapOf(
|
||||||
Column(modifier = modifier.imePadding().fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) {
|
LocalVaultRoute::class.qualifiedName!! to LocalVaultRoute(),
|
||||||
TextField(text, onValueChange = { s ->
|
RemoteVaultsRoute::class.qualifiedName!! to RemoteVaultsRoute()
|
||||||
text = s
|
)
|
||||||
})
|
}
|
||||||
Button( onClick = {
|
) {
|
||||||
focusManager.clearFocus()
|
val topLevelNavBarItems = remember {
|
||||||
onSettingsRoute(SettingsRoute(text))
|
mapOf(
|
||||||
}) {
|
LocalVaultRoute::class.qualifiedName!! to NavBarItemData(
|
||||||
Text("Press Me!")
|
R.string.nav_label_local_vault, LocalVaultRoute::class.qualifiedName!!, null
|
||||||
|
),
|
||||||
|
RemoteVaultsRoute::class.qualifiedName!! to NavBarItemData(
|
||||||
|
R.string.nav_label_remote_vaults, RemoteVaultsRoute::class.qualifiedName!!, null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold(modifier = modifier, contentWindowInsets = WindowInsets(0.dp), bottomBar = {
|
||||||
|
Column {
|
||||||
|
NavigationBar(modifier = Modifier.height(48.dp)) {
|
||||||
|
val navBackStackEntry by navState.navHostController.currentBackStackEntryAsState()
|
||||||
|
val currentRoute = navBackStackEntry?.destination?.route
|
||||||
|
topLevelNavBarItems.forEach {
|
||||||
|
val routeClassName = it.key
|
||||||
|
val navBarItemData = it.value
|
||||||
|
NavigationBarItem(modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.fillMaxHeight(),
|
||||||
|
icon = { Text(stringResource(navBarItemData.nameStringResourceId)) },
|
||||||
|
selected = currentRoute?.startsWith(routeClassName) == true,
|
||||||
|
onClick = {
|
||||||
|
var route = routes[navBarItemData.screenRouteClass]
|
||||||
|
if (route == null)
|
||||||
|
throw NullPointerException("Route $route not found")
|
||||||
|
if (currentRoute?.startsWith(routeClassName) != true) navState.navigationTo(
|
||||||
|
route
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HorizontalDivider()
|
||||||
|
}
|
||||||
|
}) { innerPaddings ->
|
||||||
|
|
||||||
|
NavHost(
|
||||||
|
navState.navHostController,
|
||||||
|
startDestination = routes[LocalVaultRoute::class.qualifiedName]!!
|
||||||
|
) {
|
||||||
|
composable<LocalVaultRoute>(enterTransition = {
|
||||||
|
fadeIn(tween(200))
|
||||||
|
}, exitTransition = {
|
||||||
|
fadeOut(tween(200))
|
||||||
|
}) {
|
||||||
|
LocalVaultScreen(modifier = Modifier.padding(innerPaddings))
|
||||||
|
}
|
||||||
|
composable<RemoteVaultsRoute>(enterTransition = {
|
||||||
|
fadeIn(tween(200))
|
||||||
|
}, exitTransition = {
|
||||||
|
fadeOut(tween(200))
|
||||||
|
}) {
|
||||||
|
RemoteVaultsScreen(modifier = Modifier.padding(innerPaddings))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,9 @@
|
|||||||
package com.github.nullptroma.wallenc.presentation.screens.main
|
package com.github.nullptroma.wallenc.presentation.screens.main
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.github.nullptroma.wallenc.domain.usecases.TestUseCase
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class MainViewModel @javax.inject.Inject constructor(
|
class MainViewModel @javax.inject.Inject constructor(): ViewModel() {
|
||||||
testUseCase: TestUseCase,
|
|
||||||
testUseCase2: TestUseCase,
|
|
||||||
testUseCase3: TestUseCase,
|
|
||||||
): ViewModel() {
|
|
||||||
val stateFlow = MainScreenState("${testUseCase3.meta.name}, number ${testUseCase3.id}")
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault
|
||||||
|
|
||||||
|
import com.github.nullptroma.wallenc.presentation.screens.main.MainRoute
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@Parcelize
|
||||||
|
class LocalVaultRoute: MainRoute()
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault
|
||||||
|
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun LocalVaultScreen(modifier: Modifier = Modifier,
|
||||||
|
viewModel: LocalVaultViewModel = hiltViewModel()) {
|
||||||
|
Text("Local vault screen")
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault
|
||||||
|
|
||||||
|
data class LocalVaultScreenState(val value: String)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class LocalVaultViewModel @Inject constructor(): ViewModel() {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.github.nullptroma.wallenc.presentation.screens.main.screens.remotes
|
||||||
|
|
||||||
|
import com.github.nullptroma.wallenc.presentation.screens.main.MainRoute
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@Parcelize
|
||||||
|
class RemoteVaultsRoute: MainRoute()
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.github.nullptroma.wallenc.presentation.screens.main.screens.remotes
|
||||||
|
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun RemoteVaultsScreen(modifier: Modifier = Modifier,
|
||||||
|
viewModel: RemoteVaultsViewModel = hiltViewModel()) {
|
||||||
|
Text("Remote vault screen")
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package com.github.nullptroma.wallenc.presentation.screens.main.screens.remotes
|
||||||
|
|
||||||
|
data class RemoteVaultsScreenState(val value: String)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.github.nullptroma.wallenc.presentation.screens.main.screens.remotes
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class RemoteVaultsViewModel @Inject constructor(): ViewModel() {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
<string name="nav_label_local_vault">Local</string>
|
||||||
|
<string name="nav_label_remote_vaults">Remotes</string>
|
||||||
|
<string name="nav_label_main">Main</string>
|
||||||
|
<string name="nav_label_settings">Settings</string>
|
||||||
|
|
||||||
<string name="settings_title">Settings Screen Title!</string>
|
<string name="settings_title">Settings Screen Title!</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user