📱 Phone Text Field
A comprehensive Flutter package for international phone number input with validation, formatting, and country selection. Perfect for apps requiring phone number collection with proper validation and beautiful UI.
🚀 Live Demo
🌐 Try the Interactive Web Demo - Experience all features in your browser!
📱 Mobile Demo: Clone and run the example app to see it in action on mobile devices.
✨ Demo

🎯 Key Features
🌍 International Support
- 200+ countries with proper validation rules
- Automatic formatting based on country standards
- Real-time validation with visual feedback
- Smart country detection from phone numbers
🎨 Customizable UI
- Material 3 Design support
- Dark/Light theme compatibility
- Flexible styling options
- Custom decorations for input fields
- Responsive design for web and mobile
🔧 Developer-Friendly
- TextEditingController support for form integration
- Validation callbacks with custom error messages
- Localization support (Arabic, English, French)
- TypeScript-like strongly typed API
- Well-documented with comprehensive examples
🐛 Recent Bug Fixes (v1.0.0+)
- ✅ Fixed controller integration - Proper TextEditingController support
- ✅ Fixed +1 country codes - US/Canada flag switching issue resolved
- ✅ Fixed Chinese validation - Corrected 11-digit validation for China
- ✅ Improved performance - Optimized country selection logic
📦 Installation
1. Add Dependency
Add this to your pubspec.yaml:
dependencies:
  phone_text_field: ^1.0.0 # Use latest version
