flutter_country_phone 0.1.2
flutter_country_phone: ^0.1.2 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
CountryPhoneScopeto share oneCountryBlocacross many fields when you want explicit catalog reuse. CountryFormFieldfor selecting a country inside aForm.PhoneFormFieldfor collecting an international phone number.CountryFieldandPhoneFieldlower-level widgets when you do not need aFormFieldwrapper.- Search by localized country name, ISO code, or phone prefix.
onlyCountriesfiltering for restricted country lists.CountryPhoneFieldStyleplus item/button builders for UI customization.- Locale fallback support for values such as
es,es_MX,es-MX,pt_BR, andpt-BR.
Installation #
Add the package to your pubspec.yaml:
dependencies:
flutter_country_phone: ^0.1.2
The package declares its own flag assets. No extra asset registration is needed in your app.
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 #
Most visual customization is intentionally grouped into
CountryPhoneFieldStyle:
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 #
CountryEntityis immutable, extendsEquatable, and includescopyWith.CountryModelextendsCountryEntityand providesfromJson,fromEntity, andtoJson.CountryModel.fromJsonacceptsphoneCodeorphone_codeand normalizes ISO codes to uppercase.CountryFormField,CountryField,PhoneFormField,PhoneField, andCountryWidgetautomatically provide aCountryBlocwhen no ancestor bloc is present.CountryPhoneScopeis available when a subtree should share one explicit country catalog instance.- Phone formatting and parsing are delegated to
phone_numbers_parser. - The package is UI-framework friendly: use the widgets directly or keep the domain/data classes for app-specific wrappers.
Development #
Run the local checks before publishing:
flutter analyze
flutter test
flutter pub publish --dry-run
License #
MIT License. See LICENSE.