Заложена навигация с передачей параметров и сохранением состояний
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Wallenc"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
||||
@@ -5,6 +5,7 @@ coreKtx = "1.15.0"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.2.1"
|
||||
espressoCore = "3.6.1"
|
||||
kotlinReflect = "2.0.21"
|
||||
kotlinxCoroutinesCore = "1.9.0"
|
||||
kotlinxSerializationJson = "1.7.3"
|
||||
lifecycleRuntimeKtx = "2.8.7"
|
||||
@@ -23,6 +24,7 @@ material = "1.12.0"
|
||||
runtimeAndroid = "1.7.5"
|
||||
|
||||
[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-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
|
||||
navigation = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" }
|
||||
|
||||
@@ -4,6 +4,7 @@ plugins {
|
||||
alias(libs.plugins.compose.compiler)
|
||||
alias(libs.plugins.dagger.hilt)
|
||||
alias(libs.plugins.jetbrains.kotlin.serialization)
|
||||
id("kotlin-parcelize")
|
||||
alias(libs.plugins.ksp)
|
||||
}
|
||||
|
||||
@@ -54,6 +55,7 @@ dependencies {
|
||||
ksp(libs.dagger.hilt.compiler)
|
||||
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.kotlin.reflect)
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
|
||||
@@ -1,14 +1,30 @@
|
||||
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.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.Surface
|
||||
import androidx.compose.material3.Text
|
||||
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.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
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.MainScreen
|
||||
import com.github.nullptroma.wallenc.presentation.screens.settings.SettingsRoute
|
||||
@@ -25,17 +41,70 @@ fun WallencUi() {
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun WallencNavRoot() {
|
||||
val navController = rememberNavController()
|
||||
Scaffold { innerPaddings ->
|
||||
NavHost(navController, startDestination = MainRoute()) {
|
||||
composable<MainRoute> {
|
||||
val navState = rememberNavigationState()
|
||||
|
||||
val topLevelScreenRoutes = rememberSaveable {
|
||||
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 ->
|
||||
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()
|
||||
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
|
||||
|
||||
import com.github.nullptroma.wallenc.presentation.screens.ScreenRoute
|
||||
import kotlinx.parcelize.Parcelize
|
||||
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.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
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.platform.LocalFocusManager
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.github.nullptroma.wallenc.presentation.screens.settings.SettingsRoute
|
||||
|
||||
@@ -22,12 +23,14 @@ fun MainScreen(modifier: Modifier = Modifier,
|
||||
viewModel: MainViewModel = hiltViewModel(),
|
||||
onSettingsRoute: (SettingsRoute) -> Unit) {
|
||||
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) {
|
||||
TextField(text, onValueChange = { s ->
|
||||
text = s
|
||||
})
|
||||
Button( onClick = {
|
||||
focusManager.clearFocus()
|
||||
onSettingsRoute(SettingsRoute(text))
|
||||
}) {
|
||||
Text("Press Me!")
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package com.github.nullptroma.wallenc.presentation.screens.settings
|
||||
|
||||
import com.github.nullptroma.wallenc.presentation.screens.ScreenRoute
|
||||
import kotlinx.parcelize.Parcelize
|
||||
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