Skip to content

Instantly share code, notes, and snippets.

@vengateshm
Created May 11, 2024 14:20
Show Gist options
  • Save vengateshm/fb49800e0f06bfdcb5cf41c8938e611c to your computer and use it in GitHub Desktop.
Save vengateshm/fb49800e0f06bfdcb5cf41c8938e611c to your computer and use it in GitHub Desktop.
This snippet is used to create custom tabs
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.boundsInRoot
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp

@Composable
fun CustomTabLayoutSample(modifier: Modifier = Modifier) {
    val pages = listOf("Personal Details", "Technical Details", "Other Details")
    var screen by remember {
        mutableIntStateOf(0)
    }
    CustomTabLayout(
        pages = pages,
        screen = screen,
        onClick = {
            screen = it
        }
    )
}

@Composable
fun CustomTabLayout(
    modifier: Modifier = Modifier, pages: List<String>, screen: Int,
    onClick: (Int) -> Unit
) {

    val color = MaterialTheme.colorScheme.onBackground

    var startOffset by remember {
        mutableStateOf(Pair(0f, 0f))
    }
    var endOffset by remember {
        mutableStateOf(Pair(0f, 0f))
    }

    Canvas(modifier = Modifier.fillMaxWidth()) {
        drawLine(
            color = color,
            strokeWidth = 4f,
            end = Offset(x = endOffset.first, y = endOffset.second),
            start = Offset(x = startOffset.first, y = startOffset.second),
        )
    }

    Row(
        modifier = Modifier
            .fillMaxWidth(),
        horizontalArrangement = Arrangement.SpaceEvenly
    ) {
        pages.forEachIndexed { index, page ->
            Column(
                modifier = Modifier.clickable {
                    onClick(index)
                },
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                HeaderBox(
                    title = page,
                    active = screen == index,
                    visible = screen >= index
                )
                Spacer(modifier = Modifier.height(8.dp))
                Box(
                    modifier = Modifier
                        .size(12.dp)
                        .onGloballyPositioned {
                            when (index) {
                                0 -> {
                                    startOffset = it.getCenter()
                                }

                                pages.lastIndex -> {
                                    endOffset = it.getCenter()
                                }
                            }
                        }
                ) {
                    Canvas(
                        modifier = Modifier
                            .fillMaxSize()
                    ) {
                        drawCircle(
                            color = color,
                            radius = if (screen == index) 12f else 8f
                        )
                    }
                }
            }
        }
    }
}

@Composable
private fun HeaderBox(title: String, active: Boolean, visible: Boolean) {
    Box(
        modifier = Modifier.height(24.dp)
    ) {
        AnimatedVisibility(visible = visible) {
            Text(
                text = title,
                fontWeight = if (active) FontWeight.Bold else FontWeight.Normal,
                color = MaterialTheme.colorScheme.onBackground
            )
        }
    }
}


fun LayoutCoordinates.getCenter(): Pair<Float, Float> {
    val bounds = this.boundsInRoot()
    val radius = (bounds.right - bounds.left) / 2
    val yCenter = bounds.top + radius
    val xCenter = bounds.left + radius
    println("xCenter:$xCenter")
    println("yCenter:$yCenter")
    return Pair(xCenter, yCenter)
}

@Preview(showBackground = true, showSystemUi = true)
@Composable
private fun CustomTabLayoutPreview() {
    MaterialTheme {
        CustomTabLayoutSample()
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment