You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将DestinationsNavigator注入ViewModel依赖的Navigator类

实现导航逻辑从Composable到ViewModel的迁移(基于Compose Destinations + Hilt)

下面提供两种可行方案,分别适用于复杂解耦场景和简单快速实现场景:


方案一:基于AssistedInject的解耦实现(推荐复杂项目使用)

这种方案让ViewModel完全依赖自定义导航接口,不直接绑定Compose导航实现,解耦性更强。

1. 添加依赖

确保模块级build.gradle中包含以下依赖:

plugins {
    id 'kotlin-kapt'
    id 'dagger.hilt.android.plugin'
}

dependencies {
    // Hilt核心
    implementation "com.google.dagger:hilt-android:2.44"
    kapt "com.google.dagger:hilt-android-compiler:2.44"
    // Hilt导航Compose支持
    implementation "androidx.hilt:hilt-navigation-compose:1.0.0"
    // AssistedInject
    implementation "com.google.dagger:hilt-assisted-inject:2.44"
    kapt "com.google.dagger:hilt-compiler:2.44"
}

2. 定义导航接口

封装所有导航操作,让ViewModel依赖抽象而非具体实现:

interface Navigator {
    fun navigateToProfile(userParam: String)
    fun navigateBack()
    // 根据需求添加其他导航方法
}

3. 实现导航接口

创建具体实现类,通过AssistedInject接收Compose Destinations的导航实例:

import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject

class NavigatorImpl @AssistedInject constructor(
    @Assisted private val navigator: DestinationsNavigator
) : Navigator {
    override fun navigateToProfile(userParam: String) {
        // 替换为你的实际Destination参数逻辑
        navigator.navigate(ProfileDestination(userParam))
    }

    override fun navigateBack() {
        navigator.navigateUp()
    }

    // 提供工厂类用于创建实例
    @AssistedFactory
    interface Factory {
        fun create(navigator: DestinationsNavigator): NavigatorImpl
    }
}

4. 修改ViewModel使用AssistedInject

让ViewModel通过AssistedInject注入自定义Navigator:

import androidx.lifecycle.ViewModel
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import dagger.assisted.AssistedFactory

class WelcomeViewModel @AssistedInject constructor(
    private val navigator: Navigator
) : ViewModel() {

    fun onProfileButtonClick(userParam: String) {
        // 直接调用导航方法,无需处理事件流
        navigator.navigateToProfile(userParam)
    }

    fun onBackClick() {
        navigator.navigateBack()
    }

    // ViewModel工厂类
    @AssistedFactory
    interface Factory {
        fun create(navigator: Navigator): WelcomeViewModel
    }

    companion object {
        // 提供ViewModelProvider.Factory,供Composable调用
        fun provideFactory(
            factory: Factory,
            navigator: Navigator
        ): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
            @Suppress("UNCHECKED_CAST")
            override fun <T : ViewModel> create(modelClass: Class<T>): T {
                return factory.create(navigator) as T
            }
        }
    }
}

5. 简化Composable代码

Composable仅负责创建实例和传递UI事件,不再处理导航逻辑:

import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootNavGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.viewmodel.compose.viewModel

@RootNavGraph(start = true)
@Destination
@Composable
fun Welcome(
    navController: DestinationsNavigator,
    navigatorFactory: NavigatorImpl.Factory = hiltViewModel(),
    welcomeViewModelFactory: WelcomeViewModel.Factory = hiltViewModel()
) {
    val navigator = navigatorFactory.create(navController)
    val viewModel = viewModel<WelcomeViewModel>(
        factory = WelcomeViewModel.provideFactory(welcomeViewModelFactory, navigator)
    )

    // 仅传递UI交互事件给ViewModel
    WelcomeView(
        onProfileClick = { userParam -> viewModel.onProfileButtonClick(userParam) },
        onBackClick = { viewModel.onBackClick() }
    )
}

6. 配置Hilt基础

确保Application类添加注解:

import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class MyApp : Application()

Activity添加注解:

import dagger.hilt.android.AndroidEntryPoint
import androidx.activity.compose.setContent

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyAppTheme {
                DestinationsNavHost(navGraph = NavGraphs.root)
            }
        }
    }
}

方案二:基于SharedFlow的简单实现(适合小型项目)

这种方式无需额外依赖,通过单例类传递导航命令,实现快速迁移。

1. 定义导航命令密封类

sealed class NavigationCommand {
    data class NavigateToProfile(val param: String) : NavigationCommand()
    object NavigateBack : NavigationCommand()
}

2. 创建单例导航类

import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow

object Navigator {
    private val _commands = MutableSharedFlow<NavigationCommand>()
    val commands = _commands.asSharedFlow()

    fun navigateToProfile(param: String) {
        _commands.tryEmit(NavigationCommand.NavigateToProfile(param))
    }

    fun navigateBack() {
        _commands.tryEmit(NavigationCommand.NavigateBack)
    }
}

3. 修改ViewModel

import androidx.lifecycle.ViewModel

class WelcomeViewModel : ViewModel() {
    fun onProfileButtonClick(param: String) {
        Navigator.navigateToProfile(param)
    }

    fun onBackClick() {
        Navigator.navigateBack()
    }
}

4. 简化Composable

仅保留一次Flow收集逻辑,不再处理ViewModel的事件流:

@RootNavGraph(start = true)
@Destination
@Composable
fun Welcome(
    navController: DestinationsNavigator,
    viewModel: WelcomeViewModel = hiltViewModel()
) {
    LaunchedEffect(key1 = true) {
        Navigator.commands.collect { command ->
            when (command) {
                is NavigationCommand.NavigateToProfile -> {
                    navController.navigate(ProfileDestination(command.param))
                }
                NavigationCommand.NavigateBack -> {
                    navController.navigateUp()
                }
            }
        }
    }

    WelcomeView(
        onProfileClick = { param -> viewModel.onProfileButtonClick(param) },
        onBackClick = { viewModel.onBackClick() }
    )
}

内容的提问来源于stack exchange,提问作者Oleh Kuchura

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 00:11:04