https://kotlinlang.org logo
#compose
Title
# compose
o

Orhan Tozan

03/31/2022, 10:20 AM
I want to emit 6 times the same composable (Card), but in the middle, I want a Divider. So I want to get something like: C C C D C C C How would one do this?
f

Filip Wiesner

03/31/2022, 10:23 AM
Copy code
repeat(3) { C() }
D()
repeat(3) { C() }

//or maybe

repeat(7) {
  if (it == 3) D() else C()
}
o

Orhan Tozan

03/31/2022, 10:25 AM
I have the first one already, but I think it could be cleaner so I don't have to duplicate the logic I'm writing in the two repeat blocks. I was thinking about doing something like: repeat(6) { C }.add(3, D)
But that obviously doesn't work in Compose
f

Filip Wiesner

03/31/2022, 10:27 AM
You can make something like that work. You have the full power of Kotlin, the possibilities are endless 😄
o

Orhan Tozan

03/31/2022, 10:29 AM
@Filip Wiesner I was thinking about that, but it's not that easy. I think the problem here is, that Compose doesn't return anything, but emits things. I think it would be easy to do something like this in a return-based system like ReactJS, but in Compose, every time you call a Composable, it runs. It's like a side effect
f

Filip Wiesner

03/31/2022, 10:30 AM
Couldn't you return a @Composable function? 🤔
o

Orhan Tozan

03/31/2022, 10:31 AM
Hmm gonna try
f

Filip Wiesner

03/31/2022, 10:32 AM
Something like movableContentOf()
o

Orhan Tozan

03/31/2022, 10:37 AM
Copy code
buildList {
            repeat(6) { index ->
                add(
                    @Composable {
                        C(...)
                    }
                )
            }
        }
This gives me the following error: Internal Error occurred while analyzing this expression
c

Csaba Szugyiczki

03/31/2022, 10:49 AM
Do not oversimplify things, they just get messy after a certain point
👍 1
I think there is nothing wrong with the 1st suggestion
but if you want you can have a
Copy code
@Composable
fun CardGroup(count: Int) {
    repeat(count) { Card() }
}
and then write
Copy code
CardGroup(count = 3)
Divider()
CardGroup(count = 3)
o

Orhan Tozan

03/31/2022, 10:53 AM
@Csaba Szugyiczki
Copy code
Row(
        verticalAlignment = Alignment.CenterVertically,
        modifier = modifier.fillMaxWidth(),
    ) {

        repeat(3) { index ->

            Cell(
                value = value[index],
                onValueChange = { newNumber ->
                    val newValue = value.toMutableList().apply { set(index, newNumber) }
                    val newNumberWasEntered = value != newValue && newNumber != null

                    onValueChange(newValue)

                    if (newNumberWasEntered) {
                        focusManager.moveFocus(FocusDirection.Right)
                    }
                },
                imeAction = ImeAction.Next,
                onKeyboardAction = {
                    focusManager.moveFocus(FocusDirection.Right)
                },
                modifier = Modifier
                    .weight(1f)
                    .padding(end = 10.dp)
                    .onFocusChanged { focusState ->
                        if (focusState.isFocused) {
                            val newValue = value
                                .toMutableList()
                                .apply { set(index, null) }
                                .toList()
                            onValueChange(newValue)
                        }
                    },
            )
        }

        Divider(
            color = MaterialTheme.colors.primary,
            thickness = 2.dp,
            modifier = Modifier
                .width(10.dp)
        )

        repeat(3) { loopIndex ->

            val digitIndex = loopIndex + 3

            val isLastCell = digitIndex == value.lastIndex

            Cell(
                value = value[digitIndex],
                onValueChange = { newNumber ->
                    val newValue = value
                        .toMutableList()
                        .apply { set(digitIndex, newNumber) }
                        .toList()
                    val newNumberWasEntered = value != newValue && newNumber != null
                    onValueChange(newValue)
                    if (newNumberWasEntered) {
                        focusManager.moveFocus(FocusDirection.Right)
                    }
                },
                imeAction =
                if (isLastCell) {
                    ImeAction.Done
                } else {
                    ImeAction.Next
                },
                onKeyboardAction = {
                    if (isLastCell) {
                        focusManager.clearFocus()
                    } else {
                        focusManager.moveFocus(FocusDirection.Right)
                    }
                },
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(start = 10.dp)
                    .weight(1f)
                    .onFocusChanged { focusState ->
                        if (focusState.isFocused) {
                            val newValue = value
                                .toMutableList()
                                .apply { set(digitIndex, null) }
                                .toList()
                            onValueChange(newValue)
                        }
                    },
            )
        }
    }
c

Csaba Szugyiczki

03/31/2022, 10:53 AM
What you mean by composable lambda is I think referred to as Slot in Compose and looks like this:
content: @Composable () -> Unit
This concept is all over compose
o

Orhan Tozan

03/31/2022, 10:53 AM
Above is my real code
c

Csaba Szugyiczki

03/31/2022, 10:55 AM
Ohh, I think I know what you want to create. Is it a 6 digit input box? I think it is much easier to have only one invisible Input box, and just display the entered digits however you want
o

Orhan Tozan

03/31/2022, 10:57 AM
@Csaba Szugyiczki In react, you can do something like
Copy code
const items = Array(6) { C() }
items.splice(3, 0, D())
// items is now C C C D C C C
How would you do this in Compose?
c

Csaba Szugyiczki

03/31/2022, 10:58 AM
That is not possible in Compose Im affraid
o

Orhan Tozan

03/31/2022, 10:58 AM
@Csaba Szugyiczki yes it is a 6 digit input box. Could you elaborate on the one invisible input box part?
@Csaba Szugyiczki that's unfortunate, and a bit dissappointing IMO. It reduces the power of Compose if that's the case in my eyes. I'm open to be proven wrong
c

Csaba Szugyiczki

03/31/2022, 10:59 AM
Basically you would have an input field covering your whole view. But make it transparent. And below it you could draw the 6 boxes in a Row, and have whatever decoration on the boxes, or between them. You just have to make sure the user knows which digit is typed at the moment
Composable functions return nothing. They are plain kotlin functions without a return value, so there is nothing that you could add to a list either
o

Orhan Tozan

03/31/2022, 11:01 AM
Not sure if it is easier to build something like this into one view
(Just the 6 boxes part)
Yes, they return nothing indeed. Which can be an advantage in some cases: like being able to write:
Copy code
if (showButton) {
    Button()
}
instead of
Copy code
if (showButton) {
    Button()
} else {
    null
}
but it also has it's flaws, as you now lose the power of the regular programming power of transforming and mapping values. Example is that we can't achieve my React example few messages above
It loses the purity of functional programming
f

Filip Wiesner

03/31/2022, 11:08 AM
The Composable functions are intended for "Data -> UI" transformation. You can still "transform and map" the data before you emit UI based on it.
o

Orhan Tozan

03/31/2022, 11:16 AM
Hmm I guess so, but that means I now have to create a new data model for each UI component, which is more work
a

Albert Chang

03/31/2022, 11:33 AM
Something like
Copy code
val card = @Composable { Card() }
repeat(3) { card() }
D()
repeat(3) { card() }
will work.
☝️ 1
👍 1
o

Orhan Tozan

03/31/2022, 12:24 PM
Yep that's what I've sticked with
4 Views