Kompose Country Code Picker

repository·main·Indexed 43 days ago

https://github.com/joelkanyi/kompose-country-code-picker

A Compose Multiplatform country code picker built with Material 3, supporting Android, iOS, Desktop, and Web. It provides built-in phone number validation, formatting, and a responsive dialog for selecting countries with flags and dialling codes. Features include the KomposeCountryCodePicker composable, CountrySelectionDialog for custom UIs, and state management via rememberKomposeCountryCodePickerState.

Tokens
8.2K
Snippets
22
Records
31
Agent score
51%

What's inside Kompose Country Code Picker

  1. Platform-specific UI and behavior notes

    main

    When using the library, be aware of the following platform-specific behaviors:

    • Keyboard Navigation: On Desktop and Web platforms, the country selection dialog supports Arrow keys, Enter, and Escape.
    • Responsive Dialog: The country selection dialog is responsive. It appears as a full-screen view on compact screens (width < 600dp) and as a popup on expanded screens.
    • Validation Engines: Android and JVM provide high-accuracy validation via Google's libphonenumber. iOS, JS, and WasmJS use built-in digit-length rules which cover most standard use cases.
  2. Implement a fully custom text field

    main

    If you need to use your own design system, you can bypass the default KomposeCountryCodePicker component. Instead, use CountrySelectionDialog to show the picker and interact with state.selectedCountry and state.countryList to drive your custom UI. Use state.setCode(country.code) to update the selection.

    @OptIn(RestrictedApi::class)
    @Composable
    fun PhoneNumberField() {
        var phoneNumber by rememberSaveable { mutableStateOf("") }
        var showCountryPicker by rememberSaveable { mutableStateOf(false) }
        val state = rememberKomposeCountryCodePickerState()
    
        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
                },
            )
        }
    
        OutlinedTextField(
            modifier = Modifier.fillMaxWidth(),
            value = phoneNumber,
            onValueChange = { phoneNumber = it },
            placeholder = { Text("Phone Number") },
            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone),
            leadingIcon = {
                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)
                }
            },
        )
    }
  3. Install Kompose Country Code Picker in Android Projects

    main

    For standalone Android projects, add the dependency directly to your app's build.gradle.kts file. Ensure mavenCentral() is present in your repositories.

    Using Kotlin DSL

    dependencies {
        implementation("io.github.joelkanyi:komposecountrycodepicker:<latest-version>")
    }

    Using Gradle Version Catalog

    1. Add the version and library to libs.versions.toml:
    [versions]
    komposecountrycodepicker = "<latest-version>"
    
    [libraries]
    komposecountrycodepicker = { module = "io.github.joelkanyi:komposecountrycodepicker", version.ref = "komposecountrycodepicker" }
    1. Apply the dependency in your build.gradle.kts:
    dependencies {
        implementation(libs.komposecountrycodepicker)
    }
  4. Run sample applications for each platform

    main

    You can run the provided sample applications located in the sample/ directory using the following commands:

    • Android: ./gradlew :sample:android:installDebug
    • Desktop (JVM): ./gradlew :sample:desktop:run
    • Web (JS): ./gradlew :sample:web-js:jsBrowserDevelopmentRun
    • Web (WasmJS): ./gradlew :sample:web-wasm:wasmJsBrowserDevelopmentRun
    • iOS: Open sample/iosApp/iosApp.xcodeproj in Xcode and run on a simulator or device.
    # Android
    ./gradlew :sample:android:installDebug
    
    # Desktop (JVM)
    ./gradlew :sample:desktop:run
    
    # Web (JS)
    ./gradlew :sample:web-js:jsBrowserDevelopmentRun
    
    # Web (WasmJS)
    ./gradlew :sample:web-wasm:wasmJsBrowserDevelopmentRun
  5. Style phone number and country code independently

    main

    By default, textStyle applies to both the phone number input and the country code. To apply different styles to each, use the countryCodeTextStyle parameter for the country code specifically.

    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
    )
  6. Override default country detection

    main

    The library automatically detects the user's country based on platform-specific methods (e.g., Locale.getDefault().country on Android/JVM, NSLocale on iOS, or navigator.language on Web).

    To bypass auto-detection and set a specific starting country, pass the defaultCountryCode parameter to rememberKomposeCountryCodePickerState().

    // Example of overriding auto-detection
    val state = rememberKomposeCountryCodePickerState(defaultCountryCode = "US")
  7. Build a fully custom phone input using Country data

    main

    If you have a custom design system, you can bypass KomposeCountryCodePicker and use the state object to drive your own UI. You can use state.selectedCountry to access the flag and phone code, and CountrySelectionDialog to show the country picker.

    Note: state.setCode() is marked with @RestrictedApi. You must add @OptIn(RestrictedApi::class) to use it.

    @OptIn(RestrictedApi::class)
    @Composable
    fun PhoneNumberField() {
        var phoneNumber by rememberSaveable { mutableStateOf("") }
        var showCountryPicker by rememberSaveable { mutableStateOf(false) }
        val state = rememberKomposeCountryCodePickerState()
    
        if (showCountryPicker) {
            CountrySelectionDialog(
                countryList = state.countryList,
                onDismissRequest = { showCountryPicker = false },
                onSelect = { country ->
                    state.setCode(country.code)
                    showCountryPicker = false
                },
            )
        }
    
        OutlinedTextField(
            value = phoneNumber,
            onValueChange = { phoneNumber = it },
            leadingIcon = {
                Row(modifier = Modifier.clickable { showCountryPicker = true }) {
                    Image(
                        painter = painterResource(state.selectedCountry.flag),
                        contentDescription = state.selectedCountry.name,
                    )
                    Text(text = state.selectedCountry.phoneNoCode)
                }
            },
        )
    }
  8. Migrate from Android-only to Compose Multiplatform

    main

    If you are upgrading from the Android-only version of Kompose Country Code Picker to the Compose Multiplatform version, you must move the dependency from your Android app/build.gradle.kts to your KMP module's commonMain source set. The Maven coordinates remain identical.

    // In your KMP module's build.gradle.kts
    kotlin {
        sourceSets {
            commonMain.dependencies {
                implementation("io.github.joelkanyi:komposecountrycodepicker:x.y.z")
            }
        }
    }
  9. Use Country Code Picker with TextField

    main

    The primary usage pattern is to use KomposeCountryCodePicker as a complete input component. It provides an OutlinedTextField with an integrated country code picker as the leading icon. It follows the standard Compose controlled-text pattern using text and onValueChange.

    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") 
        },
        shape = MaterialTheme.shapes.medium,
        colors = TextFieldDefaults.colors(
            unfocusedContainerColor = Color.Transparent,
            focusedContainerColor = Color.Transparent,
        ),
        state = state,
    )
  10. Use CountrySelectionDialog as a standalone component

    main

    If you have a custom text field and only want to use the library's searchable country list and flag assets, use the CountrySelectionDialog composable independently.

    To update the state when a user selects a country, call state.setCode(country.code). You can then access the selected country's details via state.selectedCountry (providing flag, name, and phoneNoCode).

    CountrySelectionDialog(
        countryList = state.countryList,
        containerColor = MaterialTheme.colorScheme.background,
        contentColor = MaterialTheme.colorScheme.onBackground,
        onDismissRequest = { /* handle dismiss */ },
        onSelect = { country ->
            state.setCode(country.code) // updates state.selectedCountry
        },
    )
  11. Use Country Code Picker Only (Picker Mode)

    main

    To display only the country selector without an attached text field (useful for embedding in custom layouts or as a leadingIcon), set showOnlyCountryCodePicker = true.

    val state = rememberKomposeCountryCodePickerState()
    
    KomposeCountryCodePicker(
        modifier = Modifier,
        showOnlyCountryCodePicker = true,
        text = phoneNumber,
        state = state,
    )