phone_field_plus 1.0.2 copy "phone_field_plus: ^1.0.2" to clipboard
phone_field_plus: ^1.0.2 copied to clipboard

Offline-first Flutter phone input with country picker and auto-detection.

GitHub Repository Pub Version License Dart Flutter

phone_field_plus #

A highly customizable, offline-first Flutter phone number input widget with:

  • 🌍 200+ countries β€” fully hardcoded, zero network calls
  • πŸ” Auto-detection β€” types +216? Country switches to πŸ‡ΉπŸ‡³ Tunisia automatically
  • βœ… Strict / loose validation per country (min/max length rules)
  • 🎨 Fully customizable β€” swap flags, list tiles, or the entire prefix
  • πŸ“¦ Pub.dev ready β€” null-safe, clean architecture, tested

Installation #

dependencies:
  phone_field_plus: ^1.0.0

Quick Start #

import 'package:phone_field_plus/phone_field_plus.dart';

PhoneField(
  initialCountry: Countries.tunisia,
  autoDetectCountry: true,
  autoValidate: true,
  onChanged: (PhoneValue value) {
    print(value.e164);           // +21612345678
    print(value.country.isoCode); // TN
    print(value.isValid);         // true
  },
)

PhoneField API #

Parameter Type Default Description
initialCountry Country? Countries.unitedStates Country shown on first render
countries List<Country>? Countries.all Countries available in picker
autoDetectCountry bool true Auto-switch country from typed +code
autoValidate bool false Validate on every keystroke
validatorMode ValidatorMode strict strict or loose
validator String? Function(PhoneValue)? β€” Custom validator
onChanged ValueChanged<PhoneValue>? β€” Fires on every change
onSubmitted ValueChanged<PhoneValue>? β€” Fires on keyboard submit
controller PhoneController? β€” External controller
decoration InputDecoration? β€” Pass-through to TextField
flagBuilder Widget Function(Country)? β€” Custom flag widget
countryPickerBuilder Widget Function(Country, VoidCallback)? β€” Custom picker tile
prefixBuilder Widget Function(Country, VoidCallback)? β€” Replaces entire prefix

PhoneValue #

class PhoneValue {
  final Country country;
  final String nationalNumber;    // "12345678"
  final String internationalNumber; // "+21612345678"
  final String e164;              // "+21612345678"
  final bool isValid;
}

Countries #

// Named constants
Countries.tunisia     // πŸ‡ΉπŸ‡³ +216
Countries.france      // πŸ‡«πŸ‡· +33
Countries.unitedStates // πŸ‡ΊπŸ‡Έ +1

// Groups
Countries.all         // All 200+
Countries.africa      // African countries
Countries.europe      // European countries
Countries.asia        // Asian countries
Countries.americas    // North + South American countries
Countries.oceania     // Oceanian countries

// O(1) lookup
Countries.byDialCode['216']  // β†’ Countries.tunisia
Countries.byIsoCode['TN']    // β†’ Countries.tunisia

Validation Modes #

// strict (default) β€” enforces country's exact min/max length
PhoneField(
  validatorMode: ValidatorMode.strict,  // e.g. France = exactly 9 digits
)

// loose β€” accepts partial input (β‰₯3 digits, ≀maxLength)
PhoneField(
  validatorMode: ValidatorMode.loose,
)

Auto-Detection #

The engine uses longest-prefix matching β€” critical for overlapping codes:

+2165... β†’ +216 (Tunisia) β€” NOT +2 (some 2-digit code)
+1876... β†’ +1876 (Jamaica) β€” NOT +1 (US/CA)
+331...  β†’ +33 (France) β€” NOT +3 (hypothetical)

PhoneController #

Use when you need programmatic control:

final controller = PhoneController(
  initialCountry: Countries.france,
  autoDetectCountry: true,
  validatorMode: ValidatorMode.strict,
);

// Read current value
print(controller.phoneValue.e164);

// Switch country
controller.setCountry(Countries.germany);

// Use as a FormField validator
validator: (_) => controller.validate(),

Custom UI #

Custom flag #

PhoneField(
  flagBuilder: (country) => Text(
    country.flagEmoji,
    style: TextStyle(fontSize: 24),
  ),
)

Custom picker tile #

PhoneField(
  countryPickerBuilder: (country, onSelect) => ListTile(
    leading: Text(country.flagEmoji),
    title: Text(country.name),
    subtitle: Text('+${country.dialCode}'),
    onTap: onSelect,
  ),
)

Fully custom prefix #

PhoneField(
  prefixBuilder: (country, onTap) => GestureDetector(
    onTap: onTap,
    child: Chip(label: Text('+${country.dialCode}')),
  ),
)

Form Integration #

Form(
  key: _formKey,
  child: PhoneField(
    autoValidate: false,
    validator: (value) {
      if (!value.hasNumber) return 'Required';
      if (!value.isValid) return 'Invalid number';
      return null;
    },
  ),
)

Country Model #

class Country {
  final String name;        // "Tunisia"
  final String isoCode;     // "TN"
  final String dialCode;    // "216"  (no '+')
  final String flagEmoji;   // "πŸ‡ΉπŸ‡³"
  final int minLength;      // 8
  final int maxLength;      // 8
  final String continent;   // "Africa"
}

Architecture #

lib/
  src/
    models/
      country.dart          # Country data model
      phone_value.dart      # Immutable result object
    data/
      countries.dart        # 200+ country database
    widgets/
      phone_field.dart      # Main public widget
      country_picker.dart   # Bottom sheet picker
    controllers/
      phone_controller.dart # State, parsing, validation
    utils/
      phone_parser.dart     # Pure parsing functions
      country_resolver.dart # Longest-prefix matching engine

License #

MIT

1
likes
145
points
0
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Offline-first Flutter phone input with country picker and auto-detection.

License

MIT (license)

Dependencies

flutter

More

Packages that depend on phone_field_plus