การส่งอาร์กิวเมนต์ไปยัง ViewModel (Hilt)
สูตรนี้แสดงวิธีส่งอาร์กิวเมนต์การนำทาง (คีย์) ไปยัง ViewModel โดยใช้ Hilt สำหรับการแทรกทรัพยากร Dependency
วิธีการทำงาน
ตัวอย่างนี้ใช้ฟีเจอร์การแทรกความช่วยเหลือของ Dagger/Hilt
ViewModelมีคำอธิบายประกอบด้วย@HiltViewModelและตัวสร้างใช้@AssistedInjectเพื่อรับคีย์การนำทาง (ซึ่งมีคำอธิบายประกอบด้วย@Assisted)- มีการกำหนดอินเทอร์เฟซ
@AssistedFactoryเพื่อสร้างViewModel - ใช้ฟังก์ชันที่ประกอบกันได้
hiltViewModelเพื่อรับอินสแตนซ์ViewModelเราจะมอบcreationCallbackเพื่อส่งคีย์การนำทางไปยังโรงงาน ซึ่งจะทำให้ViewModelใช้คีย์ดังกล่าวได้
หมายเหตุ: ระบบจะเพิ่ม rememberViewModelStoreNavEntryDecorator ลงในentryDecoratorsของNavDisplay ซึ่งจะช่วยให้มั่นใจได้ว่า 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 (พื้นฐาน)
สูตรนี้แสดงวิธีส่งอาร์กิวเมนต์การนำทาง (คีย์) ไปยัง ViewModel โดยใช้ ViewModelProvider.Factory ที่กำหนดเอง
วิธีการทำงาน
- สร้าง
ViewModelProvider.Factoryที่กำหนดเองซึ่งใช้คีย์การนำทางเป็นพารามิเตอร์ของตัวสร้าง - ภายใน Composable
entryจะใช้viewModel(factory = ...)เพื่อสร้างอินสแตนซ์ViewModelโดยส่งคีย์การนำทางปัจจุบันไปยัง Factory ซึ่งจะทำให้ViewModelใช้คีย์การนำทางได้
หมายเหตุ: ระบบจะเพิ่ม rememberViewModelStoreNavEntryDecorator ลงในentryDecoratorsของNavDisplay ซึ่งจะช่วยให้มั่นใจได้ว่า 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 } } }
การส่งอาร์กิวเมนต์ไปยัง ViewModel (Koin)
สูตรนี้แสดงวิธีส่งอาร์กิวเมนต์การนำทาง (คีย์) ไปยัง ViewModel โดยใช้ Koin สำหรับการแทรกทรัพยากร Dependency
วิธีการทำงาน
- มีการกำหนดโมดูล Koin ที่ให้
ViewModel - ใช้ฟังก์ชันที่ประกอบกันได้
koinViewModelเพื่อรับอินสแตนซ์ViewModel - ระบบจะส่งคีย์การนำทางไปยังเครื่องมือสร้างของ
ViewModelโดยใช้parametersOf(key)ซึ่งจะทำให้ViewModelใช้คีย์การนำทางได้
หมายเหตุ: ระบบจะเพิ่ม rememberViewModelStoreNavEntryDecorator ลงในentryDecoratorsของNavDisplay ซึ่งจะช่วยให้มั่นใจได้ว่า 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()