I had a custom layout like this: ```fun MyLayout(...
# compose
d
I had a custom layout like this:
Copy code
fun MyLayout(someState: Int) {
  Layout(content) { measureables, constraints -> 
    if(someState) thisLogic() else thatLogic()
  }
}
It updates layout whenever
someState
changes, but now I want to factor out MeasurePolicy into a separate class (there will be several of them):
Copy code
fun MyLayout(someState: Int) {
  val policy = remember { MyMeasurePolicy({ someState }) }
  Layout(content, policy)
}
class MyMeasurePolicy(private val someState: () -> Int)) : MeasurePolicy
I have made
someState
into a "provider lambda" to avoid recreating policy on each
someState
change (it will change more or less often). But now my layout is not re-measured when
someState
changes. Any hints on how to do this right?
s
Well, you need to pass the `key`s to
remember
(this is also how
LazyList
does it)
a
Parameters are not states, so you must turn it into a state first:
Copy code
val state = rememberUpdatedState(someState)
val policy = remember(state) { MyMeasurePolicy(state::value) }
Layout(content, policy)
d
but wouldn't this recreate MyMeasurePolicy on each someState сhange? Ah, or it won't. So if I turn it into state it willl work why? Because
measure
is re-invoked whenever
State
values inside it change? I didn't know this
As a sidenote, it's interesting that
Copy code
Layout(content) { m, c -> if (someState) this() else that() }
works perfectly and it "captures"
someState
correctly, and the lambda itself is the same instance across
someState
changes, but as soon as I try to turn it into a class (which in fact lambda is under the hood IIUC), it's suddenly not as straightforward to achieve without having a lot of allocations of said class.
a
In your first snippet, the lambda (
MeasurePolicy
) is recreated every time
someState
changes. If you do not
remember
the
MyMeasurePolicy
in the second snippet it will also work
d
no, if I do this
Copy code
Layout(content) { m, c -> 
  Timber.d("$this, someState=$someState")
  if (someState) this() else that() 
}
it prints the same
this
but different `someState`'s, e.g.
Copy code
androidx.compose.ui.node.LayoutNode$measureScope$1@98c1123, someState=1
androidx.compose.ui.node.LayoutNode$measureScope$1@98c1123, someState=2
pointer stays the same. Could it be because I'm running this on API 30 and it's not being compiled to an anonymous class?
s
In your example,
this
seems to be the measure scope, not lambda instance And to answer your previous question, if state is read in measure or draw, it invalidates related layouts on write
d
Oh, right. I thought about it being the
MeasureScope
, but seeing
$1
made me think its a lambda's
this
. But it should be the measure scope, indeed, that's the receiver. Good to know about measure-invalidation thing, thanks! Makes sense now why it works, I didn't know this bit.