Заложена навигация с передачей параметров и сохранением состояний
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.Wallenc"
|
android:theme="@style/Theme.Wallenc"
|
||||||
|
android:enableOnBackInvokedCallback="true"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ coreKtx = "1.15.0"
|
|||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
junitVersion = "1.2.1"
|
junitVersion = "1.2.1"
|
||||||
espressoCore = "3.6.1"
|
espressoCore = "3.6.1"
|
||||||
|
kotlinReflect = "2.0.21"
|
||||||
kotlinxCoroutinesCore = "1.9.0"
|
kotlinxCoroutinesCore = "1.9.0"
|
||||||
kotlinxSerializationJson = "1.7.3"
|
kotlinxSerializationJson = "1.7.3"
|
||||||
lifecycleRuntimeKtx = "2.8.7"
|
lifecycleRuntimeKtx = "2.8.7"
|
||||||
@@ -23,6 +24,7 @@ material = "1.12.0"
|
|||||||
runtimeAndroid = "1.7.5"
|
runtimeAndroid = "1.7.5"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlinReflect" }
|
||||||
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" }
|
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" }
|
||||||
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
|
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
|
||||||
navigation = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" }
|
navigation = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" }
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ plugins {
|
|||||||
alias(libs.plugins.compose.compiler)
|
alias(libs.plugins.compose.compiler)
|
||||||
alias(libs.plugins.dagger.hilt)
|
alias(libs.plugins.dagger.hilt)
|
||||||
alias(libs.plugins.jetbrains.kotlin.serialization)
|
alias(libs.plugins.jetbrains.kotlin.serialization)
|
||||||
|
id("kotlin-parcelize")
|
||||||
alias(libs.plugins.ksp)
|
alias(libs.plugins.ksp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +55,7 @@ dependencies {
|
|||||||
ksp(libs.dagger.hilt.compiler)
|
ksp(libs.dagger.hilt.compiler)
|
||||||
|
|
||||||
implementation(libs.kotlinx.serialization.json)
|
implementation(libs.kotlinx.serialization.json)
|
||||||
|
implementation(libs.kotlin.reflect)
|
||||||
|
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
|
|||||||
@@ -1,14 +1,30 @@
|
|||||||
package com.github.nullptroma.wallenc.presentation
|
package com.github.nullptroma.wallenc.presentation
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.Menu
|
||||||
|
import androidx.compose.material.icons.rounded.Settings
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.NavigationBar
|
||||||
|
import androidx.compose.material3.NavigationBarItem
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.rememberNavController
|
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.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.settings.SettingsRoute
|
import com.github.nullptroma.wallenc.presentation.screens.settings.SettingsRoute
|
||||||
@@ -25,17 +41,70 @@ fun WallencUi() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun WallencNavRoot() {
|
fun WallencNavRoot() {
|
||||||
val navController = rememberNavController()
|
val navState = rememberNavigationState()
|
||||||
Scaffold { innerPaddings ->
|
|
||||||
NavHost(navController, startDestination = MainRoute()) {
|
val topLevelScreenRoutes = rememberSaveable {
|
||||||
composable<MainRoute> {
|
mutableMapOf(
|
||||||
|
MainRoute::class.qualifiedName!! to MainRoute(),
|
||||||
|
SettingsRoute::class.qualifiedName!! to SettingsRoute("Base settings")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Все пункты меню верхнего уровня
|
||||||
|
val topLevelNavBarItems = remember {
|
||||||
|
listOf(
|
||||||
|
NavBarItem("Main", MainRoute::class.qualifiedName!!, Icons.Rounded.Menu),
|
||||||
|
NavBarItem("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(
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
it.icon,
|
||||||
|
contentDescription = it.name
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = { Text(it.name) },
|
||||||
|
selected = currentRoute?.startsWith(routeClassName) == true,
|
||||||
|
onClick = {
|
||||||
|
var route = topLevelScreenRoutes[it.screenRouteClass]
|
||||||
|
if(route == null)
|
||||||
|
throw NoSuchElementException("Screen route of type ${it.screenRouteClass} no found")
|
||||||
|
if(currentRoute?.startsWith(routeClassName) != true)
|
||||||
|
navState.navigationTo(route)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) { innerPaddings ->
|
||||||
|
NavHost(navState.navHostController, startDestination = topLevelScreenRoutes[MainRoute::class.qualifiedName]!!) {
|
||||||
|
composable<MainRoute>(enterTransition = {
|
||||||
|
fadeIn(tween(200))
|
||||||
|
}, exitTransition = {
|
||||||
|
fadeOut(tween(200))
|
||||||
|
}) {
|
||||||
MainScreen(Modifier.padding(innerPaddings), onSettingsRoute = { settingsRoute ->
|
MainScreen(Modifier.padding(innerPaddings), onSettingsRoute = { settingsRoute ->
|
||||||
navController.navigate(settingsRoute)
|
topLevelScreenRoutes[settingsRoute::class.qualifiedName!!] = settingsRoute
|
||||||
|
navState.navigationTo(settingsRoute)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
composable<SettingsRoute> {
|
composable<SettingsRoute>(enterTransition = {
|
||||||
|
fadeIn(tween(200))
|
||||||
|
}, exitTransition = {
|
||||||
|
fadeOut(tween(200))
|
||||||
|
}) {
|
||||||
val route: SettingsRoute = it.toRoute()
|
val route: SettingsRoute = it.toRoute()
|
||||||
SettingsScreen(Modifier.padding(innerPaddings), route.text)
|
SettingsScreen(Modifier.padding(innerPaddings), route.text)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.github.nullptroma.wallenc.presentation.navigation
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
|
||||||
|
data class NavBarItem(val name: String, val screenRouteClass: String, val icon: ImageVector)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.github.nullptroma.wallenc.presentation.navigation
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import com.github.nullptroma.wallenc.presentation.screens.ScreenRoute
|
||||||
|
|
||||||
|
|
||||||
|
class NavigationState(
|
||||||
|
val navHostController: NavHostController
|
||||||
|
) {
|
||||||
|
fun navigationTo(route: ScreenRoute) {
|
||||||
|
navHostController.navigate(route) {
|
||||||
|
popUpTo(navHostController.graph.findStartDestination().id)
|
||||||
|
launchSingleTop = true
|
||||||
|
restoreState = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun rememberNavigationState(
|
||||||
|
navHostController: NavHostController? = null
|
||||||
|
): NavigationState {
|
||||||
|
val controller = navHostController ?: rememberNavController()
|
||||||
|
return remember { NavigationState(controller) }
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.github.nullptroma.wallenc.presentation.screens
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
abstract class ScreenRoute() : Parcelable
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
package com.github.nullptroma.wallenc.presentation.screens.main
|
package com.github.nullptroma.wallenc.presentation.screens.main
|
||||||
|
|
||||||
|
import com.github.nullptroma.wallenc.presentation.screens.ScreenRoute
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable class MainRoute
|
@Serializable
|
||||||
|
@Parcelize
|
||||||
|
class MainRoute: ScreenRoute()
|
||||||
@@ -9,10 +9,11 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.material3.TextField
|
import androidx.compose.material3.TextField
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.github.nullptroma.wallenc.presentation.screens.settings.SettingsRoute
|
import com.github.nullptroma.wallenc.presentation.screens.settings.SettingsRoute
|
||||||
|
|
||||||
@@ -22,12 +23,14 @@ fun MainScreen(modifier: Modifier = Modifier,
|
|||||||
viewModel: MainViewModel = hiltViewModel(),
|
viewModel: MainViewModel = hiltViewModel(),
|
||||||
onSettingsRoute: (SettingsRoute) -> Unit) {
|
onSettingsRoute: (SettingsRoute) -> Unit) {
|
||||||
val state = viewModel.stateFlow
|
val state = viewModel.stateFlow
|
||||||
var text by remember { mutableStateOf("") }
|
var text by rememberSaveable { mutableStateOf("") }
|
||||||
|
val focusManager = LocalFocusManager.current
|
||||||
Column(modifier = modifier.imePadding().fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) {
|
Column(modifier = modifier.imePadding().fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) {
|
||||||
TextField(text, onValueChange = { s ->
|
TextField(text, onValueChange = { s ->
|
||||||
text = s
|
text = s
|
||||||
})
|
})
|
||||||
Button( onClick = {
|
Button( onClick = {
|
||||||
|
focusManager.clearFocus()
|
||||||
onSettingsRoute(SettingsRoute(text))
|
onSettingsRoute(SettingsRoute(text))
|
||||||
}) {
|
}) {
|
||||||
Text("Press Me!")
|
Text("Press Me!")
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package com.github.nullptroma.wallenc.presentation.screens.settings
|
package com.github.nullptroma.wallenc.presentation.screens.settings
|
||||||
|
|
||||||
|
import com.github.nullptroma.wallenc.presentation.screens.ScreenRoute
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable class SettingsRoute(val text: String)
|
@Serializable
|
||||||
|
@Parcelize
|
||||||
|
class SettingsRoute(val text: String): ScreenRoute()
|
||||||
|
|||||||
Reference in New Issue
Block a user