Rick Regan
10/19/2021, 5:39 PMBoxWithConstraints
to compute the number of a particular UI element that can fit on the screen. Let’s say I want two elements when the width is “portrait” and three when it is “landscape”. These elements display editable state that I want retained across rotations, so that when the third element is rotated off screen and then back on, it displays its old data. (My state is “global”, so I’m not talking about rememberSaveable
.)
The composable I've written that detects the rotation puts out the appropriate number of elements and then uses a SideEffect
to tell the state what the number of elements is. This seems backwards, since it’s like the state change is reacting to the UI change. (Another detail that may be relevant: I have a button that “tabs through” the visible elements, so that button’s callback needs to check the state to know what’s on screen.)
Now I’m thinking that instead I should use a LaunchedEffect
(with key the number of elements) to update the state and then use the number of elements as recorded in the state (rather than in the local variable as above) to generate the elements. This seems more natural, almost like making configuration change a callback. In any case, I wanted to ask about best practices before I got too far off track. Thanks.Alex Vanyo
10/19/2021, 6:00 PMThis seems backwards, since it’s like the state change is reacting to the UI change.Your intuition is correct here, and I’d recommend seeing if it is possible to avoid any
SideEffect
or LaunchedEffect
at all.
At the level of BoxWithConstraints
, you can transform your width into a specific state (such as number of columns). Then, see if you can combine that state with the rest of your app’s state in such a way so that you don’t need any SideEffect
s.
(Another detail that may be relevant: I have a button that “tabs through” the visible elements, so that button’s callback needs to check the state to know what’s on screen.)This is a very interesting detail, one that I have a question about: What happens if the user is tabbed to an element that is only visible at some screen sizes, and then they rotate their device so that the element is no longer visible? Then what happens if they rotate their device back, so the element is visible again?
Rick Regan
10/19/2021, 6:35 PMAlex Vanyo
10/19/2021, 6:56 PMso that when the third element is rotated off screen and then back on, it displays its old dataThat you’re already taking reversibility into account
Alex Vanyo
10/19/2021, 6:57 PMRick Regan
10/19/2021, 7:10 PMRick Regan
10/19/2021, 7:15 PMRick Regan
10/19/2021, 7:29 PMAlex Vanyo
10/19/2021, 7:41 PMN
most recent languages, where N
is based on the size available.
One consequence of that is your underlying app state will always be able to display all of the languages that could be shown, even if there isn’t space to do so.Alex Vanyo
10/19/2021, 7:44 PMRick Regan
10/19/2021, 8:19 PMAlex Vanyo
10/19/2021, 9:03 PMN
was, and what was being displayed.Alex Vanyo
10/19/2021, 9:04 PMEven then I get a little hung up thinking about how to keep track of which elements are visible without high-level state trackingThere’s a client/server analogy here that I really like (credit to Adam Powell): Imagine that your UI is a client, and anything in a
ViewModel
layer (or in this case, everything outside of BoxWithConstraints
) is a server.
The server should be able to function independently of the client, regardless if there are 0, 1, 2, or more UI “clients” rendering the data. For a few reasons, it’d be very difficult to have the server keep track of how many columns a specific client is trying to render.
So in this fictional case, you probably wouldn’t be sending out requests to a server for different sets of data for a small screen or large screens. Instead, you’d encode all of the data necessary in a format where any UI client or clients could display at whatever screen size they have.
Analogy aside, there isn’t actually a network request in-between your “client” and “server” to keep everything in sync, but shifting to that mode of thinking where possible can help reframe the problem into a simpler solution in these cases.
Because that solution is based purely on state, it will be easier to test, more flexible, and easier to build on top of (hoisting state, animations, alternate ways to present data)Rick Regan
10/21/2021, 3:36 PMBoxWithConstraints
sizes and deriving it from that) to the elements, which will be captured in the onValueChange()
as a parameter to the "translation" callback made from the input element? But then individual composable elements are aware of this higher-level state (rather than only a hoisted callback being aware of it). I don’t know if that’s better than a side effect done at the higher level.
(Now you did say “the callback for the button that selects can know what N was, and what was being displayed” so maybe you had another mechanism in mind that I missed.)Alex Vanyo
10/21/2021, 4:16 PMurl
until the UI element that is displaying the image.
If calculating and passing down the translation is too expensive, pass down some sort of key, identifier, or other lazy mechanism abstracted in a way of your choosing that will only do the expensive computation if actually needed. If that computation is expensive, I’m guessing there’s some sort of loading state and caching mechanism too.