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

如何基于Compose Destinations Library实现Compose项目的优雅导航?

优化Compose Destinations下ViewModel与Composable的导航通信

我在开发纯Compose项目时,总觉得ViewModel和Composable之间的导航逻辑耦合性太高,不够优雅。理想状态是让ViewModel直接决定跳转目标(比如调用navManager.navigate(LoginDestination)),而我用的是Compose Destinations库。找了不少示例仓库,但都没找到适配这个库的优雅实现,自己写了一套方案,不过还想再简化。

我当前的实现代码如下:

@Composable
fun DestinationWithNav(
    navState: StateFlow<StateEventWithContent<NavigationState>>,
    navigator: DestinationsNavigator,
    consume: () -> Unit,
    screen: @Composable () -> Unit
) {
    val state by navState.collectAsStateWithLifecycle()

    EventEffect(event = state, onConsumed = consume) {
        navigator.navigate(
            direction = it.direction,
            builder = it.builder
        )
    }

    screen()
}

下面是更简洁的优化方案,核心是把导航逻辑抽象到ViewModel基类,用事件流处理一次性导航动作:

1. 定义导航事件密封类

用密封类统一管理所有导航动作类型:

sealed class NavEvent {
    data class NavigateTo(val direction: Direction) : NavEvent()
    data class NavigateToWithArgs(val direction: Direction, val args: NavArgs) : NavEvent()
    object NavigateUp : NavEvent()
}

2. 封装导航ViewModel基类

把导航逻辑封装到基类,让业务ViewModel直接继承即可复用:

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch

abstract class BaseNavViewModel : ViewModel() {
    private val _navEvents = MutableSharedFlow<NavEvent>()
    val navEvents = _navEvents.asSharedFlow()

    protected fun navigateTo(direction: Direction) {
        viewModelScope.launch {
            _navEvents.emit(NavEvent.NavigateTo(direction))
        }
    }

    protected fun navigateTo(direction: Direction, args: NavArgs) {
        viewModelScope.launch {
            _navEvents.emit(NavEvent.NavigateToWithArgs(direction, args))
        }
    }

    protected fun navigateUp() {
        viewModelScope.launch {
            _navEvents.emit(NavEvent.NavigateUp)
        }
    }
}

3. 编写导航事件处理扩展函数

用Compose的LaunchedEffect处理事件流,替代自定义的EventEffect:

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import com.ramcosta.composedestinations.navigation.DestinationsNavigator

@Composable
fun handleNavEvents(navEvents: SharedFlow<NavEvent>, navigator: DestinationsNavigator) {
    LaunchedEffect(navigator) {
        navEvents.collect { event ->
            when (event) {
                is NavEvent.NavigateTo -> navigator.navigate(event.direction)
                is NavEvent.NavigateToWithArgs -> navigator.navigate(event.direction, event.args)
                NavEvent.NavigateUp -> navigator.navigateUp()
            }
        }
    }
}

4. 在业务屏幕中使用

ViewModel中直接调用导航方法,屏幕Composable只需一行代码处理事件:

@Destination
@Composable
fun HomeScreen(
    viewModel: HomeViewModel = hiltViewModel(),
    navigator: DestinationsNavigator
) {
    // 处理导航事件
    handleNavEvents(viewModel.navEvents, navigator)

    // 屏幕UI内容
    Button(onClick = { viewModel.navigateToLogin() }) {
        Text("跳转至登录页")
    }
}

// 业务ViewModel
class HomeViewModel : BaseNavViewModel() {
    fun navigateToLogin() {
        // 直接调用基类方法,无需关心Composable层的导航实现
        navigateTo(LoginDestination)
    }
}

优化点说明

  • 用SharedFlow替代StateFlow:导航事件是一次性动作,不需要保留状态,SharedFlow更适合这类事件流场景
  • 抽象基类减少重复:所有需要导航的ViewModel只需继承BaseNavViewModel,不用重复写事件发送逻辑
  • 简化Composable层:屏幕无需嵌套DestinationWithNav,只需调用扩展函数即可处理导航,代码更清爽
  • 符合Compose生命周期:用LaunchedEffect收集事件,自动跟随Composable生命周期管理协程

内容的提问来源于stack exchange,提问作者Teo Vladušić

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 13:02:52