2. Install
flutter pub get
3. Import
import 'package:phone_text_field/phone_text_field.dart';
🔥 Quick Start
Basic Usage
PhoneTextField(
  onChanged: (phoneNumber) {
    print('Complete number: ${phoneNumber.completeNumber}');
    print('Country: ${phoneNumber.countryISOCode}');
  },
)
With Controller (Form Integration)
class MyForm extends StatefulWidget {
  @override
  _MyFormState createState() => _MyFormState();
}
class _MyFormState extends State<MyForm> {
  final _phoneController = TextEditingController();
  final _formKey = GlobalKey<FormState>();
  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: PhoneTextField(
        controller: _phoneController,
        isRequired: true,
        autovalidateMode: AutovalidateMode.onUserInteraction,
        decoration: const InputDecoration(
          labelText: 'Phone Number',
          border: OutlineInputBorder(),
        ),
        onChanged: (phoneNumber) {
          // Handle phone number changes
        },
      ),
    );
  }
  @override
  void dispose() {
    _phoneController.dispose();
    super.dispose();
  }
}
🎨 Advanced Examples
Custom Styling with Material 3
PhoneTextField(
  initialCountryCode: 'AE',
  decoration: const InputDecoration(
    filled: true,
    labelText: 'Phone Number',
    border: OutlineInputBorder(
      borderRadius: BorderRadius.all(Radius.circular(12)),
    ),
    prefixIcon: Icon(Icons.phone),
  ),
  searchFieldInputDecoration: const InputDecoration(
    filled: true,
    border: OutlineInputBorder(
      borderRadius: BorderRadius.all(Radius.circular(12)),
    ),
    suffixIcon: Icon(Icons.search),
    hintText: 'Search country',
  ),
  countryViewOptions: CountryViewOptions.countryCodeWithFlag,
  onChanged: (phoneNumber) {
    debugPrint('Phone: ${phoneNumber.completeNumber}');
  },
)
Arabic Localization
PhoneTextField(
  locale: const Locale('ar'),
  decoration: const InputDecoration(
    filled: true,
    labelText: 'رقم الهاتف',
    border: OutlineInputBorder(
      borderRadius: BorderRadius.all(Radius.circular(12)),
    ),
    prefixIcon: Icon(Icons.phone),
  ),
  searchFieldInputDecoration: const InputDecoration(
    filled: true,
    border: OutlineInputBorder(
      borderRadius: BorderRadius.all(Radius.circular(12)),
    ),
    suffixIcon: Icon(Icons.search),
    hintText: 'بحث عن بالاسم او الرمز',
  ),
  dialogTitle: 'اختر الدولة',
  initialCountryCode: 'AE',
  onChanged: (phoneNumber) {
    debugPrint('رقم الهاتف: ${phoneNumber.completeNumber}');
  },
)
Validation & Error Handling
PhoneTextField(
  isRequired: true,
  invalidNumberMessage: 'Please enter a valid phone number',
  autovalidateMode: AutovalidateMode.onUserInteraction,
  decoration: const InputDecoration(
    labelText: 'Phone Number *',
    border: OutlineInputBorder(),
    helperText: 'Enter your phone number with country code',
  ),
  onChanged: (phoneNumber) {
    if (phoneNumber.isValid) {
      print('Valid number: ${phoneNumber.completeNumber}');
    }
  },
)
Different Country Display Options
// Flag only
PhoneTextField(
  countryViewOptions: CountryViewOptions.countryFlagOnly,
  onChanged: (phoneNumber) {},
)
// Country name with flag
PhoneTextField(
  countryViewOptions: CountryViewOptions.countryNameWithFlag,
  onChanged: (phoneNumber) {},
)
// Country code only
PhoneTextField(
  countryViewOptions: CountryViewOptions.countryCodeOnly,
  onChanged: (phoneNumber) {},
)
📋 API Reference
PhoneTextField Properties
| Property | Type | Default | Description | 
|---|---|---|---|
| onChanged | Function(PhoneNumber) | required | Callback when phone number changes | 
| controller | TextEditingController? | null | Controller for form integration | 
| initialCountryCode | String? | null | Initial country code (e.g., 'US', 'AE') | 
| initialValue | String? | null | Initial phone number value | 
| decoration | InputDecoration? | null | Input field decoration | 
| searchFieldInputDecoration | InputDecoration? | null | Country search field decoration | 
| locale | Locale? | null | Localization (ar, en, fr) | 
| isRequired | bool | false | Whether the field is required | 
| invalidNumberMessage | String? | null | Custom validation error message | 
| countryViewOptions | CountryViewOptions | countryCodeWithFlag | How to display countries | 
| dialogTitle | String? | null | Custom dialog title | 
| autovalidateMode | AutovalidateMode? | null | When to validate input | 
PhoneNumber Object
class PhoneNumber {
  String completeNumber;      // Full international number
  String countryISOCode;      // Country code (US, AE, etc.)
  String countryCode;         // Dial code (+1, +971, etc.)
  String number;              // Local number
  bool isValid;               // Validation status
}
CountryViewOptions
enum CountryViewOptions {
  countryCodeOnly,           // +1
  countryNameOnly,           // United States
  countryFlagOnly,           // 🇺🇸
  countryCodeWithFlag,       // 🇺🇸 +1
  countryNameWithFlag,       // 🇺🇸 United States
}
🔧 Integration Examples
With Form Validation
class PhoneForm extends StatefulWidget {
  @override
  _PhoneFormState createState() => _PhoneFormState();
}
class _PhoneFormState extends State<PhoneForm> {
  final _formKey = GlobalKey<FormState>();
  final _phoneController = TextEditingController();
  PhoneNumber? _phoneNumber;
  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: [
          PhoneTextField(
            controller: _phoneController,
            isRequired: true,
            autovalidateMode: AutovalidateMode.onUserInteraction,
            decoration: const InputDecoration(
              labelText: 'Phone Number *',
              border: OutlineInputBorder(),
            ),
            onChanged: (phoneNumber) {
              setState(() {
                _phoneNumber = phoneNumber;
              });
            },
          ),
          const SizedBox(height: 20),
          ElevatedButton(
            onPressed: () {
              if (_formKey.currentState!.validate() &&
                  _phoneNumber?.isValid == true) {
                // Process valid phone number
                print('Valid phone: ${_phoneNumber!.completeNumber}');
              }
            },
            child: const Text('Submit'),
          ),
        ],
      ),
    );
  }
  @override
  void dispose() {
    _phoneController.dispose();
    super.dispose();
  }
}
With Bloc/Provider State Management
// Using Provider
class PhoneProvider extends ChangeNotifier {
  PhoneNumber? _phoneNumber;
  PhoneNumber? get phoneNumber => _phoneNumber;
  void updatePhone(PhoneNumber phoneNumber) {
    _phoneNumber = phoneNumber;
    notifyListeners();
  }
}
// In your widget
Consumer<PhoneProvider>(
  builder: (context, phoneProvider, child) {
    return PhoneTextField(
      onChanged: (phoneNumber) {
        phoneProvider.updatePhone(phoneNumber);
      },
    );
  },
)
🐛 Bug Fixes & Improvements
Version 1.0.0+ Fixes
✅ Controller Integration Issue (#2)
Problem: Controller property was commented out, causing integration issues with forms.
// Before (broken)
// final TextEditingController? controller;
// After (fixed)
final TextEditingController? controller;
✅ +1 Country Code Flag Switching (#3)
Problem: When editing US/Canada numbers, flag would incorrectly switch between countries. Solution: Improved country selection logic to preserve originally selected country when multiple countries share the same dial code.
✅ Chinese Phone Number Validation (#4)
Problem: Chinese numbers were validated as 12 digits instead of 11.
// Before
'CN': {minLength: 12, maxLength: 12}
// After
'CN': {minLength: 11, maxLength: 11}
Testing the Fixes
You can test these fixes in the example app or the live demo:
- Controller Test: Use the form integration examples
- +1 Countries Test: Try switching between US (+1) and Canada (+1)
- Chinese Numbers Test: Enter a Chinese number like 13812345678
🌍 Supported Countries
This package supports 200+ countries with proper validation rules including:
- 🇺🇸 United States (+1)
- 🇨🇦 Canada (+1)
- 🇬🇧 United Kingdom (+44)
- 🇦🇪 United Arab Emirates (+971)
- 🇨🇳 China (+86)
- 🇮🇳 India (+91)
- 🇧🇷 Brazil (+55)
- 🇩🇪 Germany (+49)
- 🇫🇷 France (+33)
- 🇯🇵 Japan (+81)
- And many more...
🌐 Localization
The package supports multiple languages:
| Language | Locale | Status | 
|---|---|---|
| English | en | ✅ Full Support | 
| Arabic | ar | ✅ Full Support | 
| French | fr | ✅ Full Support | 
Adding New Languages
Contributions for additional languages are welcome! Check our contribution guide for details.
📱 Platform Support
| Platform | Status | Notes | 
|---|---|---|
| 📱 iOS | ✅ Full Support | iOS 9.0+ | 
| 🤖 Android | ✅ Full Support | API 16+ | 
| 🌐 Web | ✅ Full Support | All modern browsers | 
| 🖥️ macOS | ✅ Full Support | macOS 10.11+ | 
| 🖥️ Windows | ✅ Full Support | Windows 10+ | 
| 🐧 Linux | ✅ Full Support | Any distribution | 
🎯 Examples & Demos
📱 Example App
Run the comprehensive example app:
cd example
flutter run
🌐 Web Demo
Experience the full demo in your browser:
cd example
flutter run -d chrome
📋 Code Examples
📸 Screenshots
📱 Feature Gallery (Grid Layout)
|   🌍 Country Selection Easy country selection with search functionality |   📱 Phone Input Clean phone number input with validation | 
|   🇸🇦 Arabic Localization Full RTL support with Arabic text |   ✅ Validation & Error Handling Real-time validation with error messages | 
🎬 Live Demo

Interactive demo showing all features in action
🎨 Key Features Grid
|   🌍 International 200+ countries with proper validation rules |   🎨 Customizable Material 3 design with flexible styling |   🌐 Localized Arabic, English, French support |   ✅ Validation Real-time validation with custom messages | 
� Feature Showcase
| ✨ Feature | 📷 Preview | 📝 Description | 
|---|---|---|
| 🌍 International |  | 200+ countries with proper validation rules | 
| 🎨 Customizable |  | Material 3 design with flexible styling | 
| 🌐 Localized |  | Arabic, English, French support | 
| ✅ Validation |  | Real-time validation with custom messages | 
🤝 Contributing
We welcome contributions! Here's how you can help:
🐛 Report Bugs
- Use the issue tracker
- Include reproduction steps and Flutter/Dart versions
- Check existing issues first
✨ Request Features
- Open a feature request
- Describe the use case and expected behavior
- Consider contributing the implementation
🔧 Submit Pull Requests
- Fork the repository
- Create a feature branch: git checkout -b feature/amazing-feature
- Make your changes and add tests
- Ensure all tests pass: flutter test
- Commit with conventional commits: git commit -m "feat: add amazing feature"
- Push to your fork: git push origin feature/amazing-feature
- Open a Pull Request
📝 Development Setup
# Clone the repository
git clone https://github.com/MohamedAbd0/phone_text_field.git
cd phone_text_field
# Install dependencies
flutter pub get
# Run tests
flutter test
# Run example app
cd example
flutter run
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
👨💻 Author
Mohamed Abdo
- 🐙 GitHub: @MohamedAbd0
- 📧 Email: your-email@example.com
- 🐦 Twitter: @YourTwitter
⭐ Show Your Support
If this package helped you, please:
- ⭐ Star the repository on GitHub
- 👍 Like the package on pub.dev
- 🐦 Share it on social media
- 💝 Consider sponsoring the project
📊 Stats
Made with ❤️ by Mohamed Abdo