nitrog42
04/21/2021, 9:51 AMBox {
Column(Modifier.padding(bottom = /* height of the snackbar*/)) {
//content
}
Snackbar {
Text("My text")
}
}
what is the best method to do that? a custom layout ? Do you have any resources that could show me some code sample about this?Filip Wiesner
04/21/2021, 9:56 AMnitrog42
04/21/2021, 9:59 AMFilip Wiesner
04/21/2021, 10:03 AMonSizeChanged
Modifier which will give you the size right after measure.
Invoke onSizeChanged when the size of the modifier immediately after it has changed. If there is no modifier following onSizeChanged, the content size of the layout is reported.
onSizeChanged will only be invoked during the first time measurement or when the size has changed.
nitrog42
04/21/2021, 10:04 AMFilip Wiesner
04/21/2021, 10:05 AMnitrog42
04/21/2021, 10:06 AMLocalDensity.current.run { coordinates.size.height.toDp() }
but unfortunatelly I get a runtime crash
CompositionLocal LocalDensity not present
so I'm looking in how to do thatnitrog42
04/21/2021, 10:08 AMFilip Wiesner
04/21/2021, 10:08 AMLocalDensity
should be provided by setContent
method in your Activity
Filip Wiesner
04/21/2021, 10:09 AMnitrog42
04/21/2021, 10:09 AMstate
worked before but this is super powerful ! Thanks for your helpFilip Wiesner
04/21/2021, 10:11 AMFilip Wiesner
04/21/2021, 10:13 AMremember {}
function is really important to understand too if you are starting with Composenitrog42
04/21/2021, 10:14 AMFilip Wiesner
04/21/2021, 10:15 AMAdam Powell
04/21/2021, 1:34 PMonSizeChanged
to write a state read by a peer/sibling's layout.Adam Powell
04/21/2021, 1:35 PMAdam Powell
04/21/2021, 1:36 PMnitrog42
04/21/2021, 2:09 PMnitrog42
04/21/2021, 2:20 PM@Composable
fun Content(displaySnackbar: Boolean, paddingBottom: Int, onSnackbarMeasured: (Int) -> Unit) {
Box {
Column(
modifier = Modifier.fillMaxHeight().padding(
bottom = if (displaySnackbar) with(LocalDensity.current) { paddingBottom.toDp() }
else 0.dp)
) {
//content
}
if (displaySnackbar) {
Snackbar(modifier = Modifier.align(Alignment.BOTTOM).onSizeChanged {
onSnackbarMeasured(it.height)
}) {
Text("My text")
}
}
}
}
@Composable
fun Screen() {
val displaySnackbar by remember { mutableStateOf(false) /* Should be real data coming from a viewmodel/livedata/state*/ }
var paddingBottom by remember { mutableStateOf(0) }
Content(displaySnackbar = displaySnackbar, paddingBottom = paddingBottom, onSnackbarMeasured = {
paddingBottom = it
})
}
nitrog42
04/21/2021, 2:22 PMAdam Powell
04/21/2021, 2:28 PMColumn
instead of an outer Box
? You could skip the out of band layout adjustment entirelyAdam Powell
04/21/2021, 2:29 PMnitrog42
04/21/2021, 2:29 PMAdam Powell
04/21/2021, 2:30 PMnitrog42
04/21/2021, 2:30 PMnitrog42
04/21/2021, 2:30 PMAdam Powell
04/21/2021, 2:30 PMnitrog42
04/21/2021, 2:35 PMnitrog42
04/21/2021, 2:36 PMFilip Wiesner
04/21/2021, 2:38 PMnitrog42
04/21/2021, 2:40 PMnitrog42
04/21/2021, 2:41 PMAdam Powell
04/21/2021, 2:41 PMpadding(NNN.dp)
isn't quite smart enough to do it, and you need be deliberate about the order of measurement.Adam Powell
04/21/2021, 2:42 PMnitrog42
04/21/2021, 2:42 PMAdam Powell
04/21/2021, 2:43 PMpadding
modifier when the target size changes, you're performing that work during composition, and composition has finished for that frame already if something is receiving a new size. That happens during layout.Adam Powell
04/21/2021, 2:44 PMAdam Powell
04/21/2021, 2:45 PMAdam Powell
04/21/2021, 2:45 PMnitrog42
04/21/2021, 2:46 PMnitrog42
04/21/2021, 3:04 PMAdam Powell
04/21/2021, 3:08 PMScaffold
already does this for snackbars, it passes a value to the content function that represents the space consumed by things like snackbarsnitrog42
04/21/2021, 3:09 PMnitrog42
04/21/2021, 3:14 PMAnalogue of Layout which allows to subcompose the actual content during the measuring stage for example to use the values calculated during the measurement as params for the composition of the children.
Possible use cases:
You want to use the size of one child during the composition of the second child.
nitrog42
04/21/2021, 3:16 PMAdam Powell
04/21/2021, 6:07 PMAdam Powell
04/21/2021, 6:08 PMAdam Powell
04/21/2021, 6:08 PMnitrog42
04/22/2021, 6:50 AMnitrog42
04/22/2021, 9:54 AMwhich provides a modifier for inset padding that doesn't perform the state read until layout time; the actual modifier itself doesn't get recreated for these changes.I didn't think
modifier = Modifier.fillMaxHeight().padding(
bottom = if (displaySnackbar) with(LocalDensity.current) { paddingBottom.toDp() }
else 0.dp)
would recreate the Modifier.padding() each time.
So I suppose I can make a custom modifier :
private fun Modifier.avoidSnackbar() = composed {
AvoidSnackbarModifier()
}
private class AvoidSnackbarModifier() : LayoutModifier {
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
): MeasureResult {
val snackbarHeight = // Find a way to get/obtain snackbar's height here
val placeable = measurable.measure(constraints.offset(0, -snackbarHeight))
val width = placeable.width
val height = (placeable.height + snackbarHeight)
.coerceIn(constraints.minHeight, constraints.maxHeight)
return layout(width, height) {
placeable.place(left, top)
}
}
}
fun Screen() {
Box {
Column(modifier = Modifier.avoidSnackbar()) { }
Snackbar() {}
}
}
But now, I'm still uncertain about how to "give" the Snackbar height to my custom avoidSnackbar() modifier.
Should I use the same instance and give it to both ? (I don't think it's good idea?!)
fun Screen() {
val modifier = Modifier.avoidSnackbar()
Box {
Column(modifier = modifier) { }
Snackbar(modifier = modifier) {}
}
}
I also saw the ParentDataModifier which allows to put data for the parent layout to place the child, but it would require a custom layout or a
Modifier.layout { } on my Box layout to make it work?
I'm trying hard to understand how everything works here but I'm still unsure of how to achiveve the right way to do what I wanted π
If you have any lead (doc, example), it would be great (if you have time to answer) !Adam Powell
04/22/2021, 2:18 PMModifier.layout {}
to define a layout modifier inline rather than declare a whole class for it, and there's nothing there that looks like it needs composed {}
Adam Powell
04/22/2021, 2:20 PMnitrog42
04/22/2021, 2:26 PMnitrog42
04/22/2021, 2:30 PMAdam Powell
04/22/2021, 2:37 PMAdam Powell
04/22/2021, 2:39 PMwith(LocalDensity.current) { something.toDp() }
βοΈ this is a sign that something is trying to move backwards through the phases and suggests that there's probably a way to stay in the later phases for whatever is going onnitrog42
04/22/2021, 2:41 PM