Skip to content

PhoneNumberInput

The original phone-number component. Its public API is unchanged; the implementation now runs on the Country Picker foundation.

Basic usage

PhoneNumberInput(
    onValueChange = { phone ->
        phone.formattedPhone  // "+254 724 154 958"
        phone.phoneNumber     // "+254724154958" (E.164; empty while unparseable)
        phone.country         // SelectedCountry(name, code, dialCode, flag)
        phone.isValid         // true / false
    },
)

State hoisting

Hoist PhoneState when you need to read state or drive the field from outside the composable, for example to clear it after submission or to validate on a button tap.

val phoneState = rememberPhoneState()

PhoneNumberInput(state = phoneState, onValueChange = { phone -> /* … */ })

Button(onClick = {
    if (phoneState.isValid) submit(phoneState.toPhone()) else phoneState.clearPhone()
}) { Text("Submit") }

Picker style: bottom sheet or dropdown

// Default: modal bottom sheet
PhoneNumberInput(
    ccpConfig = CCPDefaults.defaultConfig(countryPickerStyle = CountryPickerStyle.BottomSheet),
)

// Compact inline dropdown (tablets, landscape, dense forms)
PhoneNumberInput(
    ccpConfig = CCPDefaults.defaultConfig(countryPickerStyle = CountryPickerStyle.Dropdown),
)

In the BottomSheet style the field is a real Material outlined text field with the flag, dial code and chevron as leading content and a subtle divider before the number editor. The floating label and the divider are independently togglable with showLabel (off by default) and showPhonePrefixDivider (on by default). The Dropdown style keeps its original compact selector button with a popup anchored below it.

Error state

var isError by remember { mutableStateOf(false) }

PhoneNumberInput(
    isError = isError,
    errorMessage = "Please enter a valid phone number",
    onValueChange = { phone -> isError = !phone.isValid && phone.phoneNumber.isNotEmpty() },
)

Pre-fill and country override

PhoneNumberInput(
    value = "+254724154958",   // pre-fill in E.164; the country is taken from it
    setCountry = "KE",         // or force a country by ISO code or name ("Kenya")
)

Country filtering

PhoneNumberInput(
    countriesToShow = listOf("US", "GB", "KE", "FR", "AU"),   // allow list
    countriesExclude = listOf("KP", "IR"),                    // exclusions win
    pinnedCountries = listOf("US", "GB", "KE"),               // "Suggested" section at the top
)

Behaviour options

PhoneNumberInput(
    ccpConfig = CCPDefaults.defaultConfig(
        showClearButton = true,        // × inside the field
        autoDetectCountry = true,      // SIM → network → locale → default
        enforceMaxLength = false,      // default true: cap digits at the country's max
        defaultCountryListStyle = CountryListStyle.Grid,   // or List
        showLabel = true,              // floating label (BottomSheet style)
        phoneFieldSize = PhoneFieldSize.Compact,
    ),
    label = "Mobile number",
    onDone = { submitForm() },         // only when isValid
)

Colors, shapes and typography

PhoneNumberInput(
    colors = CCPDefaults.colors(
        containerColor = Color.White,
        borderColor = Color.Gray,
        errorBorderColor = Color.Red,
        errorColor = Color.Red,
        cursorColor = Color.Black,
        inputTextColor = Color.DarkGray,
        phoneHintColor = Color.LightGray,
    ),
    ccpConfig = CCPDefaults.defaultConfig(
        phoneInputShape = RoundedCornerShape(8.dp),
        borderWidth = 1.dp,
        showCountryFlag = true,
        showHeader = true,
        showCountriesHeaderDivider = true,
    ),
)

Reference

PhoneNumberInput parameters

Parameter Type Default Description
modifier Modifier Modifier Applied to the outer column
state PhoneState rememberPhoneState() Hoistable state holder
phoneHint String "Enter phone" Placeholder text
label String? "Phone number" Floating label; rendered only when CCPConfig.showLabel is true
onValueChange (Phone) -> Unit {} Called on every change
onPhoneValueChange (String, String, Boolean) -> Unit Deprecated. Use onValueChange
onDone () -> Unit {} Called on IME Done, only when valid
value String "" Initial number (E.164 or local)
setCountry String? null ISO code or name to preselect
countriesToShow List<String> emptyList() Allow list of ISO codes
countriesExclude List<String> emptyList() ISO codes to hide
pinnedCountries List<String> emptyList() ISO codes for the Suggested section
isError Boolean false Error border and message
errorMessage String? null Shown below the field when isError
colors CCPColors CCPDefaults.colors() Color configuration
ccpConfig CCPConfig CCPDefaults.defaultConfig() Behaviour and UI configuration

