ViewModel: androidx.navigation.NavController中的 NavController



我试图注入navController到我的ViewModel -

ViewModel——

@HiltViewModel
class DeviceHolderListViewModelImpl @Inject constructor(
private val fetchUsersUseCase: FetchUsersUseCase,
private val navigationUtil: NavigationUtil
) : DeviceHolderListViewModel, ViewModel() {

// Trying to access navigationUtil here
}

NavigationUtil——

class NavigationUtil @Inject constructor(private val navController: NavController) {
fun navigateTo(destination: String, bundle: Bundle) {
when(destination) {
DEVICE_HOLDER_LIST -> navController.navigate(R.id.action_global_goto_deviceHolderListFragment, bundle)
DEVICE_HOLDER_DETAILS -> navController.navigate(R.id.action_global_goto_deviceHolderDetailsFragment, bundle)
}
}
fun navigateBack() {
navController.popBackStack()
}
}

NavigationModule——

@Module
@InstallIn(ActivityComponent::class)
object NavigationModule {
@Provides
fun provideNavController(activity: AppCompatActivity): NavController {
return Navigation.findNavController(activity, R.id.nav_host_fragment)
}
@Provides
fun provideNavigationUtil(navController: NavController): NavigationUtil {
return NavigationUtil(navController)
}
}

在尝试构建代码时,我得到以下错误-

error: [Dagger/MissingBinding] androidx.navigation.NavController不能在没有@Inject构造函数或@Provides-annotated方法。

是否因为我试图从ViewModel访问navController,而它应该从片段或活动访问?

我的目标是从ViewModel开始导航。理想情况下我该怎么做呢?


编辑:根据@DAA的回答更改-

NavigationUtil

class NavigationUtil {
private var navController: NavController? = null
fun setController(controller: NavController) {
navController = controller
}
fun clear() {
navController = null
}
fun navigateTo(destination: String, bundle: Bundle) {
when(destination) {
DEVICE_HOLDER_LIST -> navController?.navigate(R.id.action_global_goto_deviceHolderListFragment, bundle)
DEVICE_HOLDER_DETAILS -> navController?.navigate(R.id.action_global_goto_deviceHolderDetailsFragment, bundle)
}
}
fun navigateBack() {
navController?.popBackStack()
}
}

NavigationModule

@Module
@InstallIn(ActivityComponent::class)
object NavigationModule {
@Provides
@ViewModelScoped
fun provideNavigationUtil(): NavigationUtil {
return NavigationUtil()
}
}

NavigationUtil

class NavigationUtil {
private var navController: NavController? = null
fun setController(controller: NavController) {
navController = controller
}
fun clear() {
navController = null
}
fun navigateTo(destination: String, bundle: Bundle) {
when(destination) {
DEVICE_HOLDER_LIST -> navController?.navigate(R.id.action_global_goto_deviceHolderListFragment, bundle)
DEVICE_HOLDER_DETAILS -> navController?.navigate(R.id.action_global_goto_deviceHolderDetailsFragment, bundle)
}
}
fun navigateBack() {
navController?.popBackStack()
}
}

你不能以这种方式直接注入NavController与Hilt,因为你只能从你的活动/片段调用Navigation.findNavController,但是你正在采取的NavigationUtil方法是一个很好的方法,以便从ViewModel访问导航。

你只需要做一些改变:

  • 代替将NavController作为NavigationUtil的参数传递,创建两个方法setNavControllerclearNavController
  • 使NavigationUtil作用域为ViewModel,用@ViewModelScoped注释提供方法。
  • NavigationUtil注入到Activity中,并在onCreate方法中调用setNavController,在onDestroy方法中调用clearNavController

现在你的NavigationUtil可以访问NavController,当活动被重新创建时它被更新(当它被销毁时清除),你可以从你的ViewModel访问它。

你可以检查我给了一个非常类似的问题的答案在这里:如何注入' memorbernavcontroller '从jetpack撰写到一个活动使用柄?

我会建议一个不同的方法。

我通常做一个Navigatior类,这是在Activity和你的ViewModel注入。ViewModel调用导航器方法,活动订阅它们。

免责声明:以下代码是根据我的记忆编写的,可能在语法上不正确。请随意改进。

sealed interface NavEvent {
data class Navigate(val directions: NavDirections): NavEvent
object Pop(): NavEvent
data class PopForResult(val requestKey: String, val result: Bundle): NavEvent
}
@Singleton
class Navigator @Inject constructor() {
private val _navigateFlow = MutableSharedFlow<NavEvent>
val navigateFlow: SharedFlow = _navigateFlow
suspend fun navigate(nav: NavDirections) {
_navigateFlow.emit(Navigate(directions))
}

suspend fun pop() {
_navigateFlow.emit(Pop)
}
}
@HiltViewModel
class MyVm @Inject constructor(
private val navigator: Navigator
) : ViewModel() {
fun onClickSmth() {
viewModelScope.launch {
navigator.navigate(MyFragmentDirections.actionToSomewhere())
}
}
}
@AndroidEntryPoint
class MainActivity : Activity {

@Inject lateinit var navigator: Navigator
private lateinit var navController: NavController = TODO()
fun onCreate() {
lifecycleScope.launch {
navigator.navigateFlow.collect(this::onNavEvent)
}
}

private fun onNavEvent(event: NavEvent) {
when (navEvent) {
Navigate -> navController.navigate(navEvent.directions)
Pop -> navController.popBackStack()
PopForResult -> {
navController.previousBackStackEntry
?.savedStateHandle
?.set(event.requestKey, event.result)
navController.popBackStack()
}
}
}
}

最新更新