Вот мой файл MainActivity.kt
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
NavigationHoster()
}
вот функция навхостера
@Composable
fun NavigationHoster(){
val navController = rememberNavController();
NavHost(navController = navController, startDestination = "Home"){
composable("Home"){ HomeScreen(navController = navController)}
composable("Chat"){ ChatScreen()}
}
}
вот функция домашнего экрана
@OptIn(ExperimentalMaterial3Api::class)
//@Preview(showBackground = true, showSystemUi = true)
@Composable
fun HomeScreen(
homeViewModel: HomeViewModel = viewModel(),
navController: NavController
){
val HomeViewState by homeViewModel.uiState.collectAsState()
Scaffold (
topBar = {
TopAppBar(title = {
Text(text = "Chat Wallet", style = MaterialTheme.typography.titleMedium)
}, colors = TopAppBarDefaults.mediumTopAppBarColors(
containerColor = MaterialTheme.colorScheme.primary,
titleContentColor = MaterialTheme.colorScheme.background
))
}
){
paddingValues -> Box(modifier = Modifier.padding(paddingValues).fillMaxSize(), contentAlignment = Alignment.Center) {
IconButton(onClick = { navController.navigate("Chat") }, colors = IconButtonDefaults.iconButtonColors(containerColor = MaterialTheme.colorScheme.primary)) {
Icon(painter = painterResource(id = R.drawable.baseline_chat_24), contentDescription = null, tint = MaterialTheme.colorScheme.background)
}
}
}
}
Вот моя ViewModel
class HomeViewModel : ViewModel(){
private var _uiState = MutableStateFlow(HomeData())
var uiState : StateFlow<HomeData> = _uiState.asStateFlow()
public fun ChangeName() {
_uiState.value = HomeData(UserName = "hello there")
}
}
Эти коды работают хорошо, и я проверил. эта модель просмотра и файл экрана могут быть не связаны. Потому что я делаю разные тесты, изменяя их. Могу ли я узнать, как перейти к экрану чата, вызвав функцию changeName из пользовательского интерфейса? понятия не имею как это реализовать
У меня не было возможности импортировать navcontroller в модель представления. поэтому я остановился на этом пункте. Я думал, что его нужно импортировать в viewmodel.
пожалуйста, дайте мне простой пример. Спасибо.
Вы можете создать класс NavManager, в котором будут функции навигации, и вы можете вызывать их из ViewModel:
class NavManager(private val navController: NavController) {
fun navigateToChatScreen() {
navController.navigate("Chat")
}
}
Теперь передайте NavManager в HomeViewModel во время навигации:
@Composable
fun NavigationHoster(){
val navController = rememberNavController();
val navManager = NavManager(navController)
NavHost(navController = navController, startDestination = "Home"){
composable("Home"){
val viewModel = HomeViewModel(navManager)
HomeScreen(viewModel)
}
composable("Chat"){ ChatScreen()}
}
}
Теперь домашний экран должен выглядеть так:
@Composable
fun HomeScreen(
homeViewModel: HomeViewModel
){
...
}
И HomeViewModel должен выглядеть так:
class HomeViewModel(private val navManager: NavManager) : ViewModel(){
private var _uiState = MutableStateFlow(HomeData())
var uiState : StateFlow<HomeData> = _uiState.asStateFlow()
public fun ChangeName() {
_uiState.value = HomeData(UserName = "hello there")
}
// Call this function from view whenever you want to navigate to chat screen
fun navigateToChatScreen(){
navManager.navigateToChatScreen()
}
}
По-другому можно использовать hilt, чтобы не приходилось вот так передавать navManager через навигацию. Вместо этого вы можете просто внедрить его зависимость в HomeViewModel с помощью конструктора.
Навигация необходима, когда вы работаете над приложением Single Activity Compose. Но теперь вы можете сделать это очень легко, как в примере ниже.
Во-первых, сделайте SealedClass, чтобы избежать опечатки в навигации.
sealed class Screens(val route: String) {
object Login : Screens("login")
object Register : Screens("register")
}
Тогда вот полный пример, которому вы можете следовать.
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.cb.jetpackcomposenestednavigation.ui.theme.JetpackComposeNestedNavigationTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
JetpackComposeNestedNavigationTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
NestedNavigationExample()
}
}
}
}
}
@Composable
fun NestedNavigationExample() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = Screens.Login.route
) {
composable(route = Screens.Login.route) {
LoginScreen {
navController.navigate(it)
}
}
composable(route = Screens.Register.route) {
RegisterScreen {
navController.navigate(it)
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LoginScreen(navigateTo: (route: String) -> Unit) {
Scaffold(topBar = {
TopAppBar(
title = { Text(text = "Login") },
colors = TopAppBarDefaults.topAppBarColors(containerColor = MaterialTheme.colorScheme.primary)
)
}) {
Column(
Modifier
.padding(it)
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Button(onClick = {
navigateTo(Screens.Register.route)
}) {
Text(text = "Login")
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RegisterScreen(navigateTo: (route: String) -> Unit) {
Scaffold(topBar = {
TopAppBar(
title = { Text(text = "Register") },
colors = TopAppBarDefaults.topAppBarColors(containerColor = MaterialTheme.colorScheme.primary)
)
}) {
Box(
Modifier
.padding(it)
.fillMaxSize(), contentAlignment = Alignment.Center
) {
}
}
}
Удачного сочинения!! :)
как я могу переместить navigateTo(Screens.Register.route) в ViewModel. Я хочу перейти после некоторых проверок из модели представления
О, дорогой, перемещение его в ViewModel - не лучшая практика, здесь я привел пример лучших практик навигации.
и почему вы хотите переместить его в viewModel
Я хочу сделать навигацию после вызова некоторой функции в модели представления
Я привел пример передовой практики. Кстати, выбирать вам.
Лучше всего было бы добавить в viewModel переменную с именем navigationToChatScreen, а затем, когда эта переменная имеет значение true, вы можете перемещаться по компоновке следующим образом:
class HomeViewModel : ViewModel(){
private var _uiState = MutableStateFlow(HomeData())
var uiState : StateFlow<HomeData> = _uiState.asStateFlow()
private var _navigateToChatScreen = MutableStateFlow(false)
var navigateToChatScreen : StateFlow<Boolean> = _navigateToChatScreen.asStateFlow()
public fun ChangeName() {
_uiState.value = HomeData(UserName = "hello there")
_navigateToChatScreen.value = true
}
}
Затем вы делаете это так в своем составном:
@Composable
fun HomeScreen(
homeViewModel: HomeViewModel = viewModel(),
navController: NavController
){
val HomeViewState by homeViewModel.uiState.collectAsState()
val navigateToChatScreen by homeViewModel.navigateToChatScreen.collectAsState()
Launchedeffect(key= navigateToChatScreen){
If(navigateToChatScreen){
navController.navigate("chat")
}
}
Scaffold (
topBar = {
TopAppBar(title = {
Text(text = "Chat Wallet", style = MaterialTheme.typography.titleMedium)
}, colors = TopAppBarDefaults.mediumTopAppBarColors(
containerColor = MaterialTheme.colorScheme.primary,
titleContentColor = MaterialTheme.colorScheme.background
))
}
){
paddingValues -> Box(modifier = Modifier.padding(paddingValues).fillMaxSize(), contentAlignment = Alignment.Center) {
IconButton(onClick = { homeViewModel.changeName()}, colors = IconButtonDefaults.iconButtonColors(containerColor = MaterialTheme.colorScheme.primary)) {
Icon(painter = painterResource(id = R.drawable.baseline_chat_24), contentDescription = null, tint = MaterialTheme.colorScheme.background)
}
}
}
}
вроде нормально проверю