【Navigation3】结合ViewModel(二)

背景

在使用ViewModel时,一般会考虑使用hilt来依赖注入ViewModel对象。在使用compose时,安卓官方推荐使用androidx.hilt.lifecycle.viewmodel.compose库中的hiltViewModel()方法,来注入ViewModel对象

在Nav3中使用Hilt注入ViewModel对象

  1. 配置环境
  2. 完善lifecycle-viewmodel-navigation3库配置
  3. 使用hiltViewModel()完成对象注入

环境配置

  1. 导入Nav3相关库
  2. 导入androidx.hilt.lifecycle.viewmodel.compose
  3. 导入Hilt相关库
  4. 导入lifecycle-viewmodel-navigation3

使用案例

kotlin 复制代码
/*
 * Copyright 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.nav3recipes.passingarguments.viewmodels.hilt

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.compose.dropUnlessResumed
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
import androidx.navigation3.ui.NavDisplay
import com.example.nav3recipes.content.ContentBlue
import com.example.nav3recipes.content.ContentGreen
import com.example.nav3recipes.passingarguments.viewmodels.basic.RouteB
import com.example.nav3recipes.ui.setEdgeToEdgeConfig
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.lifecycle.HiltViewModel

data object RouteA
data class RouteB(val id: String)

@AndroidEntryPoint
class HiltViewModelsActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        setEdgeToEdgeConfig()
        super.onCreate(savedInstanceState)
        setContent {
            val backStack = remember { mutableStateListOf<Any>(RouteA) }

            NavDisplay(
                backStack = backStack,
                onBack = { backStack.removeLastOrNull() },

                // In order to add the `ViewModelStoreNavEntryDecorator` (see comment below for why)
                // we also need to add the default `NavEntryDecorator`s as well. These provide
                // extra information to the entry's content to enable it to display correctly
                // and save its state.
                entryDecorators = listOf(
                    rememberSaveableStateHolderNavEntryDecorator(),
                    rememberViewModelStoreNavEntryDecorator()
                ),
                entryProvider = entryProvider {
                    entry<RouteA> {
                        ContentGreen("Welcome to Nav3") {
                            LazyColumn {
                                items(10) { i ->
                                    Button(onClick = dropUnlessResumed {
                                        backStack.add(RouteB("$i"))
                                    }) {
                                        Text("$i")
                                    }
                                }
                            }
                        }
                    }
                    entry<RouteB> { key ->
                        val viewModel = hiltViewModel<RouteBViewModel, RouteBViewModel.Factory>(
                            // Note: We need a new ViewModel for every new RouteB instance. Usually
                            // we would need to supply a `key` String that is unique to the
                            // instance, however, the ViewModelStoreNavEntryDecorator (supplied
                            // above) does this for us, using `NavEntry.contentKey` to uniquely
                            // identify the viewModel.
                            //
                            // tl;dr: Make sure you use rememberViewModelStoreNavEntryDecorator()
                            // if you want a new ViewModel for each new navigation key instance.
                            creationCallback = { factory ->
                                factory.create(key)
                            }
                        )
                        ScreenB(viewModel = viewModel)
                    }
                }
            )
        }
    }
}

@Composable
fun ScreenB(viewModel: RouteBViewModel) {
    ContentBlue("Route id: ${viewModel.navKey.id} ")
}

@HiltViewModel(assistedFactory = RouteBViewModel.Factory::class)
class RouteBViewModel @AssistedInject constructor(
    @Assisted val navKey: RouteB
) : ViewModel() {

    @AssistedFactory
    interface Factory {
        fun create(navKey: RouteB): RouteBViewModel
    }
}

相关参考文献

Nav3官方文档

Nav3源码示例

相关推荐
阿懂在掘金11 小时前
早点下班(Vue2.7版):旧项目也能少写 40%+ 异步代码
前端·vue.js·开源
Mintopia11 小时前
Web性能测试流程全解析:从概念到落地的完整指南
前端·性能优化·测试
Qinana11 小时前
第一次用向量数据库!手搓《天龙八部》RAG助手,让AI真正“懂”你
前端·数据库·后端
忆江南11 小时前
# Flutter Engine、Dart VM、Runner、iOS 进程与线程 —— 深度解析
前端
龙国浪子11 小时前
从「选中一段」到「整章润色」:编辑器里的 AI 润色是怎么做出来的
前端·人工智能
小码哥_常11 小时前
Android 开发秘籍:用Tint为Icon动态变色
前端
小码哥_常11 小时前
从0到1手把手封装Android基类Activity/Fragment,告别重复代码,开发效率直接拉满!
前端
ChoriaKiinweill11 小时前
不会有人现在还不了解BOM的知识吧? 关于它的一切都在这里!!!
前端
ChoriaKiinweill11 小时前
我们最爱操纵的DOM是个什么玩意? 关于DOM的知识快速一览!
前端
毛骗导演11 小时前
万字解析 OpenClaw 源码架构-代理系统(二)
前端·架构