PhoneState

Member Type Description
activeCountry Country? Selected country (legacy model). Never null in practice
phoneNumber String Raw digits
phoneField TextFieldValue The field's text and cursor; derived from phoneNumber
formattedPhone String International format, e.g. +254 724 154 958
unformattedPhone String E.164, e.g. +254724154958. Empty while unparseable
isValid Boolean Valid and dialable for the country
validity PhoneNumberValidity The granular reason behind isValid; see Validation
value PhoneNumberValue The full evaluated value, including the nullable E.164 form
selectCountry(Country) Set the country and reformat; the digits are kept and re-validated
setCountryByCode(String) Set the country by ISO code
updatePhoneNumber(TextFieldValue) Update the field and reformat
parseAndSet(String) Parse a full string; adopts the country when the input identifies one
clearPhone() Reset the number, keep the country
toPhone() Phone Snapshot of the current state

Phone

data class Phone(
    val formattedPhone: String,       // "+254 724 154 958"
    val phoneNumber: String,          // E.164 "+254724154958"
    val country: SelectedCountry?,    // name, code, dialCode, flag
    val isValid: Boolean,
)

CCPConfig

Property Type Default Description
autoDetectCountry Boolean false Detect the country from SIM / network / locale
countryPickerStyle CountryPickerStyle BottomSheet BottomSheet or Dropdown
defaultCountryListStyle CountryListStyle List List or Grid
showClearButton Boolean false × button inside the input
enforceMaxLength Boolean true Cap digits at the country's maximum
readOnly Boolean false Disable the input and the selector
showCountryFlag Boolean true Flag in the selector button
showHeader Boolean true Letter headers in the country list
showCountriesHeaderDivider Boolean true Divider next to letter headers
showFlagCountryItem Boolean true Flag in each row
showDialCodeCountryItem Boolean true Dial code in each row
showLabel Boolean false Floating label notched into the outline (BottomSheet style)
showPhonePrefixDivider Boolean true Divider between the prefix and the editor (BottomSheet style)
phoneFieldSize PhoneFieldSize Regular Regular, Compact or ExtraCompact (BottomSheet style)
borderWidth Dp 0.dp Input border thickness
phoneInputShape Shape RoundedCornerShape(16.dp) Input container shape
phoneInputCornerRadius Dp 16.dp Radius used by the default phoneInputShape
countriesSheetShape Shape top corners 16.dp Bottom sheet shape
searchCornerRadius Dp 16.dp Search field corner radius
searchBorderWidth Dp 1.dp Search field border thickness
phoneHintStyle, searchHintStyle, headerStyle, countryItemNameTextStyle, countryItemDialCodeTextStyle TextStyle Material defaults Text styles
countryItemShape Shape RoundedCornerShape(16.dp) Row shape

CCPColors

Property Default Description
containerColor colorScheme.background Input background
borderColor colorScheme.outline Normal border
errorBorderColor colorScheme.error Error border
errorColor colorScheme.error Error message text
cursorColor colorScheme.primary Text cursor
inputTextColor colorScheme.onSurface Entered number
phoneHintColor colorScheme.onSurfaceVariant Placeholder
countryCodeTextColor colorScheme.onSurface Dial code in the selector
countryChevronColor colorScheme.onSurface Chevron icon
ccpSheetColor CCPDefaults.ccpSheetColor() Bottom sheet and dropdown colors (CCPSheetColor)

Standalone pickers

CountriesDropdown(expanded, onDismiss, onSelectCountry, …) and CountriesBottomSheet(sheetState, onSelectCountries, …) are public for hosts that used them directly. Both accept the same countriesToShow / countriesExclude / pinnedCountries, ccpColors and ccpConfig parameters as PhoneNumberInput.