Делаем кастомный Switch на compose (iOS like)

Я не стал здесь описывать все возможные конфигурации компонента, т.к мне это не нужно, но вы при желании можете легко добавить и модифицировать.
@Composable
fun GradientSwitch(
checked: Boolean,
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
checkedTrackColor: Brush = Theme.colors.primaryPurpleGradientBrush,
uncheckedTrackColor: Brush = Brush.horizontalGradient(
colors = listOf(Color.LightGray,Color.Gray)
),
thumbColor: Color = Color.White
) {
val thumbPosition by animateFloatAsState(targetValue = if (checked) 1f else 0f)
val circleRadius = remember { 13.5.dp }
val interactionSource = remember { MutableInteractionSource() }
}
Здесь все достаточно стандартно, нет ничего специфичного, но на всякий случай подсвечу что checkedTrackColor - градиент для состояние флага checked = true, uncheckedTrackColor - для состояния false. Можно сделать несколько перегрузок GradientSwitch, и дать возможность использовать на градиент, а просто цвет.
Далее рисуем на квадрат(контейнер)
drawRoundRect(
brush = trackBrush,
size = Size(size.width, size.height),
cornerRadius = CornerRadius(x = 18.dp.toPx(), y = 18.dp.toPx())
)
Рисуем саму кнопку, которая будет двигаться влево-вправо.
drawCircle(
color = thumbColor,
radius = circleRadius.toPx(),
center = Offset(x = thumbOffset, y = size.height / 2)
)
Ниже расчитывается отступ для кнопки тогла, которая двигается. Fraction нужен для анимации, который в зависимости от состояния ходит от 0 до 1 и наоборот
val thumbOffset = calculateThumbOffset(
start = 16.dp.toPx(),
stop = size.width - 16.dp.toPx(),
fraction = thumbPosition
)
private fun calculateThumbOffset(
start: Float,
stop: Float,
fraction: Float
): Float = start + (stop - start) * fraction
Ниже представлен весь код компонента
@Composable
fun GradientSwitch(
checked: Boolean,
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
checkedTrackColor: Brush = Theme.colors.primaryPurpleGradientBrush,
uncheckedTrackColor: Brush = Brush.horizontalGradient(
colors = listOf(
Color.LightGray,Color.Gray
)
),
thumbColor: Color = Color.White
) {
val thumbPosition by animateFloatAsState(targetValue = if (checked) 1f else 0f)
val circleRadius = remember { 13.5.dp }
val interactionSource = remember { MutableInteractionSource() }
Box(
modifier = modifier
.size(width = 51.dp, height = 31.dp)
.background(color = Color.Transparent)
.clickable(
onClick = { onCheckedChange(!checked) },
interactionSource = interactionSource,
indication = null
)
) {
Canvas(modifier = Modifier.matchParentSize()) {
val trackBrush = if (checked) checkedTrackColor else uncheckedTrackColor
drawRoundRect(
brush = trackBrush,
size = Size(size.width, size.height),
cornerRadius = CornerRadius(x = 18.dp.toPx(), y = 18.dp.toPx())
)
val thumbOffset = calculateThumbOffset(
start = 16.dp.toPx(),
stop = size.width - 16.dp.toPx(),
fraction = thumbPosition
)
drawCircle(
color = thumbColor,
radius = circleRadius.toPx(),
center = Offset(x = thumbOffset, y = size.height / 2)
)
}
}
}

Спасибо за внимание!