android - Jetpack Compose 中的主页和身份验证导航流程

标签 android kotlin android-jetpack-compose android-jetpack jetpack-compose-navigation

我的 Compose 应用程序中的导航出现问题。因此,从一开始我想要两个导航图:一个用于身份验证相关的内容,第二个用于主要功能,只有在登录后才能访问。非常典型的案例。

我想使用 Android 12 中的 Splash Screen API,因此在我的主要 Activity 中我可以执行以下操作:

class AppMainActivity : ComponentActivity() {

    private val viewModel by viewModels<SplashViewModel>()
    private var userAuthState: UserAuthState = UserAuthState.UNKNOWN

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

        viewModel.onEvent(SplashEvent.CheckAuthentication)

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.isAuthenticated.collect {
                    userAuthState = it
                }
            }
        }

        setContent {
            CarsLocalizerTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    val scaffoldState = rememberScaffoldState()
                    val navController = rememberNavController()

                    Scaffold(
                        modifier = Modifier.fillMaxSize(),
                        scaffoldState = scaffoldState
                    ) {
                        val splash = installSplashScreen()
                        splash.setKeepOnScreenCondition {
                            userAuthState != UserAuthState.UNKNOWN
                        }

                        when (userAuthState) {
                            UserAuthState.UNAUTHENTICATED -> {
                                AuthNavigation(
                                    navController = navController,
                                    scaffoldState = scaffoldState
                                )
                            }
                            UserAuthState.AUTHENTICATED -> {
                                HomeScreen(
                                    viewModel = hiltViewModel(),
                                    onLogout = { navController.popBackStack() }
                                )
                            }
                            UserAuthState.UNKNOWN -> {}
                        }
                    }
                }
            }
        }
    }
}

这里我从 View 模型中收集 StateFlow,它描述了用户是否已经通过身份验证。如果身份验证成功,则转到 HomeScreen,其中包含 HomeNavigation。如果未通过身份验证,请转至身份验证导航图。问题是这种方法行不通,因为 Activity 仅创建一次,所以如果我用户登录,则此时

when (userAuthState) {
    UserAuthState.UNAUTHENTICATED -> {
        AuthNavigation(
            navController = navController,
            scaffoldState = scaffoldState
        )
    }
    UserAuthState.AUTHENTICATED -> {
        HomeScreen(
            viewModel = hiltViewModel(),
            onLogout = { navController.popBackStack() }
        )
    }
    UserAuthState.UNKNOWN -> {}
}

不会再被调用。

我试图为我的问题找到一些解决方案,但是找不到任何有用的东西。也许有人以前遇到过这样的问题,或者看到过一些有用的东西?将非常高兴获得任何帮助。

我的其余代码: SplashView模型

@HiltViewModel
class SplashViewModel @Inject constructor(
    private val authenticateUseCase: AuthenticateUseCase
): ViewModel() {

    private val _isAuthenticated = MutableStateFlow<UserAuthState>(value = UserAuthState.UNKNOWN)
    val isAuthenticated: StateFlow<UserAuthState> = _isAuthenticated.asStateFlow()

    fun onEvent(event: SplashEvent) {
        when (event) {
            is SplashEvent.CheckAuthentication -> {
                viewModelScope.launch {
                    val result = authenticateUseCase()
                    when (result) {
                        true -> {
                            _isAuthenticated.emit(UserAuthState.AUTHENTICATED)
                        }
                        false -> {
                            _isAuthenticated.emit(UserAuthState.UNAUTHENTICATED)
                        }
                    }
                }
            }
        }
    }

}

验证导航

