custom_phone_field 1.0.3
custom_phone_field: ^1.0.3 copied to clipboard
A smart and customizable Flutter package for phone number input with country selection, validation, and formatting.
Custom Phone Field #
A beautiful and customizable phone number input field for Flutter applications. This package provides a ready-to-use phone input field with country selection, validation, and customization options.
Features #
- 🌍 Country selection with flags and dial codes
- 📱 Phone number validation with country-specific rules
- 🎨 Two widget options:
CustomPhoneInput(pre-built) andPhoneField(customizable) - 🔄 Form integration with validation
- 🌐 Internationalization support (20+ languages)
- ♿ Accessibility support
- 📦 Lightweight and easy to use
Installation #
Add this to your package's pubspec.yaml file:
dependencies:
custom_phone_field: ^1.0.3
Quick Start #
Using CustomPhoneInput (Pre-built Widget) #
import 'package:custom_phone_field/custom_phone_field.dart';
CustomPhoneInput(
initialCountry: 'US',
onCountryChanged: (country) {
print('Selected country: ${country.name}');
},
onPhoneNumberChanged: (phoneNumber) {
print('Phone number: $phoneNumber');
},
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a phone number';
}
return null;
},
)
Using PhoneField (Customizable Widget) #
import 'package:custom_phone_field/custom_phone_field.dart';
PhoneField(
initialCountry: 'US',
onCountryChanged: (country) {
print('Selected country: ${country.name}');
},
builder: (context, country, openPicker) {
return Row(
children: [
// Country selector
GestureDetector(
onTap: openPicker,
child: Container(
padding: EdgeInsets.all(8),
child: Row(
children: [
Text('+${country.dialCode}'),
Icon(Icons.arrow_drop_down),
],
),
),
),
// Phone input
Expanded(
child: TextField(
keyboardType: TextInputType.phone,
decoration: InputDecoration(
hintText: 'Enter phone number',
),
),
),
],
);
},
)
Available Widgets #
1. CustomPhoneInput #
A pre-built, ready-to-use phone input field with common customizations.
CustomPhoneInput({
Key? key,
String initialCountry = 'EG',
String languageCode = 'en',
List<String> availableCountries = const [],
void Function(Country country)? onCountryChanged,
void Function(String phoneNumber)? onPhoneNumberChanged,
String? initialValue,
bool enabled = true,
bool readOnly = false,
InputDecoration? decoration,
TextStyle? style,
String? Function(String?)? validator,
TextEditingController? controller,
})
Properties
| Property | Type | Description |
|---|---|---|
initialCountry |
String |
Initial country code (default: 'EG') |
languageCode |
String |
Language code for localization (default: 'en') |
availableCountries |
List<String> |
List of available country codes |
onCountryChanged |
Function(Country) |
Callback when country is changed |
onPhoneNumberChanged |
Function(String) |
Callback when phone number is changed |
initialValue |
String? |
Initial phone number value |
enabled |
bool |
Whether the field is enabled |
readOnly |
bool |
Whether the field is read-only |
decoration |
InputDecoration? |
Custom input decoration |
style |
TextStyle? |
Custom text style |
validator |
Function(String?)? |
Form validation callback |
controller |
TextEditingController? |
Controller for programmatic control of the input field |
2. PhoneField #
A customizable phone field widget that allows complete control over the UI.
PhoneField({
Key? key,
required Widget Function(
BuildContext context,
Country country,
VoidCallback openPicker,
) builder,
String initialCountry = 'EG',
String languageCode = 'en',
List<String> availableCountries = const [],
void Function(Country country)? onCountryChanged,
})
The PhoneField widget provides a base implementation that you can customize using the builder pattern. The builder function receives:
context: The current BuildContextcountry: The currently selected Country object with properties:name: Country namecode: ISO country code (e.g., 'US')dialCode: Country dial code (e.g., '+1')flag: Country flag emojiminLength: Minimum phone number lengthmaxLength: Maximum phone number lengthnameTranslations: Map of country names in different languages
openPicker: A callback function to open the country picker dialog
Country Model #
The Country model provides detailed information about each country:
class Country {
final String name;
final Map<String, String> nameTranslations;
final String flag;
final String code;
final String dialCode;
final String regionCode;
final int minLength;
final int maxLength;
final String pattern;
final List<String> allowedCharacters;
final bool requiresAreaCode;
final String? areaCodePattern;
final String? exampleNumber;
}
Examples #
Basic Usage with CustomPhoneInput #
CustomPhoneInput(
onPhoneNumberChanged: (phoneNumber) {
print('Phone number: $phoneNumber');
},
)
Form Integration #
final _formKey = GlobalKey<FormState>();
Form(
key: _formKey,
child: CustomPhoneInput(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a phone number';
}
return null;
},
),
)
Custom Styling #
CustomPhoneInput(
initialCountry: 'US',
style: TextStyle(
fontSize: 16,
color: Colors.black87,
),
decoration: InputDecoration(
labelText: 'Phone Number',
hintText: 'Enter your phone number',
prefixIcon: Icon(Icons.phone),
),
)
Restricted Countries #
CustomPhoneInput(
availableCountries: ['US', 'CA', 'GB', 'AU'],
initialCountry: 'US',
)
Custom Implementation with PhoneField #
PhoneField(
initialCountry: 'US',
onCountryChanged: (country) {
print('Selected country: ${country.name}');
},
builder: (context, country, openPicker) {
return Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
// Country flag and code
GestureDetector(
onTap: openPicker,
child: Container(
padding: EdgeInsets.all(8),
child: Row(
children: [
Text(country.flag),
SizedBox(width: 8),
Text('+${country.dialCode}'),
],
),
),
),
// Phone input
Expanded(
child: TextField(
keyboardType: TextInputType.phone,
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter phone number',
),
),
),
],
),
);
},
)
Using with Controller #
class _MyFormState extends State<MyForm> {
final _phoneController = TextEditingController();
@override
void initState() {
super.initState();
// Set initial value
_phoneController.text = '+1234567890';
}
@override
void dispose() {
_phoneController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return CustomPhoneInput(
controller: _phoneController,
onPhoneNumberChanged: (phoneNumber) {
print('Phone number: $phoneNumber');
},
);
}
}
Programmatic Control #
// Clear the input
_phoneController.clear();
// Set a new value
_phoneController.text = '+1987654321';
// Get the current value
String currentPhone = _phoneController.text;
// Listen to changes
_phoneController.addListener(() {
print('Phone number changed: ${_phoneController.text}');
});
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License #
Copyright (c) 2025 Habeeb
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments #
- Country flags from flag-icons
- Phone number validation patterns from libphonenumber
Support #
If you find this package helpful, please give it a star on GitHub and share it with others!
For issues and feature requests, please use the GitHub issue tracker.