Jetpack Compose Custom Switch Button with Icon

jetpack compose custom switch button

This is a custom switch button made with Android Jetpack Compose. It contains an icon on the thumb. In the ON state, it shows the Done icon and the OFF state contains the Close icon. You can download the source code for free below.

The switch button is made with several APIs like Box, Icon, Text, etc… It shows the state of the switch using the Text composable. I have added animation to the thumb. When you tap on it, the thumb moves left to right (or right to left) with a nice animation. It also changes the icon on the thumb according to the state.

The Jetpack Compose custom switch button composable takes multiple parameters. The width and height are for the Box size. The checked and unchecked colors represent the ON and OFF states. I set the thumb size to 24 dp, but you can play around with it. Change the values according to your requirements.

If you want to use this composable, you should download the latest version of Android Studio. Jetpack Compose doesn’t work in the older versions.

Final Output:

If the video isn’t working, watch it on the YouTube.

Helpful Links to Understand the Code:

Resources Used in the Project:

Here are the Gradle files used in the project.

Jetpack Compose Custom Switch Button Source Code:


import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Done
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.BiasAlignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

You can use the following code for commercial purposes with some restrictions.
Read the full license here:
For more designs with source code, visit:
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        setContent {
            YourProjectNameTheme(darkTheme = false) {
                    modifier = Modifier
                        .background(color = Color.White),
                    verticalArrangement = Arrangement.Center,
                    horizontalAlignment = Alignment.CenterHorizontally
                ) {

fun CustomSwitch(
    width: Dp = 72.dp,
    height: Dp = 40.dp,
    checkedTrackColor: Color = Color(0xFF35898F),
    uncheckedTrackColor: Color = Color(0xFFe0e0e0),
    gapBetweenThumbAndTrackEdge: Dp = 8.dp,
    borderWidth: Dp = 4.dp,
    cornerSize: Int = 50,
    iconInnerPadding: Dp = 4.dp,
    thumbSize: Dp = 24.dp
) {

    // this is to disable the ripple effect
    val interactionSource = remember {

    // state of the switch
    var switchOn by remember {

    // for moving the thumb
    val alignment by animateAlignmentAsState(if (switchOn) 1f else -1f)

    // outer rectangle with border
        modifier = Modifier
            .size(width = width, height = height)
                width = borderWidth,
                color = if (switchOn) checkedTrackColor else uncheckedTrackColor,
                shape = RoundedCornerShape(percent = cornerSize)
                indication = null,
                interactionSource = interactionSource
            ) {
                switchOn = !switchOn
        contentAlignment = Alignment.Center
    ) {

        // this is to add padding at the each horizontal side
            modifier = Modifier
                    start = gapBetweenThumbAndTrackEdge,
                    end = gapBetweenThumbAndTrackEdge
            contentAlignment = alignment
        ) {

            // thumb with icon
                imageVector = if (switchOn) Icons.Filled.Done else Icons.Filled.Close,
                contentDescription = if (switchOn) "Enabled" else "Disabled",
                modifier = Modifier
                    .size(size = thumbSize)
                        color = if (switchOn) checkedTrackColor else uncheckedTrackColor,
                        shape = CircleShape
                    .padding(all = iconInnerPadding),
                tint = Color.White

    // gap between switch and the text
    Spacer(modifier = Modifier.height(height = 16.dp))

    Text(text = if (switchOn) "ON" else "OFF")

private fun animateAlignmentAsState(
    targetBiasValue: Float
): State<BiasAlignment> {
    val bias by animateFloatAsState(targetBiasValue)
    return derivedStateOf { BiasAlignment(horizontalBias = bias, verticalBias = 0f) }


Leave a Comment