https://kotlinlang.org logo
Title
d

dazza5000

04/14/2022, 5:18 PM
Does anyone have a suggestions on how I could make this a reusable component / modifier?
a

Adam Powell

04/14/2022, 5:34 PM
That looks pretty unstable; at a minimum it creates a data dependency where composition depends on layout, which will never resolve in a single frame. What are you trying to do?
d

dazza5000

04/14/2022, 5:34 PM
you can see what I am trying to do in the original thread https://kotlinlang.slack.com/archives/CJLTWPH7S/p1649264638345149
a

Adam Powell

04/14/2022, 5:41 PM
ok, so it's predicated on misuse of LazyRow. This is a dead end; you're not going to be able to implement something free of jank/edge cases this way since you're depending on backtracking in both composition and layout that can't happen within a single frame. It's going to lead to flickering UIs and it will be difficult to test.
If you're using this modifier on multiple row children then that's where the backtracking within layout comes from even if the backtracking in composition is resolved
even if you could resolve the layout backtracking part, you would still get a UI where the user could scroll the row and suddenly its height would grow mid-scroll
k

Kirill Grouchnikov

04/14/2022, 5:47 PM
If the content is loaded lazily, how do you know the "max" height of yet-unloaded elements?
a

Adam Powell

04/14/2022, 5:47 PM
exactly, you don't
now, presuming you're determined enough to ignore all of the above and do it anyway, you can use
Modifier.layout
instead of
defaultMinSize
so that you can work in pixels rather than having to consult
LocalDensity
, then you could factor out an explicit hoisted state object to hold the max, passing that same hoisted state object any time you use the modifier. Custom modifiers are written using extension functions, so you could do
fun Modifier.myJumpyHeight(state: MyJumpyHeightState) = onSizeChanged { ... }.layout { ... }
d

dazza5000

04/14/2022, 5:50 PM
i am determined to ignore all common sense and warnings
😂 1
a

Adam Powell

04/14/2022, 5:50 PM
so long as we're on the same page there 😄
d

dazza5000

04/14/2022, 5:51 PM
indeed 🙂
another noob question - does the state object need to have any characterists/interfaces implemented to be persisted across composition or to just be
remembered
when defined
like it doesn't need to have it's properties in saver or anything special......or...does it?
a

Adam Powell

04/14/2022, 5:54 PM
only if you want it to. Its properties that you want to be observable and cause relayout/recomposition should be
by mutableStateOf
, etc. but other than that, nothing special
🍕 1
d

dazza5000

04/14/2022, 6:20 PM
I didn't migrate to use .layout yet, but this is what I have so far
fun Modifier.myJumpyHeight(state: MyJumpyHeightState, density: Density) = onSizeChanged { size ->
    val itemHeight = with(density) {
        val height = size.height
        height.toDp()
    }

    if (itemHeight > state.minHeight ?: 0.dp) {
        state.minHeight = itemHeight
    }
}.defaultMinSize(minHeight = state.minHeight ?: Dp.Unspecified)

data class MyJumpyHeightState(var minHeight: Dp? = null)
and it works
I define these in a parent composable
val density = LocalDensity.current

    var myJumpyHeightState by remember { mutableStateOf(MyJumpyHeightState()) }
and then pass it in as a modifier of an item that is part of a
LazyRow
a

Adam Powell

04/14/2022, 6:22 PM
never use a
var
in a
data class
constructor param, compose or no compose; it means you have unstable equality and hash codes over time. The former you can rationalize in some circumstances, the latter is a Problem(TM) if someone ever uses one as the key in a map
👍 1
you'll want that to be defined as
class MyJumpyHeightState(minHeight: Int = 0) {
  var minHeight by mutableStateOf(minHeight)
}
and then use
val myJumpyHeightState = remember { MyJumpyHeightState() }
instead of the external
mutableStateOf
👍 1
d

dazza5000

05/02/2023, 2:30 PM
sorry for reviving this, but are there any new solutions for this that have been developed in the past year?