最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

android - Sharing single instance of ViewModel between two composables and recreating it when needed - Stack Overflow

programmeradmin2浏览0评论

In my application, there are two nested navigations:

  1. SignedOut
  2. SignedIn

Every time a user navigates from SignedOut to SignedIn, a new instance of SharedViewModel must be provided.

The same instance of SharedViewModel must be shared between composables inside SignedIn.

The whole point of making SharedViewModel is that the app will be able to cache a massive amount of data between multiple composables avoiding fetching the same data several times.

What I tried:

ViewModel:

@HiltViewModel
class SharedViewModel @Inject constructor(): ViewModel() {

    private val instance =
        this.toString().substringAfterLast("@")

    val data = instance
}

Module responsible for providing SharedViewModel:

@Module
@InstallIn(ViewModelComponent::class)
class SharedViewModelModule {

    @Provides
    @ViewModelScoped
    fun provideSharedViewModel(): SharedViewModel =
        SharedViewModel()
}

MainActivity.kt

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {

            val navigationController = rememberNavController()

            NavHost(
                navController = navigationController,
                startDestination = "WelcomeScreen"
            ) {

                navigation(route = "SignedOut", startDestination = "WelcomeScreen") {...}

                navigation(route = "SignedIn", startDestination = "HomeScreen") {

                    composable(
                        route = "HomeScreen",
                        content = {

                            val viewModel = hiltViewModel<SharedViewModel>(it)
                        }
                    )

                    composable(
                        route = "SecondScreen",
                        content = {

                            val viewModel =hiltViewModel<SharedViewModel>(it)
                        }
                    )
                }
            }
        }
    }
}

What I expected:

I navigate from SignedOut to SignedIn: The same instance of SharedViewModel should be provided to both HomeScreen and SecondScreen composables.

I navigate from SignedIn to SignedOut and back to SignedIn: A completely new instance should be provided for HomeScreen and SecondScreen.

Actual result:

Dagger-hilt provides two different instances for HomeScreen and SecondScreen.

In my application, there are two nested navigations:

  1. SignedOut
  2. SignedIn

Every time a user navigates from SignedOut to SignedIn, a new instance of SharedViewModel must be provided.

The same instance of SharedViewModel must be shared between composables inside SignedIn.

The whole point of making SharedViewModel is that the app will be able to cache a massive amount of data between multiple composables avoiding fetching the same data several times.

What I tried:

ViewModel:

@HiltViewModel
class SharedViewModel @Inject constructor(): ViewModel() {

    private val instance =
        this.toString().substringAfterLast("@")

    val data = instance
}

Module responsible for providing SharedViewModel:

@Module
@InstallIn(ViewModelComponent::class)
class SharedViewModelModule {

    @Provides
    @ViewModelScoped
    fun provideSharedViewModel(): SharedViewModel =
        SharedViewModel()
}

MainActivity.kt

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {

            val navigationController = rememberNavController()

            NavHost(
                navController = navigationController,
                startDestination = "WelcomeScreen"
            ) {

                navigation(route = "SignedOut", startDestination = "WelcomeScreen") {...}

                navigation(route = "SignedIn", startDestination = "HomeScreen") {

                    composable(
                        route = "HomeScreen",
                        content = {

                            val viewModel = hiltViewModel<SharedViewModel>(it)
                        }
                    )

                    composable(
                        route = "SecondScreen",
                        content = {

                            val viewModel =hiltViewModel<SharedViewModel>(it)
                        }
                    )
                }
            }
        }
    }
}

What I expected:

I navigate from SignedOut to SignedIn: The same instance of SharedViewModel should be provided to both HomeScreen and SecondScreen composables.

I navigate from SignedIn to SignedOut and back to SignedIn: A completely new instance should be provided for HomeScreen and SecondScreen.

Actual result:

Dagger-hilt provides two different instances for HomeScreen and SecondScreen.

Share Improve this question edited Nov 20, 2024 at 22:02 Marcel asked Nov 20, 2024 at 21:56 MarcelMarcel 276 bronze badges 1
  • 1 "the app will be able to cache a massive amount of data between multiple composables avoiding fetching the same data several times" -- often, that is a better task for your repository, rather than a viewmodel. – CommonsWare Commented Nov 20, 2024 at 22:28
Add a comment  | 

2 Answers 2

Reset to default 1

Please have a look at the Android ViewModel Cheatsheet:

As you can see denoted in the orange box, a ViewModel in Hilt by default is scoped to the closest ViewModelStoreOwner, which in your case is a BackStackEntry in your NavGraph. However, you can obtain a ViewModel by its BackStackEntry and use it in other destinations.

Please try the following code:

composable(
    route = "HomeScreen",
    content = {
        val viewModel = hiltViewModel<SharedViewModel>(it)
    }
)

composable(
    route = "SecondScreen",
    content = {
        val homeEntry = remember(backStackEntry) {
            navController.getBackStackEntry("HomeScreen")
        }
        val sharedViewModel = hiltViewModel<ParentViewModel>(homeEntry)
        
    }
)

This case use activityViewModels. It Returns a property delegate to access parent activity's ViewModel, if factoryProducer is specified then ViewModelProvider.Factory returned by it will be used to create ViewModel first time.

First in your build.gradle add

val fragment_version = "1.8.5"
  // Kotlin
implementation("androidx.fragment:fragment-ktx:$fragment_version")

In your screen level or fragments

// Access shared ViewModel within composables
val sharedViewModel: MySharedViewModel by activityViewModels()

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论