ViewModelExt.kt
@Composable
inline fun <reified VM : ViewModel> assistedViewModel(
viewModelStoreOwner: ViewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
"No ViewModelStoreOwner was provided via LocalViewModelStoreOwner"
},
crossinline viewModelProducer: (SavedStateHandle) -> VM
): VM {
val factory = if (viewModelStoreOwner is NavBackStackEntry) {
object : AbstractSavedStateViewModelFactory(viewModelStoreOwner, viewModelStoreOwner.arguments) {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(key: String, modelClass: Class<T>, handle: SavedStateHandle): T {
return viewModelProducer(handle) as T
}
}
} else {
// Use the default factory provided by the ViewModelStoreOwner
// and assume it is an @AndroidEntryPoint annotated fragment or activity
null
}
return viewModel(viewModelStoreOwner, factory = factory)
}
fun Context.extractActivity(): Activity {
var ctx = this
while (ctx is ContextWrapper) {
if (ctx is Activity) {
return ctx
}
ctx = ctx.baseContext
}
throw IllegalStateException(
"Expected an activity context for creating a HiltViewModelFactory for a " +
"NavBackStackEntry but instead found: $ctx"
)
}
MainActivity.kt
@HiltAndroidApp
class MyApplication : Application()
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
Surface(color = MaterialTheme.colors.background) {
MyApp()
}
}
}
}
}
@Composable
fun MyApp() {
val navController = rememberNavController()
NavHost(navController, startDestination = "Screen1") {
composable("Screen1") {
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(20) {
Text(
text = "Item : $it",
modifier = Modifier
.fillMaxWidth()
.clickable {
navController.navigate("Screen2/$it")
}
.padding(16.dp)
)
}
}
}
composable(
route = "Screen2/{id}",
arguments = listOf(
navArgument("id") { type = NavType.IntType },
)
) {
val arguments = requireNotNull(it.arguments)
val id = arguments.getInt("id")
val viewModel = assistedViewModel { savedStateHandle ->
Screen2ViewModel.provideFactory(LocalContext.current)
.create(savedStateHandle, id)
}
Screen2(viewModel)
}
}
}
@Composable
fun Screen2(viewModel: Screen2ViewModel) {
Text(
text = viewModel.greet(),
modifier = Modifier.padding(24.dp)
)
}
class Screen2ViewModel @AssistedInject constructor(
private val nameProvider: NameProvider,
@Assisted private val savedStateHandle: SavedStateHandle,
@Assisted private val id: Int
) : ViewModel() {
@AssistedFactory
interface Factory {
fun create(savedStateHandle: SavedStateHandle, id: Int): Screen2ViewModel
}
@EntryPoint
@InstallIn(ActivityComponent::class)
interface ActivityCreatorEntryPoint {
fun getScreen2ViewModelFactory(): Factory
}
companion object {
fun provideFactory(context: Context): Factory {
val activity = context.extractActivity()
return EntryPoints.get(activity, ActivityCreatorEntryPoint::class.java)
.getScreen2ViewModelFactory()
}
}
fun greet(): String {
return "Hello ${nameProvider.name()} : id = $id"
}
}
@Singleton
class NameProvider @Inject constructor() {
fun name(): String {
return "Android"
}
}
Issue (https://github.com/google/dagger/issues/2287) は 1月からあるけど、進んでなさそう。
0 件のコメント:
コメントを投稿