Tentei seguir os exemplos na documentação do Android, mas não consigo implementar a navegação usando Navigation Compose, ViewModel e Hilt. Nenhum erro é lançado e no NavigationScreen LaunchedEffect não é acionado em emissões do ViewModel.
Compostos:
@HiltAndroidApp
class MyApplication : Application() {}
@Serializable
object NavScreen
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
val navController = rememberNavController()
PhotosSyncTheme {
NavHost(
navController = navController,
startDestination = NavScreen
) {
composable<NavScreen> { NavigationScreen(navController) }
composable<NavigationEvent.NavigateToHome> { HomeScreen() }
composable<NavigationEvent.NavigateToSettings> { Settings() }
}
}
}
}
}
@Composable
fun NavigationScreen(navController: NavController) {
val navViewModel = hiltViewModel<NavigationViewModel>()
val navigationEvent by navViewModel.navigationEvents.collectAsState(initial = NavigationEvent.NavigateToHome)
LaunchedEffect(navigationEvent) {
Log.d("Navigation Screen", navigationEvent.toString())
navigationEvent.let { event ->
Log.d("Navigation Screen", event.toString())
when (event) {
is NavigationEvent.NavigateBack -> navController.popBackStack()
NavigationEvent.NavigateToHome -> navController.navigate(NavigationEvent.NavigateToHome)
NavigationEvent.NavigateToSettings -> navController.navigate(NavigationEvent.NavigateToSettings)
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeScreen() {
val navViewModel = hiltViewModel<NavigationViewModel>()
var selectedItem by remember { mutableIntStateOf(0) }
Scaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
TopAppBar(title = { Text("Photos Sync") }, actions = {
IconButton(onClick = {
navViewModel.navigateToSettings()
}) {
Icon(
imageVector = Icons.Filled.Settings,
contentDescription = "Settings icon"
)
}
})
}
)
{ }
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Settings() {
val viewModel = hiltViewModel<NavigationViewModel>()
Scaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
TopAppBarWithBack(title = "Definições")
}
)
{ }
}
Modelo de exibição:
@HiltViewModel
class NavigationViewModel @Inject constructor() : ViewModel() {
private val _navigationEvents = MutableSharedFlow<NavigationEvent>()
val navigationEvents: SharedFlow<NavigationEvent> = _navigationEvents.asSharedFlow()
fun navigateBack() {
viewModelScope.launch { _navigationEvents.emit(NavigationEvent.NavigateBack) }
}
fun navigateToHome() {
viewModelScope.launch { _navigationEvents.emit(NavigationEvent.NavigateToHome) }
}
fun navigateToSettings() {
viewModelScope.launch {
Log.d("Navigation ViewModel", "Emit Navigate To Settings")
try {
_navigationEvents.emit(NavigationEvent.NavigateToSettings)
} catch (e: Exception) {
Log.e("Navigation ViewModel", "Error emitting navigation event")
} finally {
Log.d("Navigation ViewModel", "done Emit Navigate To Settings")
}
}
}
fun navigateTo(event: NavigationEvent) {
viewModelScope.launch { _navigationEvents.emit(event) }
}
sealed class NavigationEvent {
@Serializable
object NavigateBack : NavigationEvent()
@Serializable
object NavigateToHome : NavigationEvent()
@Serializable
object NavigateToSettings : NavigationEvent()
}
}
Eu esperava que quando eu clicasse no botão de configurações, o composable de configurações fosse empurrado para o topo da pilha de composables usandonavController.navigate