Skip to content

Instantly share code, notes, and snippets.

@volo-droid
Last active March 24, 2025 00:21
Show Gist options
  • Select an option

  • Save volo-droid/54d4443c28ec92ecb0a0741d16d4c47c to your computer and use it in GitHub Desktop.

Select an option

Save volo-droid/54d4443c28ec92ecb0a0741d16d4c47c to your computer and use it in GitHub Desktop.
ViewModel delegates for Toothpick injection library
/*
* Copyright (c) 2025 Volodymyr Galandzij
*
* 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.
*/
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import toothpick.Scope
import toothpick.Toothpick
// ----------------------
// Fragment.viewModelOf
// ----------------------
inline fun <reified VM : ViewModel> Fragment.viewModelOf(
noinline constructor: () -> VM,
noinline ownerProducer: () -> ViewModelStoreOwner = { this },
noinline extrasProducer: (() -> CreationExtras)? = null
): Lazy<VM> =
viewModelOf(ownerProducer, extrasProducer) { constructor() }
inline fun <reified VM : ViewModel, reified T1> Fragment.viewModelOf(
noinline constructor: (T1) -> VM,
noinline ownerProducer: () -> ViewModelStoreOwner = { this },
noinline extrasProducer: (() -> CreationExtras)? = null
): Lazy<VM> =
viewModelOf(ownerProducer, extrasProducer) { constructor(get()) }
inline fun <reified VM : ViewModel, reified T1, reified T2> Fragment.viewModelOf(
noinline constructor: (T1, T2) -> VM,
noinline ownerProducer: () -> ViewModelStoreOwner = { this },
noinline extrasProducer: (() -> CreationExtras)? = null
): Lazy<VM> =
viewModelOf(ownerProducer, extrasProducer) { constructor(get(), get()) }
inline fun <reified VM : ViewModel, reified T1, reified T2, reified T3> Fragment.viewModelOf(
noinline constructor: (T1, T2, T3) -> VM,
noinline ownerProducer: () -> ViewModelStoreOwner = { this },
noinline extrasProducer: (() -> CreationExtras)? = null
): Lazy<VM> =
viewModelOf(ownerProducer, extrasProducer) { constructor(get(), get(), get()) }
// ------------------------------
// Fragment.activityViewModelOf
// ------------------------------
inline fun <reified VM : ViewModel> Fragment.activityViewModelOf(
noinline constructor: () -> VM,
noinline extrasProducer: (() -> CreationExtras)? = null
): Lazy<VM> =
activityViewModelOf(extrasProducer) { constructor() }
inline fun <reified VM : ViewModel, reified T1> Fragment.activityViewModelOf(
noinline constructor: (T1) -> VM,
noinline extrasProducer: (() -> CreationExtras)? = null
): Lazy<VM> =
activityViewModelOf(extrasProducer) { constructor(get()) }
inline fun <reified VM : ViewModel, reified T1, reified T2> Fragment.activityViewModelOf(
noinline constructor: (T1, T2) -> VM,
noinline extrasProducer: (() -> CreationExtras)? = null
): Lazy<VM> =
activityViewModelOf(extrasProducer) { constructor(get(), get()) }
inline fun <reified VM : ViewModel, reified T1, reified T2, reified T3> Fragment.activityViewModelOf(
noinline constructor: (T1, T2, T3) -> VM,
noinline extrasProducer: (() -> CreationExtras)? = null
): Lazy<VM> =
activityViewModelOf(extrasProducer) { constructor(get(), get(), get()) }
// -------------------------------
// ComponentActivity.viewModelOf
// -------------------------------
inline fun <reified VM : ViewModel> ComponentActivity.viewModelOf(
noinline constructor: () -> VM,
noinline extrasProducer: (() -> CreationExtras)? = null
): Lazy<VM> =
viewModelOf(extrasProducer) { constructor() }
inline fun <reified VM : ViewModel, reified T1> ComponentActivity.viewModelOf(
noinline constructor: (T1) -> VM,
noinline extrasProducer: (() -> CreationExtras)? = null
): Lazy<VM> =
viewModelOf(extrasProducer) { constructor(get()) }
inline fun <reified VM : ViewModel, reified T1, reified T2> ComponentActivity.viewModelOf(
noinline constructor: (T1, T2) -> VM,
noinline extrasProducer: (() -> CreationExtras)? = null
): Lazy<VM> =
viewModelOf(extrasProducer) { constructor(get(), get()) }
inline fun <reified VM : ViewModel, reified T1, reified T2, reified T3> ComponentActivity.viewModelOf(
noinline constructor: (T1, T2, T3) -> VM,
noinline extrasProducer: (() -> CreationExtras)? = null
): Lazy<VM> =
viewModelOf(extrasProducer) { constructor(get(), get(), get()) }
// ---------
// Helpers
// ---------
inline fun <reified VM : ViewModel> Fragment.viewModelOf(
noinline ownerProducer: () -> ViewModelStoreOwner,
noinline extrasProducer: (() -> CreationExtras)? = null,
noinline viewModelProducer: (Scope.() -> VM)
): Lazy<VM> =
viewModels(ownerProducer, extrasProducer) {
viewModelFactory(viewModelProducer) { scopeName ->
Toothpick.openScopes(requireActivity().application, scopeName)
}
}
inline fun <reified VM : ViewModel> Fragment.activityViewModelOf(
noinline extrasProducer: (() -> CreationExtras)? = null,
noinline injectedViewModelProducer: (Scope.() -> VM)
): Lazy<VM> =
activityViewModels(extrasProducer) {
viewModelFactory(injectedViewModelProducer) { scopeName ->
Toothpick.openScopes(requireActivity().application, scopeName)
}
}
inline fun <reified VM : ViewModel> ComponentActivity.viewModelOf(
noinline extrasProducer: (() -> CreationExtras)? = null,
crossinline viewModelProducer: (Scope.() -> VM)
): Lazy<VM> =
viewModels(extrasProducer) {
viewModelFactory(viewModelProducer) { scopeName ->
Toothpick.openScopes(application, scopeName)
}
}
inline fun <reified VM : ViewModel> viewModelFactory(
crossinline viewModelProducer: (Scope.() -> VM),
scopeName: Any = VM::class.java,
crossinline openScopeBlock: (name: Any) -> Scope
): ViewModelProvider.Factory =
viewModelFactory {
initializer {
val scope = openScopeBlock(scopeName)
scope.viewModelProducer().apply {
Toothpick.inject(this, scope)
addCloseable {
Toothpick.closeScope(scopeName)
}
}
}
}
inline fun <reified T> Scope.get(): T = getInstance(T::class.java)
class MyViewModel(
private val injectedService1: MyInjectedService1,
private val injectedService2: MyInjectedService2
) : ViewModel()
class MySharedViewModel(
private val injectedFooRepository: FooRepository
) : ViewModel()
class MainFragment : Fragment() {
private val viewModel by viewModelOf(::MyViewModel)
private val sharedViewModel by activityViewModelOf(::MySharedViewModel)
// …
}
class MainActivity : AppCompatActivity() {
// Activity and Fragment will share the same instance of MySharedViewModel
private val sharedViewModel by viewModelOf(::MySharedViewModel)
// …
}
@volo-droid
Copy link
Author

If you would like to inject ViewModel via Toothpick binding you can use:

inline fun <reified VM : ViewModel> Fragment.viewModel(
    noinline ownerProducer: () -> ViewModelStoreOwner = { this },
    noinline extrasProducer: (() -> CreationExtras)? = null
): Lazy<VM> =
    viewModelOf(ownerProducer, extrasProducer) { getInstance(VM::class.java) }

That would allow ViewModel constructor to take Provider or Lazy as parameters.
Keep in mind it'd require either to register an explicit Toothpick binding or mark the ViewModel's constructor with @Inject or @InjectConstructor annotation so that Toothpick generates a Factory for the class.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment