Creating a Jetpack Compose Color Picker Easily

A common functionality in a mobile app is allowing users to choose a color. Sometimes, you need a composable according to these needs

  • A field where to place the description of the colors
  • A dialog to choose the color

For the dialog, we are going to use the library in this github repo:
https://github.com/maxkeppeler/sheets-compose-dialogs/tree/main

First, lets add the dependencies of the library

implementation("com.maxkeppeler.sheets-compose-dialogs:core:1.2.0")
implementation("com.maxkeppeler.sheets-compose-dialogs:color:1.2.0")

In the github repo there are many examples for color dialogs, so lets try the next one

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ColorPicker() {
    val color = remember { mutableStateOf(Color.Red.toArgb()) }
    ColorDialog(
        state = rememberUseCaseState(visible = true, onCloseRequest = { }),
        selection = ColorSelection(
            selectedColor = SingleColor(color.value),
            onSelectColor = { color.value = it },
        ),
        config = ColorConfig(
            defaultDisplayMode = ColorSelectionMode.CUSTOM,
            allowCustomColorAlphaValues = false
        ),
    )
}

This is how it looks like

Next, we add a textfield for the description

 OutlinedTextField(
        value = "#${color.value.toHexString().uppercase()}",
        onValueChange = {}
  )

And we add the events to open the dialog. We want that the dialog opens when the TextField is clicked. We add a state variable for this

val open = remember { mutableStateOf(false) }

And we implement the click event

OutlinedTextField(
        modifier = Modifier.clickable { open.value = true },
        value = "#${color.value.toHexString().uppercase()}",
        onValueChange = {}
    )

You will notice that the click event does not work. You need to add the enable=false attribute and fix the colors.

OutlinedTextField(
        enabled = false, // Add this to make click event work
        modifier = Modifier.clickable { open.value = true },
        value = "#${color.value.toHexString().uppercase()}",
        onValueChange = {},
        colors = OutlinedTextFieldDefaults.colors( // Fixing the colors
            disabledTextColor = MaterialTheme.colorScheme.onSurface,
            disabledBorderColor = MaterialTheme.colorScheme.outline,
            disabledLeadingIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
            disabledTrailingIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
            disabledLabelColor = MaterialTheme.colorScheme.onSurfaceVariant,
            disabledPlaceholderColor = MaterialTheme.colorScheme.onSurfaceVariant,
        )
    )

We show the dialog only when open.value is true, and we set open.value=false when the dialog is closed

if (open.value) {
        ColorDialog(
            state = rememberUseCaseState(visible = true, onCloseRequest = { open.value = false }),

            selection = ColorSelection(
                selectedColor = SingleColor(color.value),
                onSelectColor = { color.value = it },
            ),
            config = ColorConfig(
                defaultDisplayMode = ColorSelectionMode.CUSTOM,
                allowCustomColorAlphaValues = false
            ),
        ) 
    }

Finally we add the value and onChange parameters

fun ColorPicker(
    value: Color,
    onChangeValue: (Color) -> Unit
)

And we can use it like this:

@Composable
@Preview
fun ColorPickerPreview() {
    val color = remember {mutableStateOf(Color.Red)}
    AppTheme {
        ColorPicker(
            value = color.value,
            onChangeValue = { color.value = it}
        )
    }
}

This is the whole code


import androidx.compose.foundation.clickable
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.tooling.preview.Preview
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
import com.maxkeppeler.sheets.color.ColorDialog
import com.maxkeppeler.sheets.color.models.ColorConfig
import com.maxkeppeler.sheets.color.models.ColorSelection
import com.maxkeppeler.sheets.color.models.ColorSelectionMode
import com.maxkeppeler.sheets.color.models.SingleColor
import com.thisisthetime.controlinversiones.ui.theme.AppTheme
import okhttp3.internal.toHexString


@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ColorPicker(
    value: Color,
    onChangeValue: (Color) -> Unit
) {
    val open = remember { mutableStateOf(false) }

    OutlinedTextField(
        enabled = false, // Add this to make click event work
        modifier = Modifier.clickable { open.value = true },
        value = "#${value.toArgb().toHexString().uppercase()}",
        onValueChange = {},
        colors = OutlinedTextFieldDefaults.colors( // Fixing the colors
            disabledTextColor = MaterialTheme.colorScheme.onSurface,
            disabledBorderColor = MaterialTheme.colorScheme.outline,
            disabledLeadingIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
            disabledTrailingIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
            disabledLabelColor = MaterialTheme.colorScheme.onSurfaceVariant,
            disabledPlaceholderColor = MaterialTheme.colorScheme.onSurfaceVariant,
        )
    )

    if (open.value) {
        ColorDialog(
            state = rememberUseCaseState(visible = true, onCloseRequest = { open.value = false }),

            selection = ColorSelection(
                selectedColor = SingleColor(value.toArgb()),
                onSelectColor = { onChangeValue(Color(it))},
            ),
            config = ColorConfig(
                defaultDisplayMode = ColorSelectionMode.CUSTOM,
                allowCustomColorAlphaValues = false
            ),
        )
    }

}

@Composable
@Preview
fun ColorPickerPreview() {
    val color = remember {mutableStateOf(Color.Red)}
    AppTheme {
        ColorPicker(
            value = color.value,
            onChangeValue = { color.value = it}
        )
    }
}


Leave a Reply

Your email address will not be published. Required fields are marked *