Как стать автором
Обновить

Делаем кастомный 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)
      )        
    }    
  }
}

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

Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.