Issue
Building a simple keyboard is fairly simple and straightforward in Jetpack Compose.
I built a really simple KeyRow by using this:
Key.kt
@Composable
fun Key(modifier: Modifier = Modifier, label: String, onClick: () -> Unit) {
val shape = RoundedCornerShape(4.dp)
//TODO: make clickable outside but don't show ripple
Box(modifier = modifier
.padding(2.dp)
.clip(shape)
.clickable(onClick = onClick)
.background(Color.White)
.padding(vertical = 12.dp, horizontal = 4.dp), contentAlignment = Alignment.Center) {
Text(text = label, fontSize = 20.sp)
}
}
KeyRow.kt
@Composable
fun KeyRow(keys: List<String>) {
Row(modifier = Modifier.fillMaxWidth().background(color = grey200)) {
keys.forEach {
Key(modifier = Modifier.weight(1f), label = it, onClick = { })
}
}
}
That’s what it looks like:
I want to achieve this animation:
However, I’m currently stuck with this
![4]
Hierachy
-Keyboard
--KeyRow
---KeyLayout
----Key
----KeyPressedOverlay (only visible when pressed)
My main problem is that I don’t know how to show the KeyPressedOverlay Composale (which is larger than the Key Composable) without making the parent Layout larger. As a result, I need to overflow the parent layout in some way.
Solution
Not sure if it’s the best way (probably not), but I found a solution using ConstraintLayout
…
val keys = listOf("A", "B", "C", "D")
ConstraintLayout(
modifier = Modifier.graphicsLayer(clip = false)
) {
val refs = keys.map { createRef() }
refs.forEachIndexed { index, ref ->
val modifier = when (index) {
0 -> Modifier.constrainAs(ref) {
start.linkTo(parent.start)
}
refs.lastIndex -> Modifier.constrainAs(ref) {
start.linkTo(refs[index - 1].end)
end.linkTo(parent.end)
}
else -> Modifier.constrainAs(ref) {
start.linkTo(refs[index - 1].end)
end.linkTo(refs[index + 1].start)
}
}
val modifierPressed = Modifier.constrainAs(createRef()) {
start.linkTo(ref.start)
end.linkTo(ref.end)
bottom.linkTo(ref.bottom)
}
KeyboardKey(
keyboardKey = keys[index],
modifier = modifier,
modifierPressed = modifierPressed,
pressed = { s -> /* Do something with the key */}
)
}
}
One important detail here is graphicLayer(clip = false)
(which is similar to the clipChildren
in View Toolkit). Then, I’m creating a modifier to each key and to the pressed key. Noticed that the modifierPressed
is aligned to the center/bottom of the other modifier.
Finally the KeyboardKey
is described below.
@Composable
fun KeyboardKey(
keyboardKey: String,
modifier: Modifier,
modifierPressed: Modifier,
pressed: (String) -> Unit
) {
var isKeyPressed by remember { mutableStateOf(false) }
Text(keyboardKey, Modifier
.then(modifier)
.pointerInput(Unit) {
detectTapGestures(onPress = {
isKeyPressed = true
val success = tryAwaitRelease()
if (success) {
isKeyPressed = false
pressed(keyboardKey)
} else {
isKeyPressed = false
}
})
}
.background(Color.White)
.padding(
start = 12.dp,
end = 12.dp,
top = 16.dp,
bottom = 16.dp
),
color = Color.Black
)
if (isKeyPressed) {
Text(
keyboardKey, Modifier
.then(modifierPressed)
.background(Color.White)
.padding(
start = 16.dp,
end = 16.dp,
top = 16.dp,
bottom = 48.dp
),
color = Color.Black
)
}
}
Edit:
Adding some more logic, I was able to get this…
I hope it helps this time 😉
Here’s the gist just in case…
https://gist.github.com/nglauber/4cb1573efba9024c008ea71f3320b4d8
Answered By – nglauber
Answer Checked By – Gilberto Lyons (FlutterFixes Admin)