Skip to content

Instantly share code, notes, and snippets.

@Aidanvii7
Created August 17, 2021 08:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Aidanvii7/8ddf1dc4a94ca23c5dbc9ff580ea4c2f to your computer and use it in GitHub Desktop.
Save Aidanvii7/8ddf1dc4a94ca23c5dbc9ff580ea4c2f to your computer and use it in GitHub Desktop.
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.staticCompositionLocalOf
import kotlin.reflect.KClass
sealed class Providable<T : Any>(
val type: KClass<T>
) {
abstract val factory: () -> T
}
class Factory<T : Any>(
type: KClass<T>,
override val factory: () -> T,
) : Providable<T>(type)
class Single<T : Any>(
type: KClass<T>,
factory: () -> T,
) : Providable<T>(type) {
override val factory: () -> T by lazy {
val provided = factory()
val actualFactory: () -> T = { provided }
actualFactory
}
}
inline fun <reified T : Any> factory(
noinline builder: () -> T
): Providable<T> = Factory(
type = T::class,
factory = builder,
)
inline fun <reified T : Any> single(
noinline builder: () -> T
): Providable<T> = Single(
type = T::class,
factory = builder,
)
@Composable
fun Provide(
vararg values: Providable<*>,
child: @Composable () -> Unit,
) {
val entries = if (LocalProvider.current !== defaultProvided) {
LocalProvider.current.entries.toMutableMap()
} else {
mutableMapOf()
}
values.forEach { providable ->
entries[providable.type] = providable
}
CompositionLocalProvider(
LocalProvider provides Provided(entries.toMap()),
content = child,
)
}
@Composable
inline fun <reified T : Any> getProvided(): T? =
LocalProvider.current.get()
inline fun <reified T> Provided.get(): T? =
entries[T::class]?.factory?.invoke() as? T
data class Provided(
val entries: Map<KClass<*>, Providable<*>>
) {
companion object {
operator fun invoke(
vararg providables: Providable<Any>,
) = Provided(
entries = providables.associateBy { providable ->
providable.type
}
)
}
}
val LocalProvider: ProvidableCompositionLocal<Provided> = staticCompositionLocalOf {
defaultProvided
}
val defaultProvided: Provided by lazy { Provided() }
fun interface TrackScreenView {
operator fun invoke(name: String)
}
@Composable
fun HigherLevelComposable() {
Provide(
single { TrackScreenView { TODO("Not yet implemented") } },
) {
// no argument passing
LowerLevelComposable()
}
}
@Composable
fun LowerLevelComposable() {
// Retrieve a TrackScreenView instance from higher up the tree.
// nullable to allow previews to work where an instance may not be provided,
// so should never be used for retrieving data required by previews.
val trackScreenView: TrackScreenView? = getProvided()
// Call it inside a LaunchedEffect for example
LaunchedEffect(trackScreenView) {
trackScreenView ?: return@LaunchedEffect
trackScreenView("LowerLevelComposable")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment