flutter_country_phone 0.2.1 copy "flutter_country_phone: ^0.2.1" to clipboard
flutter_country_phone: ^0.2.1 copied to clipboard

Country catalog, localized country picker, SVG flags, and international phone form fields for Flutter apps.

flutter_country_phone #

Country catalog, localized country picker, SVG flags, and international phone form fields for Flutter apps.

flutter_country_phone gives you a reusable foundation for forms that need a country selector, a phone input with country prefix handling, or a simple way to render country names and flags. It ships with its own static country catalog and flag assets, so your app does not need to duplicate those resources.

Features #

  • Static country catalog with ISO 3166-1 alpha-2 codes, phone prefixes, and localized country names.
  • Package-owned SVG flags through CountryFlag.
  • Drop-in widgets: no app-level provider is required.
  • Optional CountryPhoneScope to share one CountryBloc across many fields when you want explicit catalog reuse.
  • CountryFormField for selecting a country inside a Form.
  • PhoneFormField for collecting an international phone number.
  • CountryField and PhoneField lower-level widgets when you do not need a FormField wrapper.
  • Search by localized country name, ISO code, or phone prefix.
  • Searchable country menus powered by anchored_dropdown_field, with theme-aware styling, highlighted selections, animations, keyboard navigation, and keyboard-aware positioning.
  • onlyCountries filtering for restricted country lists.
  • CountryPhoneFieldStyle plus item/button builders for UI customization.
  • Locale fallback support for values such as es, es_MX, es-MX, pt_BR, and pt-BR.

Installation #

Add the package to your pubspec.yaml:

dependencies:
  flutter_country_phone: ^0.2.1

The package declares its own flag assets. No extra asset registration is needed in your app.

Requires Dart 3.12.2 or newer and Flutter 3.35.0 or newer.

Basic usage #

Import the package and use the widgets directly. The country catalog provider is created internally when one is not already available in the widget tree:

import 'package:flutter_country_phone/flutter_country_phone.dart';

PhoneFormField(
  decoration: const InputDecoration(labelText: 'Phone number'),
  onChanged: (value) {
    debugPrint(value); // Example: +15551234567
  },
)

If several country and phone widgets live together and you want them to share a single in-memory catalog, wrap that small subtree with CountryPhoneScope:

CountryPhoneScope(
  child: Column(
    children: [
      CountryFormField(
        decoration: const InputDecoration(labelText: 'Country'),
      ),
      PhoneFormField(
        decoration: const InputDecoration(labelText: 'Phone number'),
      ),
    ],
  ),
)

Apps that already provide CountryBloc continue to work. The package will reuse the existing bloc instead of creating another one.

Country picker #

Use CountryFormField when the selected country is part of a Form:

CountryFormField(
  value: 'US',
  decoration: const InputDecoration(
    labelText: 'Country',
    hintText: 'Select your country',
  ),
  validator: (country) {
    if (country == null) return 'Select a country';
    return null;
  },
  onChanged: (country) {
    debugPrint(country?.iso2); // US
  },
)

Restrict the list when a flow supports only a few countries:

CountryFormField(
  onlyCountries: const ['US', 'BR', 'ES'],
  showPhoneCode: true,
  onChanged: (country) {
    debugPrint('${country?.iso2} ${country?.phoneCode}');
  },
)

If you need a plain widget instead of a form field, use CountryField:

CountryField(
  decoration: const InputDecoration(labelText: 'Country'),
  locale: 'pt-BR',
  onChanged: (country) {
    debugPrint(country.iso2);
  },
)

International phone field #

PhoneFormField lets the user select a country first and then enter the national phone number. The onChanged value is the international number string generated by phone_numbers_parser.

PhoneFormField(
  decoration: const InputDecoration(
    labelText: 'Phone number',
    hintText: 'Enter your phone number',
  ),
  textInputAction: TextInputAction.next,
  autofillHints: const [AutofillHints.telephoneNumber],
  validator: (value) {
    if (value == null || value.isEmpty) return 'Enter a phone number';
    return null;
  },
  onChanged: (value) {
    debugPrint(value); // Example: +15551234567
  },
)

You can pass an existing international number as the initial value:

PhoneFormField(
  initialValue: '+5511999999999',
  decoration: const InputDecoration(labelText: 'Phone'),
)

When the initial value is parseable and its country is allowed, the field opens directly in phone-entry mode with the matching country selected.

Rendering countries and flags #

Use CountryFlag when you only need a flag:

const CountryFlag(
  isoCode: 'BR',
  size: 24,
  borderRadius: BorderRadius.all(Radius.circular(4)),
)

Use CountryWidget when you need the localized country name with the flag:

const CountryWidget(
  iso: 'ES',
  flagSize: 18,
)

CountryWidget is also drop-in and loads the catalog automatically when needed.

Localization #

Widgets use Localizations.localeOf(context) by default. You can override the locale when a field needs to render in a specific language:

CountryFormField(
  locale: 'es-MX',
  decoration: const InputDecoration(labelText: 'País'),
)

Locale values can be language-only (es) or region-specific (es_MX, es-MX, pt_BR, pt-BR). The lookup tries the full locale, then the language code, then English, then the first non-empty available value.

You can also customize labels with a builder:

CountryFormField(
  countryLabelBuilder: (context, country) {
    return country.localizedName('pt-BR');
  },
)

For app-specific empty states, pass emptyBuilder.

Styling #

Both country selectors are built on anchored_dropdown_field. Their panels and options inherit MenuTheme and MenuButtonTheme, and the field inherits InputDecorationTheme. The selected country uses ColorScheme.primary with onPrimary text and no checkmark. Phone country options retain their dialing prefix. Explicit selectedItemStyle overrides take precedence over these selection colors.

Use CountryPhoneFieldStyle.dropdownStyle for anchored dropdown customization. DropdownFieldStyle is exported by this package:

const countryPhoneStyle = CountryPhoneFieldStyle(
  dropdownStyle: DropdownFieldStyle(menuMaxHeight: 320),
  flagSize: 24,
  selectedFlagSize: 20,
);

PhoneFormField(style: countryPhoneStyle)

Existing explicit overlay and item overrides are still supported and take precedence over the corresponding dropdownStyle properties:

const countryPhoneStyle = CountryPhoneFieldStyle(
  overlayMaxHeight: 260,
  overlayBorderRadius: BorderRadius.all(Radius.circular(18)),
  overlayElevation: 10,
  overlayPadding: EdgeInsets.all(8),
  itemPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14),
  itemBorderRadius: BorderRadius.all(Radius.circular(14)),
  flagSize: 24,
  selectedFlagSize: 20,
  flagFit: BoxFit.cover,
);

CountryFormField(
  style: countryPhoneStyle,
)

For deeper customization, replace the country item or selected-country button:

PhoneFormField(
  countryItemBuilder: (context, country, selected) {
    return ListTile(
      leading: CountryFlag(isoCode: country.iso2, size: 22),
      title: Text(country.localizedName('es')),
      trailing: Text(country.phoneCode),
      selected: selected,
    );
  },
  selectedCountryButtonBuilder: (context, country, onPressed) {
    return TextButton.icon(
      onPressed: onPressed,
      icon: CountryFlag(isoCode: country.iso2),
      label: Text(country.phoneCode),
    );
  },
)

Working with the catalog #

You can use the catalog directly through the datasource/use case stack:

final bloc = CountryBloc();

await bloc.fetchCountries();

final countries = bloc.countries;
final unitedStates = countries.firstWhere((country) => country.iso2 == 'US');

CountryBloc keeps the loaded countries in memory. Calling fetchCountries() again will reuse the current list unless force: true is passed internally by refreshCountries().

For UI code, prefer the widgets directly. Reach for CountryBloc only when you need programmatic access to the catalog or explicit sharing through CountryPhoneScope.

API notes #

  • CountryEntity is immutable, extends Equatable, and includes copyWith.
  • CountryModel extends CountryEntity and provides fromJson, fromEntity, and toJson.
  • CountryModel.fromJson accepts phoneCode or phone_code and normalizes ISO codes to uppercase.
  • CountryFormField, CountryField, PhoneFormField, PhoneField, and CountryWidget automatically provide a CountryBloc when no ancestor bloc is present.
  • CountryPhoneScope is available when a subtree should share one explicit country catalog instance.
  • Phone formatting and parsing are delegated to phone_numbers_parser.
  • Country search takes place inside the field while the menu is open. Typing a query does not replace the selected country; closing the menu restores the selected label.
  • Country search requests sentence capitalization by default. Use CountryFormField.textCapitalization to override this keyboard hint; typed and pasted queries are not transformed.
  • Country selectors use the anchored dropdown's search keyboard. The legacy textInputAction, keyboardType, and autofillHints country-field arguments remain accepted but do not configure that search input. PhoneField and PhoneFormField continue to apply textInputAction and autofillHints to the phone-number input.
  • The package is UI-framework friendly: use the widgets directly or keep the domain/data classes for app-specific wrappers.

License #

MIT License. See LICENSE.

2
likes
150
points
191
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Country catalog, localized country picker, SVG flags, and international phone form fields for Flutter apps.

Homepage

Topics

#country-picker #phone-field #flags #form #localization

License

MIT (license)

Dependencies

anchored_dropdown_field, collection, equatable, flutter, flutter_bloc, flutter_svg, phone_numbers_parser

More

Packages that depend on flutter_country_phone