How to bring a widget front when it is focused(or selected ) in jetpack compose

Viewed 713

I have a page like this:

enter image description here

When one box is focused, it will be scaled. I use Modifier.graphicsLayer() to scale it.

but the scaled box will be covered by other boxes(box01 is covered by box02,box04 and box 05)

enter image description here

what I actually need is: the scaled box covers other boxes,like this:

enter image description here

My Sample Code:

@Composable
fun FocusBox(
    title:String,
    requester: FocusRequester = FocusRequester(),
    modifier: Modifier = Modifier
) {
    var boxColor by remember { mutableStateOf(Color.White) }
    var scale by remember { mutableStateOf(1f) }
    Box(
        Modifier
            .focusRequester(requester)
            .onFocusChanged {
                boxColor = if (it.isFocused) Color.Green else Color.Gray
                scale = if (it.isFocused) { 1.3f } else { 1f }
            }
            .focusable()
            .graphicsLayer(
                scaleX = scale,
                scaleY = scale
            ).background(boxColor)
    ) {
        Text(
            text = title,
            modifier = Modifier.padding(30.dp),
            color = Color.White,
            style = MaterialTheme.typography.subtitle2
        )
    }
}

@Composable
fun FocusScaleBoxDemo(){
    Row(modifier = Modifier.padding(30.dp)){
        Column{
            FocusBox(title = "Box_01")
            Spacer(modifier = Modifier.padding(5.dp))
            FocusBox(title = "Box_02")
            Spacer(modifier = Modifier.padding(5.dp))
            FocusBox(title = "Box_03")
            Spacer(modifier = Modifier.padding(5.dp))
        }
        Spacer(modifier = Modifier.padding(5.dp))
        Column{
            FocusBox(title = "Box_04")
            Spacer(modifier = Modifier.padding(5.dp))
            FocusBox(title = "Box_05")
            Spacer(modifier = Modifier.padding(5.dp))
            FocusBox(title = "Box_06")
            Spacer(modifier = Modifier.padding(5.dp))
        }
    }
}
1 Answers

Basically you need zIndex to bring view under neighbours. But this modifier only works for one container. So if you only add it to the selected box, neighbour column will still be on top of that. You need to add it to the Column containing selected box too.

I also prettified you code a little bit: try to avoid code repetition as much as possible - you'll decrease mistake chances and increase modifications speed

@Composable
fun FocusScaleBoxDemo() {
    val columnsCount = 2
    val rowsCount = 3
    var focusedColumnIndex by remember { mutableStateOf(0) }
    Row(
        horizontalArrangement = Arrangement.spacedBy(10.dp),
        modifier = Modifier.padding(30.dp)
    ) {
        for (column in 0 until columnsCount) {
            Column(
                verticalArrangement = Arrangement.spacedBy(10.dp),
                modifier = Modifier
                    .zIndex(if (column == focusedColumnIndex) 1f else 0f)
            ) {
                for (row in 0 until rowsCount) {
                    val boxIndex = column * rowsCount + row
                    FocusBox(
                        title = "Box_${boxIndex + 1}",
                        onFocused = {
                            focusedColumnIndex = column
                        },
                    )
                }
            }
        }
    }
}

@Composable
fun FocusBox(
    title: String,
    onFocused: () -> Unit,
    requester: FocusRequester = remember { FocusRequester() },
) {
    var isFocused by remember { mutableStateOf(false) }
    val scale = if (isFocused) 1.3f else 1f
    Box(
        Modifier
            .focusRequester(requester)
            .onFocusChanged {
                isFocused = it.isFocused
                if (isFocused) {
                    onFocused()
                }
            }
            .focusable()
            .graphicsLayer(
                scaleX = scale,
                scaleY = scale
            )
            .background(if (isFocused) Color.Green else Color.Gray)
            .zIndex(if (isFocused) 1f else 0f)
    ) {
        Text(
            text = title,
            modifier = Modifier.padding(30.dp),
            color = Color.White,
            style = MaterialTheme.typography.subtitle2
        )
    }
}

Related