AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79316638
Accepted
alexandrade00
alexandrade00
Asked: 2024-12-30 09:41:15 +0800 CST2024-12-30 09:41:15 +0800 CST 2024-12-30 09:41:15 +0800 CST

Navegação Hilt Compose e ViewModel

  • 772

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

  • 1 1 respostas
  • 44 Views

1 respostas

  • Voted
  1. Best Answer
    tyg
    2024-12-30T20:10:16+08:002024-12-30T20:10:16+08:00

    Os modelos de visualização são delimitados para o destino de navegação atual (seja usando o Hilt ou não). Você usa o mesmo modelo de visualização para telas diferentes, então cada uma terá sua própria instância de modelo de visualização. Modificar o estado de um não afetará o estado dos outros.

    Você pode definir o escopo do view model para outra coisa para que seus destinos compartilhem a mesma instância do view model (veja Posso compartilhar um ViewModel usando hiltViewModel() em uma rota de navegação do Compose diferente? ), mas não vejo por que você quer envolver um view model em primeiro lugar, já que a navegação não está relacionada ao estado da IU. Tudo o que seu view model faz atualmente é receber o evento de navegação e passá-lo de volta para o composable. Você nem mesmo persiste a rota atual ou seus parâmetros ( o que poderia ser feito usando umSavedStateHandle ), mas isso também é útil principalmente para view models dedicados a uma tela específica, não um view model de navegação .

    Eu recomendaria remover o NavigationViewModelinteiramente. Você pode manter sua NavigationEventhierarquia sealed, embora eu ache o nome bastante enganoso e você nem precise da herança. Melhor substituí-lo por algo assim (declarado no nível superior, sem uma interface envolvente):

    @Serializable data object HomeRoute
    @Serializable data object SettingsRoute
    

    Como você não os usa mais como eventos, eles agora são usados ​​apenas para rotas no seu gráfico de navegação, daí o nome. Como NavigateBacknão é uma rota, ele pode ser removido (basta chamar navController.popBackStack()quando necessário).

    Seu NavHost ficaria assim:

    NavHost(
        navController = navController,
        startDestination = HomeRoute,
    ) {
        composable<HomeRoute> {
            HomeScreen(
                navigateToSettings = { navController.navigate(SettingsRoute) },
            )
        }
    
        composable<SettingsRoute> { Settings() }
    }
    

    Note que em vez de HomeScreendepender do modelo de visualização para acessar a navegação, o parâmetro recém-criado navigateToSettingsrecebe a lógica de navegação necessária. Você não deve passar o navController diretamente. HomeScreenthen usa o novo parâmetro assim:

    @Composable
    fun HomeScreen(
        navigateToSettings: () -> Unit,
    ) {
        // ...
        IconButton(onClick = navigateToSettings) {
            //...
        }
    }
    

    Isso também torna o HomeScreen mais testável e reutilizável, já que não depende mais do modelo de visualização.

    Toda a lógica de navegação agora está contida no NavHost. NavigationScreenCom o LaunchedEffect, é possível removê-lo.

    O Google fornece o projeto de amostra totalmente funcional Now in Android que - entre outras coisas - também mostra como usar uma navegação mais complexa. Se você quiser bisbilhotar, pode simplesmente importá-lo no Android Studio (" Obter do Controle de Versão... ") usando este link: https://github.com/android/nowinandroid

    • 1

relate perguntas

  • Adicionar número de série para atividade de cópia ao blob

  • A fonte dinâmica do empacotador duplica artefatos

  • Selecione linhas por grupo com 1s consecutivos

  • Lista de chamada de API de gráfico subscritoSkus estados Privilégios insuficientes enquanto os privilégios são concedidos

  • Função para criar DFs separados com base no valor da coluna

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve