phone_text_field 2.0.0
phone_text_field: ^2.0.0 copied to clipboard
A comprehensive Flutter plugin for international phone number input, validation, formatting, and country selection with localization support.
📱 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