Получение KClass маршрута из назначения NavController в Jetpack Compose

Проблема

Я создаю активность с выдвижным меню. Я использую библиотеку androidx.navigation:navigation-compose для навигации между вкладками. Но когда я нажимаю "Назад", экран переключается на последний, как и должно быть, но выделенный элемент в меню не поменялся.

Видео

package com.example.app.ui

import android.content.pm.PackageManager
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.os.Parcel
import android.os.Parcelable
import android.util.Log
import android.view.ViewTreeObserver
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Shortcut
import androidx.compose.material.icons.filled.Android
import androidx.compose.material.icons.filled.Apps
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.outlined.PhonelinkLock
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.NavigationDrawerItem
import androidx.compose.material3.NavigationDrawerItemDefaults
import androidx.compose.material3.PermanentDrawerSheet
import androidx.compose.material3.PermanentNavigationDrawer
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidViewBinding
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import androidx.core.content.ContextCompat
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.ui.AppBarConfiguration
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import com.example.android.Website
import com.example.android.WidthSizeClass
import com.example.android.compareTo
import com.example.android.left
import com.example.android.link
import com.example.android.right
import com.example.android.unsupported
import com.example.android.widthSizeClass
import com.example.app.BaseActivity
import com.example.app.R
import com.example.app.Settings.exitDsa
import com.example.app.ui.main.HomeScreen
import com.example.app.ui.main.SettingsScreen

