📱 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
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) {
},
),
);
}
@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
PhoneTextField (
countryViewOptions: CountryViewOptions .countryFlagOnly,
onChanged: (phoneNumber) {},
)
PhoneTextField (
countryViewOptions: CountryViewOptions .countryNameWithFlag,
onChanged: (phoneNumber) {},
)
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;
String countryISOCode;
String countryCode;
String number;
bool isValid;
}
CountryViewOptions
enum CountryViewOptions {
countryCodeOnly,
countryNameOnly,
countryFlagOnly,
countryCodeWithFlag,
countryNameWithFlag,
}
🔧 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 ) {
print ('Valid phone: ${_phoneNumber!.completeNumber} ' );
}
},
child: const Text ('Submit' ),
),
],
),
);
}
@override
void dispose () {
_phoneController.dispose ();
super .dispose ();
}
}
With Bloc/Provider State Management
class PhoneProvider extends ChangeNotifier {
PhoneNumber ? _phoneNumber;
PhoneNumber ? get phoneNumber => _phoneNumber;
void updatePhone (PhoneNumber phoneNumber) {
_phoneNumber = phoneNumber;
notifyListeners ();
}
}
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.
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.
'CN' : {minLength: 12 , maxLength: 12 }
'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
git clone https://github.com/MohamedAbd0/phone_text_field.git
cd phone_text_field
flutter pub get
flutter test
cd example
flutter run
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
👨💻 Author
Mohamed Abdo
⭐ 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