ViewModel-এ আর্গুমেন্ট পাস করা (Hilt)
এই রেসিপি থেকে জানা যায় যে ডিপেন্ডেন্সি ইনজেকশনের জন্য Hilt ব্যবহার করে কীভাবে ViewModel-এ নেভিগেশন আর্গুমেন্ট (কী) পাস করতে হয়।
এটি কীভাবে কাজ করে
এই উদাহরণে Dagger/Hilt-এর অ্যাসিস্টেড ইনজেকশন ফিচার ব্যবহার করা হয়েছে:
ViewModel-কে@HiltViewModel-এর সাথে অ্যানোটেট করা হয়েছে এবং এর কনস্ট্রাক্টর নেভিগেশন কী (যা@Assisted-এর সাথে অ্যানোটেট করা হয়েছে) পাওয়ার জন্য@AssistedInjectব্যবহার করে।ViewModelতৈরি করার জন্য একটি@AssistedFactoryইন্টারফেস নির্ধারণ করা হয়েছে।ViewModelইনস্ট্যান্স পেতেhiltViewModelকম্পোজ করার উপযুক্ত ফাংশন ব্যবহার করা হয়। ফ্যাক্টরিতে নেভিগেশন কী পাস করার জন্য একটিcreationCallbackপ্রদান করা হয়, যাViewModel-এর জন্য উপলভ্য করা হয়।
মনে রাখবেন: rememberViewModelStoreNavEntryDecorator-কে NavDisplay-এর entryDecorators-এ যোগ করা হয়। এটি নিশ্চিত করে যে ViewModel সঠিকভাবে তাদের সংশ্লিষ্ট NavEntry-এ স্কোপ করা হয়েছে, যাতে প্রতিটি অনন্য নেভিগেশন কী-এর জন্য একটি নতুন ViewModel ইনস্ট্যান্স তৈরি হয়।
/* * Copyright 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.nav3recipes.passingarguments.viewmodels.hilt import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.ViewModel import androidx.lifecycle.compose.dropUnlessResumed import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator import androidx.navigation3.runtime.entryProvider import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator import androidx.navigation3.ui.NavDisplay import com.example.nav3recipes.content.ContentBlue import com.example.nav3recipes.content.ContentGreen import com.example.nav3recipes.passingarguments.viewmodels.basic.RouteB import com.example.nav3recipes.ui.setEdgeToEdgeConfig import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.lifecycle.HiltViewModel data object RouteA data class RouteB(val id: String) @AndroidEntryPoint class HiltViewModelsActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { setEdgeToEdgeConfig() super.onCreate(savedInstanceState) setContent { val backStack = remember { mutableStateListOf<Any>(RouteA) } NavDisplay( backStack = backStack, onBack = { backStack.removeLastOrNull() }, // In order to add the `ViewModelStoreNavEntryDecorator` (see comment below for why) // we also need to add the default `NavEntryDecorator`s as well. These provide // extra information to the entry's content to enable it to display correctly // and save its state. entryDecorators = listOf( rememberSaveableStateHolderNavEntryDecorator(), rememberViewModelStoreNavEntryDecorator() ), entryProvider = entryProvider { entry<RouteA> { ContentGreen("Welcome to Nav3") { LazyColumn { items(10) { i -> Button(onClick = dropUnlessResumed { backStack.add(RouteB("$i")) }) { Text("$i") } } } } } entry<RouteB> { key -> val viewModel = hiltViewModel<RouteBViewModel, RouteBViewModel.Factory>( // Note: We need a new ViewModel for every new RouteB instance. Usually // we would need to supply a `key` String that is unique to the // instance, however, the ViewModelStoreNavEntryDecorator (supplied // above) does this for us, using `NavEntry.contentKey` to uniquely // identify the viewModel. // // tl;dr: Make sure you use rememberViewModelStoreNavEntryDecorator() // if you want a new ViewModel for each new navigation key instance. creationCallback = { factory -> factory.create(key) } ) ScreenB(viewModel = viewModel) } } ) } } } @Composable fun ScreenB(viewModel: RouteBViewModel) { ContentBlue("Route id: ${viewModel.navKey.id} ") } @HiltViewModel(assistedFactory = RouteBViewModel.Factory::class) class RouteBViewModel @AssistedInject constructor( @Assisted val navKey: RouteB ) : ViewModel() { @AssistedFactory interface Factory { fun create(navKey: RouteB): RouteBViewModel } }
ViewModel-এ আর্গুমেন্ট পাস করা (প্রাথমিক)
এই রেসিপি থেকে বোঝা যায় যে কীভাবে কাস্টম ViewModelProvider.Factory ব্যবহার করে ViewModel-এ নেভিগেশন আর্গুমেন্ট (কী) পাস করতে হয়।
এটি কীভাবে কাজ করে
- একটি কাস্টম
ViewModelProvider.Factoryতৈরি করা হয় যা কনস্ট্রাক্টর প্যারামিটার হিসেবে নেভিগেশন কী নেয়। entryকম্পোজ করার উপযুক্ত আইটেমের মধ্যে,viewModel(factory = ...)ব্যবহার করেViewModelইনস্ট্যান্স তৈরি করা হয় এবং ফ্যাক্টরিতে বর্তমান নেভিগেশন কী পাস করা হয়। এর ফলে নেভিগেশন কীViewModel-এর জন্য উপলভ্য হয়।
মনে রাখবেন: rememberViewModelStoreNavEntryDecorator-কে NavDisplay-এর entryDecorators-এ যোগ করা হয়। এটি নিশ্চিত করে যে ViewModel সঠিকভাবে তাদের সংশ্লিষ্ট NavEntry-এ স্কোপ করা হয়েছে, যাতে প্রতিটি অনন্য নেভিগেশন কী-এর জন্য একটি নতুন ViewModel ইনস্ট্যান্স তৈরি হয়।
/* * Copyright 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.nav3recipes.passingarguments.viewmodels.basic import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.compose.dropUnlessResumed import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator import androidx.navigation3.runtime.entryProvider import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator import androidx.navigation3.ui.NavDisplay import com.example.nav3recipes.content.ContentBlue import com.example.nav3recipes.content.ContentGreen import com.example.nav3recipes.ui.setEdgeToEdgeConfig data object RouteA data class RouteB(val id: String) class BasicViewModelsActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { setEdgeToEdgeConfig() super.onCreate(savedInstanceState) setContent { val backStack = remember { mutableStateListOf<Any>(RouteA) } NavDisplay( backStack = backStack, onBack = { backStack.removeLastOrNull() }, // In order to add the `ViewModelStoreNavEntryDecorator` (see comment below for why) // we also need to add the default `NavEntryDecorator`s as well. These provide // extra information to the entry's content to enable it to display correctly // and save its state. entryDecorators = listOf( rememberSaveableStateHolderNavEntryDecorator(), rememberViewModelStoreNavEntryDecorator() ), entryProvider = entryProvider { entry<RouteA> { ContentGreen("Welcome to Nav3") { LazyColumn { items(10) { i -> Button(onClick = dropUnlessResumed { backStack.add(RouteB("$i")) }) { Text("$i") } } } } } entry<RouteB> { key -> // Note: We need a new ViewModel for every new RouteB instance. Usually // we would need to supply a `key` String that is unique to the // instance, however, the ViewModelStoreNavEntryDecorator (supplied // above) does this for us, using `NavEntry.contentKey` to uniquely // identify the viewModel. // // tl;dr: Make sure you use rememberViewModelStoreNavEntryDecorator() // if you want a new ViewModel for each new navigation key instance. ScreenB(viewModel = viewModel(factory = RouteBViewModel.Factory(key))) } } ) } } } @Composable fun ScreenB(viewModel: RouteBViewModel = viewModel()) { ContentBlue("Route id: ${viewModel.key.id} ") } class RouteBViewModel( val key: RouteB ) : ViewModel() { class Factory( private val key: RouteB, ) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { return RouteBViewModel(key) as T } } }
ViewModels-এ আর্গুমেন্ট পাস করা (Koin)
এই রেসিপি দেখায় যে কীভাবে ডিপেন্ডেন্সি ইনজেকশনের জন্য Koin ব্যবহার করে ViewModel-এ নেভিগেশন আর্গুমেন্ট (কী) পাস করতে হয়।
এটি কীভাবে কাজ করে
ViewModelপ্রদান করে এমন একটি Koin মডিউল সংজ্ঞায়িত করা হয়।ViewModelইনস্ট্যান্স পেতেkoinViewModelকম্পোজেবল ফাংশন ব্যবহার করা হয়।parametersOf(key)ব্যবহার করে নেভিগেশন কীViewModel-এর কনস্ট্রাক্টরে পাস করা হয়। এর ফলে নেভিগেশন কীViewModel-এর জন্য উপলভ্য হয়।
মনে রাখবেন: rememberViewModelStoreNavEntryDecorator, NavDisplay-এর entryDecorators-এ যোগ করা হয়েছে। এটি নিশ্চিত করে যে ViewModel সঠিকভাবে তাদের সংশ্লিষ্ট NavEntry-এ স্কোপ করা হয়েছে, যাতে প্রতিটি অনন্য নেভিগেশন কী-এর জন্য একটি নতুন ViewModel ইনস্ট্যান্স তৈরি হয়।
package com.example.nav3recipes.passingarguments.viewmodels.koin import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.lifecycle.ViewModel import androidx.lifecycle.compose.dropUnlessResumed import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator import androidx.navigation3.runtime.entryProvider import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator import androidx.navigation3.ui.NavDisplay import com.example.nav3recipes.content.ContentBlue import com.example.nav3recipes.content.ContentGreen import com.example.nav3recipes.ui.setEdgeToEdgeConfig import org.koin.compose.KoinApplication import org.koin.compose.viewmodel.koinViewModel import org.koin.core.module.dsl.viewModelOf import org.koin.core.parameter.parametersOf import org.koin.dsl.koinConfiguration import org.koin.dsl.module data object RouteA data class RouteB(val id: String) class KoinViewModelsActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { setEdgeToEdgeConfig() super.onCreate(savedInstanceState) setContent { val backStack = remember { mutableStateListOf<Any>(RouteA) } // Koin Compose Entry point KoinApplication( configuration = koinConfiguration { modules(appModule) } ) { NavDisplay( backStack = backStack, onBack = { backStack.removeLastOrNull() }, // In order to add the `ViewModelStoreNavEntryDecorator` (see comment below for why) // we also need to add the default `NavEntryDecorator`s as well. These provide // extra information to the entry's content to enable it to display correctly // and save its state. entryDecorators = listOf( rememberSaveableStateHolderNavEntryDecorator(), rememberViewModelStoreNavEntryDecorator() ), entryProvider = entryProvider { entry<RouteA> { ContentGreen("Welcome to Nav3") { LazyColumn { items(10) { i -> Button(onClick = dropUnlessResumed { backStack.add(RouteB("$i")) }) { Text("$i") } } } } } entry<RouteB> { key -> val viewModel = koinViewModel<RouteBViewModel> { parametersOf(key) } ScreenB(viewModel = viewModel) } } ) } } } } // Local Koin Module private val appModule = module { viewModelOf(::RouteBViewModel) } @Composable fun ScreenB(viewModel: RouteBViewModel) { ContentBlue("Route id: ${viewModel.navKey.id} ") } class RouteBViewModel(val navKey: RouteB) : ViewModel()