flex_phone_input
A searchable phone number input field with an integrated country code picker for Flutter.
- π¨ Fully themeable β style every color, border, radius and text style via a
ThemeExtension. Apply it globally, per-widget, or derive it from your app'sColorScheme. - π Country control β show only the countries you want, hide specific ones, pin favourites, or supply your own dataset.
- π§© Four field styles β
outlined,filled,pill,underline. - πͺ Four selector surfaces β bottom sheet, dialog, full-screen page, and an inline dropdown, all sharing one searchable list.
- β
Validation β integrates with
Form, or drive the state directly.
Installation
dependencies:
flex_phone_input: ^1.0.0
import 'package:flex_phone_input/flex_phone_input.dart';
Quick start
PhoneNumberInput(
initialCountryCode: 'IN',
labelText: 'Phone number',
onChanged: (PhoneNumber phone) {
print(phone.completeNumber); // e.g. +919876543210
print(phone.country.isoCode); // IN
print(phone.number); // 9876543210
},
)
onChanged gives you a PhoneNumber with the selected country and the national number (digits only). Use completeNumber / e164 for the full +<dial><number> string.
Theming
Everything visual lives in PhoneInputTheme, a ThemeExtension. There are three ways to apply it, from broadest to most specific.
1. Globally, on your ThemeData β every field in the app picks it up, including light/dark:
MaterialApp(
theme: ThemeData.light().copyWith(
extensions: <ThemeExtension<dynamic>>[PhoneInputTheme.light()],
),
darkTheme: ThemeData.dark().copyWith(
extensions: <ThemeExtension<dynamic>>[PhoneInputTheme.dark()],
),
);
2. Derived from your ColorScheme, so it automatically matches your brand:
extensions: <ThemeExtension<dynamic>>[
PhoneInputTheme.fromColorScheme(Theme.of(context).colorScheme),
],
3. Per-widget, overriding just what you need with copyWith:
PhoneNumberInput(
theme: PhoneInputTheme.light().copyWith(
focusedBorderColor: Colors.deepPurple,
borderRadius: 14,
fillColor: const Color(0xFFF5F3FF),
),
)
If you don't pass a theme and haven't registered one on ThemeData, the widget falls back to PhoneInputTheme.light() / .dark() based on the ambient brightness.
A few of the tokens you can set: fillColor, textColor, hintColor, labelColor, borderColor, focusedBorderColor, errorColor, successColor, borderRadius, borderWidth, contentPadding, the six text styles (inputTextStyle, hintTextStyle, dialCodeTextStyle, β¦), and the selector-surface colors (surfaceColor, selectedTileColor, searchFillColor, dragHandleColor, scrimColor, β¦).
Choosing which countries appear
Restrict to an allow-list (only these show):
PhoneNumberInput(
countries: const <String>['IN', 'US', 'GB', 'AE', 'SG'],
)
Hide specific countries (everything else shows):
PhoneNumberInput(
excludeCountries: const <String>['KP', 'IR', 'SY'],
)
Pin favourites to the top of the selector:
PhoneNumberInput(
favoriteCountries: const <String>['IN', 'US', 'GB'],
)
Supply a fully custom dataset (ignores the built-in list entirely):
PhoneNumberInput(
customCountries: const <Country>[
Country(name: 'India', isoCode: 'IN', dialCode: '91'),
Country(name: 'Singapore', isoCode: 'SG', dialCode: '65'),
],
)
All four can be combined; favourites are always intersected with whatever set is effective.
Field styles
PhoneNumberInput(variant: PhoneFieldVariant.outlined) // default
PhoneNumberInput(variant: PhoneFieldVariant.filled)
PhoneNumberInput(variant: PhoneFieldVariant.pill)
PhoneNumberInput(variant: PhoneFieldVariant.underline)
Toggle the trigger contents with showFlag and showDialCode.
Selector surfaces
PhoneNumberInput(selectorMode: CountrySelectorMode.bottomSheet) // default
PhoneNumberInput(selectorMode: CountrySelectorMode.dialog)
PhoneNumberInput(selectorMode: CountrySelectorMode.fullScreen)
PhoneNumberInput(selectorMode: CountrySelectorMode.dropdown) // inline, good on desktop/web
Validation
Integrate with Form:
final formKey = GlobalKey<FormState>();
Form(
key: formKey,
child: PhoneNumberInput(
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (PhoneNumber? value) {
if (value == null || value.number.length < 6) return 'Enter a valid number';
return null;
},
),
);
// later:
if (formKey.currentState!.validate()) { /* submit */ }
Or drive the styling yourself with validationState + errorText/helperText.
Flags on Android
Emoji flags render inconsistently on some Android devices. To use image flags, pass a flagBuilder (e.g. with the country_flags package) β it's used everywhere flags appear:
PhoneNumberInput(
flagBuilder: (context, country, size) =>
CountryFlag.fromCountryCode(country.isoCode, height: size, width: size * 1.4),
)
Notes
- The built-in dataset has 175 countries. Calling codes are shared for some regions (
+1,+7), so the package stores the selectedCountryrather than inferring it from digits. - The number field accepts digits only; wrap or post-process if you need custom formatting.
License
MIT β see LICENSE.
Libraries
- flex_phone_input
- A searchable phone number input field with an integrated, fully themeable country-code picker.