Following the discussion at <https://kotlinlang.sl...
# compose-android
a
Following the discussion at https://kotlinlang.slack.com/archives/CJLTWPH7S/p1693324536742409 and many others before, what's the best way to deal with a situation where you need to adjust things based on State in both the parent & child composables, but the State's value changes only in the child? I'll take a simple example first, to explain what I mean (code in thread). I'm displaying a banner ad and I need to conditionally apply a Modifier to AdView as well as some changes here and there in the parent composable, based on whether or not the ad has loaded.
Copy code
@Composable
fun ColumnScope.BannerAd(
  adUnitId: String,
  adLoaded: MutableState<Boolean>? = null,
  viewUpdated: (AdView) -> Unit,
) = if (LocalInspectionMode.current) {
  Text("AdView")
} else AndroidView(factory = {
  AdView(it).apply {
    …init…

    if (adLoaded != null) adListener = object : AdListener() {
      override fun onAdFailedToLoad(error: LoadAdError) { adLoaded.value = false }
      override fun onAdLoaded() { adLoaded.value = true }
    }
  }
}, if (adLoaded?.value == true) Modifier.something() else Modifier, viewUpdated)
I could pass
adListener
and
Modifier
from the parent composable itself, which bypasses the need to pass a
MutableState
to this child, but that introduces the bigger question in more complicated situations: what do I do if it's not always possible to move things out? Do I forego this separate child composable entirely, and put everything in the parent composable? Or do I deconstruct MutableState and pass value/setter separately? From what I've seen with benchmarks and Layout Inspector, there aren't any performance issues or unnecessary recompositions when I pass a MutableState.
s
The rules are mostly simple. Hoist the state to the lowest possible ancestor (in your case the parent) Pass down to the child composable a way to read the state (This can be
T
or
() -> T
) Also pass down a way for the child to change the state by passing a
(T) -> Unit
if the child also needs to change the value. Never pass
State<T>
and especially not
MutableState<T>
down. For the first just because
() -> T
is better, and for the second don’t do it because you introduce a very hard to reason about flow of where you state changes.
a
I see, it would also make testing a bit annoying. Thank you!