Skip to content

Instantly share code, notes, and snippets.

@owenlejeune
Created February 11, 2022 17:25
Show Gist options
  • Save owenlejeune/325ad1dada9a5ca5b094d342d8ff7fc6 to your computer and use it in GitHub Desktop.
Save owenlejeune/325ad1dada9a5ca5b094d342d8ff7fc6 to your computer and use it in GitHub Desktop.
A top-level switch based on Material 3 design written in Jetpack Compose
@Composable
fun TopLevelSwitch(
text: String,
checkedState: MutableState<Boolean> = remember { mutableStateOf(false) },
onCheckChanged: (Boolean) -> Unit
) {
Card(
modifier = Modifier
.fillMaxWidth()
.height(100.dp)
.padding(12.dp),
shape = RoundedCornerShape(30.dp),
backgroundColor = when {
isSystemInDarkTheme() && checkedState.value -> MaterialTheme.colorScheme.primary
isSystemInDarkTheme() && !checkedState.value -> MaterialTheme.colorScheme.secondary
checkedState.value -> MaterialTheme.colorScheme.primaryContainer
else -> MaterialTheme.colorScheme.secondaryContainer
}
) {
Row(
modifier = Modifier.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = text,
color = when {
isSystemInDarkTheme() && checkedState.value -> MaterialTheme.colorScheme.onPrimary
isSystemInDarkTheme() && !checkedState.value -> MaterialTheme.colorScheme.onSecondary
checkedState.value -> MaterialTheme.colorScheme.onPrimaryContainer
else -> MaterialTheme.colorScheme.onSecondaryContainer
},
modifier = Modifier.padding(30.dp, 12.dp),
fontSize = 18.sp
)
CustomSwitch(
modifier = Modifier.padding(40.dp, 12.dp),
onCheckChanged = { isChecked ->
checkedState.value = isChecked
onCheckChanged(isChecked)
}
)
}
}
}
@Composable
private fun CustomSwitch(
modifier: Modifier = Modifier,
switchState: MutableState<Boolean> = remember { mutableStateOf(false) },
onCheckChanged: (Boolean) -> Unit = {}
) {
val width = 30.dp
val height = 15.dp
val gapBetweenThumbAndTrackEdge = 2.dp
val thumbRadius = (height / 2) - gapBetweenThumbAndTrackEdge
val animatePosition = animateFloatAsState(
targetValue = if (switchState.value) {
with(LocalDensity.current) { (width - thumbRadius - gapBetweenThumbAndTrackEdge).toPx() }
} else {
with (LocalDensity.current) { (thumbRadius + gapBetweenThumbAndTrackEdge).toPx() }
}
)
val uncheckedTrackColor = if (isSystemInDarkTheme()) MaterialTheme.colorScheme.surfaceVariant else MaterialTheme.colorScheme.outline
val uncheckedThumbColor = if (isSystemInDarkTheme()) MaterialTheme.colorScheme.outline else MaterialTheme.colorScheme.surfaceVariant
val checkedTrackColor = if (isSystemInDarkTheme()) MaterialTheme.colorScheme.outline else MaterialTheme.colorScheme.primary
val checkedThumbColor = if (isSystemInDarkTheme()) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.primaryContainer
Canvas(
modifier = modifier
.size(width = width, height = height)
.scale(scale = 2f)
.pointerInput(Unit) {
detectTapGestures(
onTap = {
switchState.value = !switchState.value
onCheckChanged(switchState.value)
}
)
}
) {
drawRoundRect(
color = if (switchState.value) checkedTrackColor else uncheckedTrackColor,
cornerRadius = CornerRadius(x = 10.dp.toPx(), y = 10.dp.toPx())
)
drawCircle(
color = if (switchState.value) checkedThumbColor else uncheckedThumbColor,
radius = thumbRadius.toPx(),
center = Offset(
x = animatePosition.value,
y = size.height / 2
)
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment