https://kotlinlang.org logo
#compose-android
Title
# compose-android
l

Lucas Villa Verde

10/09/2023, 4:51 PM
Hey everyone, how are you today? Have a question about best practices for one-time event pattern Let's say I have the following code for this case:
Copy code
val focusRequester = remember { FocusRequester() }
UIEventFlow.collect {
  if (it is RequestFocusEvent) {
    focusRequester.requestFocus()
  }
}
Since I would need to pass a
Flow
in my parameters, this would make my composable unstable and it will not skip recomposition. This could lead to some inconsistencies specially if I want to work with custom Modifiers, key events or even performance issues if I'm calculating something specific at every recomposition. I thought about converting this to some kind of state, but feels strange since it's really an event. Do you have any hints or recommendations for solving this case?
v

vide

10/10/2023, 9:34 AM
You could consider wrapping the flow in a stable wrapper class if you need to pass it very deep. You shouldn't calculate anything heavy in recomposition, you should generally memoize any heavy calculation with
remember
to avoid performance issues with recomposition. (Light calculations do not need to be remembered because
remember
causes its own overhead.)
thank you color 1
If you know what you are doing, you can do something like this:
Copy code
@Stable
class StableWrapper<T> (var it: T)
🙌 1
l

Lucas Villa Verde

10/10/2023, 9:39 AM
You could consider wrapping the flow in a stable wrapper class if you need to pass it very deep
Thank you and good suggestion! I thought about hoisting a lambda provider as well, something like
provideEventFlow: () -> Flow<UIEvent>
which would avoid the recomposition as well. Also after reading this article I'm not quite sure if I agree to turning those kind of UI interactions to states 🤔
v

vide

10/10/2023, 10:48 AM
I think that article should be taken with a grain of salt in the context of focus interactions, thinking about reducing events to change in "focus state" is not very simple.
Using lambdas is quite dangerous as well if you're really looking to hand-optimize performance because of the way the compose compiler optimizes lambdas. You will get a new lambda every time if you reference an unstable value in the closure of the lambda, which will then not skip recomposition because it's a different instance every time. For example
Copy code
val flow: Flow = [...]
MyComposable(flowProvider = { flow })
will cause MyComposable to not skip, because the lambda instance is recreated every time. When referencing stable values, the compiler automatically generates a remember around the lambda like this:
Copy code
// your source
val flow: StableFlow = [...]
MyComposable(flowProvider = { flow })

// compiler output:
val flow: StableFlow = [...]
MyComposable(flowProvider = remember(flow) { flow })
So if you want to use the function provider approach, you should write it like this:
Copy code
MyComposable(provideEventFlow = remember(flow) { flow })
very nice 1
🙌 1
I'm not going to recommend either way since it depends so much on the use case, but I want to bring up additional considerations you might not be aware of blob smile
👌 1
l

Lucas Villa Verde

10/10/2023, 11:01 AM
> So if you want to use the function provider approach, you should write it like this: >
Copy code
MyComposable(provideEventFlow = remember(flow) { flow })
Yup, this is exactly what I did, thanks so much for all the information and context here. > I think that article should be taken with a grain of salt in the context of focus interactions, thinking about reducing events to change in "focus state" is not very simple. I agree with that, but it's indeed a tricky one showing regular and simple situations as examples and people start to use that as a rule, which IMO is not the case for many different situations.