I wrote a little abstraction to render some conten...
# compose
b
I wrote a little abstraction to render some content (a series of Text composables) either horizontally (Row) or Vertically (Column). Does Compose offer a better (built-in) way to do this?
Copy code
enum class Orientation {
    HORIZONTAL,
    VERTICAL
}

@Composable
fun OrientedContent(
        orientation: Orientation, 
        modifier: Modifier = Modifier, 
        content: @Composable () -> Unit) = when (orientation) {
    Orientation.HORIZONTAL -> Row(modifier = modifier) { content() }
    Orientation.VERTICAL -> Column(modifier = modifier) { content () }
}
d
It doesn't (yet?). The parameters are type safe, so it would require some interesting generics to get right.
a
The better way is to just write
Row
when you want horizontal and
Column
when you want vertical 😛
b
@Adam Powell don't be bringing logic and reason into the conversation ... I'm not interested in those things! 😉
a
this code doesn't maintain content identity if the orientation changes. Here's what I mean:
Copy code
var orientation by remember { mutableStateOf(Orientation.Horizontal) }
OrientedContent(orientation) {
  var counter by remember { mutableStateOf(0) }
  Button(
    onClick = {
      counter++
      orientation = if (orientation == Orientation.Horizontal) Orientation.Vertical else Orientation.Horizontal
    }
  ) {
    Text("Toggle!")
  }
  Text("Clicked $counter times")
}
🤯 2
try it 🙂
d
Ha, the results I see in my head are.... shocking. I guess it would be asking too much from compose to make this "Just work" or perhaps it's just the components fault.
b
yes, "just write
Row
or
Column
" is obvious now. What led me down the
OrientedContent
path was a refactor. I originally had a Composable (
PostMetaData
) that was showing a
Row
of several
Text
composables. In a new spot, I needed to show the exact same things, just in a column instead of a Row, and so I came up with the above solution, instead of just extracting the
Row
from
PostMetaData
entirely and wrapping the call(s) to it in the appropriate Row/Column. It allowed me to leave my original calls to
PostMetaData
untouched, while allowing the new call to achieve verticality.
d
Hmm, I just ran it and skija crashed.
a
oops 😄
probably report that one
on android counter is always 0, because each of the two calls to
content
in the different
when
branches has different identity
b
yeah, that's what I got when i emulated it in my brain.
Never would've thought of that on my own thuogh
d
a
the nature of this is why we're currently working on a feature of composition as a value, where you can move a part of a composition and preserve this identity. It's important for being able to do things like shared element transitions along with some other fun stuff
👍 2
anyway, key takeaway: composable lambda parameters are factories for content, and each call site of that lambda creates an instance of it
b
or in layman's terms ... if you write a composable that has a composable content lambda parameter, most of the time you probably want to avoid calling the content lambda in multiple places ...
a
unless you explicitly want multiple instances of that content, yes. (Think things like LazyColumn where the lambdas can represent the same presentation of different data.)
b
right
hence "most of the time"
a
yep