Created
August 17, 2021 08:20
-
-
Save Aidanvii7/8ddf1dc4a94ca23c5dbc9ff580ea4c2f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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