Hi guys, I want to use share same viewmodel instan...
# koin
k
Hi guys, I want to use share same viewmodel instance in koin in jetpack compose navigation. I know there is a function in
koinViewModel()
to get instance of viewModel. I recently saw a Koin Documentation have separate
koin-androidx-compose-navigation
which gives a
koinNavViewModel()
function.
build.gradle.kts
Copy code
dependencies {

    implementation("androidx.core:core-ktx:1.12.0")

    implementation("io.insert-koin:koin-android:3.4.0")
    implementation("io.insert-koin:koin-androidx-workmanager:3.4.0")
    implementation("io.insert-koin:koin-androidx-compose:3.4.6")
    implementation("io.insert-koin:koin-androidx-compose-navigation:3.4.6")

    implementation(platform("androidx.compose:compose-bom:2023.06.01"))
    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.foundation:foundation")
    implementation("androidx.compose.foundation:foundation-layout")
    implementation("androidx.compose.material:material")
    implementation("androidx.compose.material3:material3")
    implementation("androidx.compose.runtime:runtime")
    implementation("androidx.compose.runtime:runtime-livedata")
    implementation("androidx.compose.ui:ui-tooling")
    implementation("androidx.compose.ui:ui-tooling-preview")
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose")
    implementation("androidx.activity:activity-compose:1.7.0")
    implementation("androidx.lifecycle:lifecycle-runtime-compose:$2.6.2")
    implementation("androidx.navigation:navigation-compose:$2.6.0")

    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.1.5")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
    androidTestImplementation(platform("androidx.compose:compose-bom:2023.08.00"))
    androidTestImplementation("androidx.compose.ui:ui-test-junit4")
    debugImplementation("androidx.compose.ui:ui-tooling")
    debugImplementation("androidx.compose.ui:ui-test-manifest")
}
Now I am trying to make a use of One viewmodel to different screens. When I get some data in viewmodel, I stored in
SharedFlow
and navigate to another screen with same viewmodel instance it gives me variable
null
. MainActivity.kt
Copy code
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            SimpleComposeNavigationTheme {
                SimpleNavigation()
            }
        }
    }
}
SimpleNavigation
Copy code
@Composable
fun SimpleNavigation(navController: NavHostController = rememberNavController()) {

    NavHost(
        navController = navController,
        startDestination = navController.currentBackStackEntry?.destination?.route ?: "first_screen"
    ) {
        composable("first_screen") {
            val viewModel: FirstViewModel = koinNavViewModel()
            Surface {
                Column(Modifier.fillMaxSize()) {
                    Button(onClick = { viewModel.updateName("Hello world") }) {
                        Text(text = "Add Name")
                    }
                    Button(onClick = { navController.navigate("second_screen") }) {
                        Text(text = "Next Screen")
                    }
                }
            }
        }
        composable("second_screen") {
            val viewModel: FirstViewModel = koinNavViewModel()
            val firstName by viewModel.firstName.collectAsState()
            LaunchedEffect(firstName){
                println(">> $firstName")
            }
            Surface {
                Column(Modifier.fillMaxSize()) {
                    firstName?.let { name -> Text(text = name) }
                }
            }
        }
    }
}
FirstViewModel.kt
Copy code
class FirstViewModel : ViewModel() {
   private val _firstName = MutableSharedFlow<String?>()
   val firstName: SharedFlow<String?> = _firstName.asSharedFlow()

   fun updateName(name: String) {
      viewModelScope.launch {
         _firstName.emit(name)
      }
   }
}
What is the benefits of using
koinNavViewModel()
if we cannot share the same instance in different screens of compose through navigation?
Copy code
class SampleApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        initializeDependencyInjection()
    }

    private fun initializeDependencyInjection() {
        startKoin {
            androidLogger(Level.ERROR)
            modules(
                listOf(simpleModule)
            )
        }
    }
}


val simpleModule = module {
    viewModelOf(::FirstViewModel)
}
s
koinNavViewModel
is just "koinViewModel" but automatically passes the arguments passed to the destination as shown here https://github.com/InsertKoinIO/koin/blob/main/projects%2Fcompose%2Fkoin-androidx-compose-navigation%2Fsrc%2Fmain%2Fjava%2Forg%2Fkoin%2Fandroidx%2Fcompose%2Fnavigation%2FNavViewModelInternals.kt#L39 We just use koinViewModel() itself which automatically uses the current nav route lifecycle, and if we want to scope it higher we do it like here https://github.com/HedvigInsurance/android/blob/develop/app%2Fnavigation%2Fnavigation-compose-typed%2Fsrc%2Fmain%2Fkotlin%2Fcom%2Fhedvig%2Fandroid%2Fnavigation%2Fcompose%2Ftyped%2FDestinationScopedViewModel.kt#L16-L25 by using that higher destination as the viewModelStoreOwner
Key point here being that if you want to scope a VM to a higher up route, you still gotta do that yourself.
k
Thanks for guidance. What is createRoutePattern in your code?
s
That comes from this excellent library over here https://github.com/kiwicom/navigation-compose-typed
253 Views