class MainActivity : BaseActivity(
    backButtonBehavior = Companion.BackButtonBehavior.DEFAULT,
    savedInstanceStateEnabled = true
) {
    sealed class BaseScreen

    @Serializable sealed class Screen(
        val label: String,
        val index: Int,
        @Transient val icon: ImageVector = unsupported
    ): BaseScreen(), Parcelable {
        override fun describeContents() = 0

        override fun writeToParcel(dest: Parcel, flags: Int) {
            dest.writeInt(index)
        }

        companion object {
            operator fun get(index: Int) = when (index) {
                0 -> Home
                1 -> Settings
                2 -> Website
                else -> {
                    Log.e("MainActivity", "Unknown screen index: $index")
                    null
                }
            }

            @Suppress("unused")
            @JvmField
            val CREATOR = object: Parcelable.Creator<Screen?> {
                override fun createFromParcel(`in`: Parcel) = Screen[`in`.readInt()]
                override fun newArray(size: Int) = arrayOfNulls<Screen?>(size)
            }
        }

        override fun equals(other: Any?): Boolean {
            return other is Screen && label == other.label
        }

        override fun hashCode(): Int {
            return label.hashCode()
        }

        class Label(val label: String): BaseScreen()

        @Serializable data object Home: Screen("Home", 0, Icons.Filled.Home)
        @Serializable data object Settings: Screen("Settings", 1, Icons.Filled.Settings)
        @Serializable data object Website: Screen("Website", 2, Icons.Outlined.Website)
    }

    @OptIn(ExperimentalMaterial3Api::class)
    @Composable
    @PhonePreview
    fun MainScreen() {
        AppTheme(consumeWindowInsets = true) {
            val drawerState = rememberDrawerState(DrawerValue.Closed)
            val scope = rememberCoroutineScope()
            var selectedItem by rememberSaveable { mutableStateOf<Screen>(Screen.Home) }
            val navController = rememberNavController()

            val isScreenBig = currentWindowAdaptiveInfo().widthSizeClass >= WidthSizeClass.MEDIUM

            val drawerContent: @Composable ColumnScope.() -> Unit = {
                Column(Modifier.verticalScroll(rememberScrollState())) {
                    ConstraintLayout(
                        Modifier
                            .padding(12.dp)
                            .fillMaxWidth()
                    ) {
                        val (icon, title, subtitle) = createRefs()
                        Image(
                            painter = painterResource(R.drawable.app_icon),
                            contentDescription = stringResource(R.string.app_name),
                            modifier = Modifier
                                .clip(RoundedCornerShape(30.dp))
                                .constrainAs(icon) {
                                    top link parent.top
                                    left link parent.left
                                }
                        )
                        Text(
                            text = stringResource(R.string.app_name),
                            style = MaterialTheme.typography.titleMedium,
                            modifier = Modifier
                                .constrainAs(title) {
                                    top link parent.top
                                    right link parent.right
                                    left link icon.right
                                    width = Dimension.fillToConstraints
                                }
                                .padding(start = 12.dp)
                        )
                        Text(
                            text = stringResource(R.string.app_desc),
                            style = MaterialTheme.typography.bodySmall,
                            modifier = Modifier
                                .constrainAs(subtitle) {
                                    top link title.bottom
                                    right link parent.right
                                    left link icon.right
                                    width = Dimension.fillToConstraints
                                }
                                .padding(start = 12.dp)
                        )
                    }
                    val items = listOf(
                        Screen.Label("Main"),
                        Screen.Home,
                        Screen.Settings,
                        Screen.Website
                    )
                    items.forEach { item ->
                        when (item) {
                            is Screen -> NavigationDrawerItem(
                                icon = { Icon(item.icon, contentDescription = null) },
                                label = { Text(item.label) },
                                selected = item == selectedItem,
                                onClick = {
                                    scope.launch { drawerState.close() }
                                    if (selectedItem != item) {
                                        selectedItem = item
                                        navController.navigate(item)
                                    }
                                },
                                modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
                            )
                            is Screen.Label -> {
                                Text(
                                    text = item.label,
                                    style = MaterialTheme.typography.titleSmall,
                                    color = MaterialTheme.colorScheme.primary,
                                    modifier = Modifier.padding(top = 12.dp, bottom = 12.dp, start = 28.dp, end = 36.dp)
                                )
                            }
                        }
                    }
                    Spacer(Modifier.height(12.dp))
                }
            }

            val content = @Composable {
                Scaffold(
                    topBar = {
                        TopAppBar(
                            title = {
                                Text(selectedItem.label)
                            },
                            navigationIcon = {
                                if (!isScreenBig) {
                                    IconButton(
                                        onClick = {
                                            scope.launch {
                                                drawerState.open()
                                            }
                                        }
                                    ) {
                                        Icon(
                                            imageVector = Icons.Filled.Menu,
                                            contentDescription = stringResource(R.string.menu)
                                        )
                                    }
                                }
                            }
                        )
                    },
                ) { innerPadding ->
                    val start by remember { mutableStateOf(selectedItem) }
                    NavHost(
                        navController = navController,
                        startDestination = start,
                        modifier = Modifier.padding(innerPadding)
                    ) {
                        composable<Screen.Home> { HomeScreen() }
                        composable<Screen.Settings> { SettingsScreen() }
                        composable<Screen.Website> {
                            // TODO
                        }
                    }
                }
            }

            if (isScreenBig) {
                PermanentNavigationDrawer(
                    drawerContent = {
                        PermanentDrawerSheet(
                            drawerContainerColor = MaterialTheme.colorScheme.surfaceContainer,
                            content = drawerContent,
                            drawerShape = RoundedCornerShape(0.dp, 20.dp, 20.dp, 0.dp)
                        )
                    },
                    content = content
                )
            } else {
                ModalNavigationDrawer(
                    drawerState = drawerState,
                    drawerContent = {
                        ModalDrawerSheet(
                            drawerContainerColor = MaterialTheme.colorScheme.surfaceContainer,
                            drawerState = drawerState,
                            modifier = Modifier.widthIn(max = 360.dp),
                            content = drawerContent
                        )
                    },
                    content = content
                )
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            enableEdgeToEdge()
            window.isNavigationBarContrastEnforced = false
        }

        setContent {
            MainScreen()
        }
    }
}

Что я пробовал

Я видел этот ответ, но он не подходит, поскольку у меня маршрут KClass, а не String.

Вопрос

Как я могу получить KClass текущего маршрута в NavController? Например:

val currentRouteClass = navController.currentDestination?.routeClass

Если я сделал что-то не так, пожалуйста сообщите мне.


Ответы (0 шт):