@Composable
fun AuthNavigation(
    navController: NavHostController,
    scaffoldState: ScaffoldState
) {
    NavHost(
        navController = navController,
        startDestination = Screen.Login.route,
        modifier = Modifier.fillMaxSize()
    ) {
        composable(Screen.Login.route) {
            LoginScreen(
                onNavigate = { navController.navigate(it) } ,
                onLogin = {
                    navController.popBackStack()
                },
                scaffoldState = scaffoldState,
                viewModel = hiltViewModel()
            )
        }
        composable(Screen.Register.route) {
            RegisterScreen(
                onPopBackstack = { navController.popBackStack() },
                scaffoldState = scaffoldState,
                viewModel = hiltViewModel()
            )
        }
        composable(Screen.Onboarding.route) {
            OnboardingScreen(
                onCompleted = { navController.popBackStack() },
                viewModel = hiltViewModel()
            )
        }
    }
}

主屏幕

@Composable
fun HomeScreen(
    viewModel: HomeViewModel,
    onLogout: () -> Unit
) {
    val navController = rememberNavController()
    val scaffoldState = rememberScaffoldState()

    var appBarTitle by remember {
        mutableStateOf("")
    }

    LaunchedEffect(key1 = true) {
        viewModel.userName.collectLatest {
            appBarTitle = "Hello $it"
        }
    }

    Scaffold(
        scaffoldState = scaffoldState,
        topBar = {
            if (appBarTitle.isEmpty()) {
                AppBarWithName("Hello")
            } else {
                AppBarWithName(appBarTitle)
            }
        },
        bottomBar = { BottomNavigationBar(navController) },
        content = { padding ->
            Box(modifier = Modifier.padding(padding)) {
                HomeNavigation(
                    navController = navController,
                    scaffoldState = scaffoldState,
                    onLogout = { onLogout() }
                )
            }
        }
    )
}

首页导航

@Composable
fun HomeNavigation(
    navController: NavHostController,
    scaffoldState: ScaffoldState,
    onLogout: () -> Unit
) {
    NavHost(
        navController = navController,
        startDestination = Screen.Map.route
    ) {
        composable(Screen.Map.route) {
            MapScreen(viewModel = hiltViewModel())
        }
        composable(Screen.ManageCars.route) {
            ManageCarsScreen(
                viewModel = hiltViewModel(),
                scaffoldState = scaffoldState,
                onAddCar = {
                    navController.navigate(Screen.AddCar.route)
                }
            )
        }
        composable(Screen.AddCar.route) {
            AddCarScreen(
                viewModel = hiltViewModel(),
                onPopBackstack = {
                    navController.popBackStack()
                }
            )
        }
        composable(Screen.Logout.route) {
            LogoutDialogScreen(
                viewModel = hiltViewModel(),
                onLogout = {
                    navController.popBackStack()
                    onLogout()
                },
                onCancel = {
                    navController.popBackStack()
                }
            )
        }
    }
}

最佳答案

您需要将UserAuthState存储为MutableState,这样,当值更新时,setContent会自动重新组合:

private var userAuthState = mutableStateOf(UserAuthState.UNKNOWN)

lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.isAuthenticated.collect { userAuthState.value = it }
    }
}

setContent { 
    when (userAuthState.value) {
        etc....
    } 
}

来自docs :

mutableStateOf creates an observable MutableState, which is an observable type integrated with the compose runtime.

Any changes to value schedules recomposition of any composable functions that read value.

关于android - Jetpack Compose 中的主页和身份验证导航流程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75463575/

相关文章:

android - 如何在 Android 应用程序中嵌入不同的语言?

java - 如何修复android.support.v7.widget.AppCompatButton无法转换为android.view.ViewGroup

android - 使用来自两个 Activity 的不同回收者 View 在 Activity 之间切换

generics - Kotlin泛型和TypeVariable

android - 在 Jetpack Compose 中按字体的上升而不是基线对齐两个文本

android - 为什么 RecyclerView 项目随着滚动而消失

java - 使用 Gradle 构建 Kotlin + Java 9 项目

android - 使用 LiveData 在 SeekBar 和 EditText 中取得进展

android - 如何正确地将图像上传到 Jetpack Compose 中的 LazyList 中的项目?

kotlin - 如何设置条件属性?