Usage
Country Code Picker with TextField
The primary usage pattern provides a full OutlinedTextField with an integrated country code picker as the leading icon.
var phoneNumber by rememberSaveable { mutableStateOf("") }
val state = rememberKomposeCountryCodePickerState(
showCountryCode = true,
showCountryFlag = true,
)
KomposeCountryCodePicker(
modifier = Modifier.fillMaxWidth(),
text = phoneNumber,
onValueChange = { phoneNumber = it },
placeholder = {
Text(
text = "Phone Number",
style = MaterialTheme.typography.labelMedium.copy(
fontWeight = FontWeight.ExtraLight,
),
)
},
shape = MaterialTheme.shapes.medium,
colors = TextFieldDefaults.colors(
unfocusedContainerColor = Color.Transparent,
focusedContainerColor = Color.Transparent,
),
state = state,
)
The state object holds all the phone number data. Use text to provide the current input and onValueChange to update it — the same controlled-text pattern used by standard Compose text fields.
Independent Text Styles
By default, textStyle applies to both the phone number input and the country code. To style them independently, use countryCodeTextStyle:
KomposeCountryCodePicker(
modifier = Modifier.fillMaxWidth(),
text = phoneNumber,
onValueChange = { phoneNumber = it },
state = state,
textStyle = MaterialTheme.typography.bodyLarge, // phone number input
countryCodeTextStyle = MaterialTheme.typography.labelMedium.copy(
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.primary,
), // country code only
)
Country Code Picker Only
Set showOnlyCountryCodePicker = true to display just the country selector without a text field. This is useful when embedding the picker into a custom layout.
val state = rememberKomposeCountryCodePickerState()
KomposeCountryCodePicker(
modifier = Modifier,
showOnlyCountryCodePicker = true,
text = phoneNumber,
state = state,
)
Integration with Custom TextField
You can use the picker-only mode as a leadingIcon inside your own TextField:
var phoneNumber by rememberSaveable { mutableStateOf("") }
val state = rememberKomposeCountryCodePickerState()
TextField(
modifier = Modifier.fillMaxWidth(),
value = phoneNumber,
onValueChange = { phoneNumber = it },
placeholder = { Text(text = "Phone Number") },
leadingIcon = {
KomposeCountryCodePicker(
modifier = Modifier,
showOnlyCountryCodePicker = true,
selectedCountryPadding = 0.dp, // adjust or remove default padding
text = phoneNumber,
state = state,
)
},
colors = TextFieldDefaults.colors(
unfocusedContainerColor = Color.Transparent,
focusedContainerColor = Color.Transparent,
),
)
Tip
Use selectedCountryPadding to control the padding around the country selector. The default is 8.dp on all sides. Set it to 0.dp when embedding inside a text field's decorator box to avoid extra spacing.
Fully Custom Phone Input
If your app has its own design system with custom text field components, you likely don't want to use KomposeCountryCodePicker at all — it comes with its own OutlinedTextField baked in. Instead, you want to use your own text field and only rely on the library for the country data (flags, phone codes) and the searchable country selection dialog.
The state.selectedCountry property makes this possible. It exposes the full Country object — including the flag drawable — so you can render the selected country anywhere in your own layout.
Here is a complete, production-ready example:
@OptIn(RestrictedApi::class)
@Composable
fun PhoneNumberField() {
var phoneNumber by rememberSaveable { mutableStateOf("") }
var showCountryPicker by rememberSaveable { mutableStateOf(false) }
val state = rememberKomposeCountryCodePickerState()
// 1. Show the country selection dialog when the user taps the country section
if (showCountryPicker) {
CountrySelectionDialog(
countryList = state.countryList,
containerColor = MaterialTheme.colorScheme.background,
contentColor = MaterialTheme.colorScheme.onBackground,
onDismissRequest = { showCountryPicker = false },
onSelect = { country ->
state.setCode(country.code)
showCountryPicker = false
},
)
}
// 2. Build your own text field — any TextField, OutlinedTextField, or BasicTextField
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = phoneNumber,
onValueChange = { phoneNumber = it },
placeholder = { Text("Phone Number") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone),
leadingIcon = {
// 3. Use state.selectedCountry to display the flag and phone code
Row(
modifier = Modifier
.clickable { showCountryPicker = true }
.padding(start = 12.dp, end = 8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Image(
modifier = Modifier.width(28.dp).height(18.dp),
painter = painterResource(state.selectedCountry.flag),
contentDescription = state.selectedCountry.name,
)
Spacer(modifier = Modifier.width(6.dp))
Text(text = state.selectedCountry.phoneNoCode)
}
},
)
// 4. Access phone data from the state as usual
val fullNumber = "${state.getCountryPhoneCode()}$phoneNumber"
}
What you get from state.selectedCountry:
| Property | Type | Example |
|---|---|---|
state.selectedCountry.flag |
DrawableResource |
The country's flag drawable |
state.selectedCountry.name |
String |
"Kenya" |
state.selectedCountry.phoneNoCode |
String |
"+254" |
state.selectedCountry.code |
String |
"ke" |
Note
state.setCode() is annotated with @RestrictedApi because it's intended for advanced use cases where you manage the state yourself. Add @OptIn(RestrictedApi::class) to your function and import com.joelkanyi.jcomposecountrycodepicker.annotation.RestrictedApi.
Check the Custom tab in the sample app for more examples, including a BasicTextField variant with a fully custom layout.
Reading Phone Number Data
Use the state object to access all phone number information:
| Method / Property | Return Value | Example |
|---|---|---|
state.getCountryPhoneCode() |
Country phone code with prefix | +254 |
state.getCountryPhoneCodeWithoutPrefix() |
Country phone code without prefix | 254 |
state.getCountryName() |
Country name | Kenya |
state.countryCode |
ISO country code | ke |
state.phoneNumber |
Phone number as entered | 0712345678 |
state.getPhoneNumberWithoutPrefix() |
Phone number without leading zero | 712345678 |
state.getFullPhoneNumber() |
Full number with + prefix |
+254712345678 |
state.getFullPhoneNumberWithoutPrefix() |
Full number without + prefix |
254712345678 |
state.getFullyFormattedPhoneNumber() |
Formatted full number | +254 712 345678 |
state.isPhoneNumberValid() |
Whether the number is valid | true / false |
state.selectedCountry |
The currently selected Country object |
Country(code=ke, ...) |
state.selectedCountry.flag |
Flag drawable resource of the selected country | DrawableResource |
Validation Example
Drive the error state of the text field using isPhoneNumberValid():
var phoneNumber by rememberSaveable { mutableStateOf("") }
val state = rememberKomposeCountryCodePickerState()
KomposeCountryCodePicker(
modifier = Modifier.fillMaxWidth(),
text = phoneNumber,
onValueChange = { phoneNumber = it },
state = state,
error = !state.isPhoneNumberValid(),
)
Note
isPhoneNumberValid() uses Google's libphonenumber on Android and JVM, and built-in per-country rules on iOS, JS, and WasmJS. See the Platforms page for details.