phone_input_plus 0.3.0
phone_input_plus: ^0.3.0 copied to clipboard
A customizable phone input field with auto country detection, formatting and validation. Perfect for African and international phone numbers.
π± phone_input_plus #
A customizable Flutter phone input field with automatic country detection, smart formatting, and validation. Perfect for African and international phone numbers.
β¨ Features #
- Auto Country Detection - Detects user's country via IP or device locale
- Intelligent Paste Detection - Auto-detects country from pasted international numbers
- Smart Formatting - Real-time number formatting as you type
- Dynamic Length Limitation - Automatically limits input based on country
- Adaptive Keyboard - Automatically chooses the best keyboard type
- 5 Predefined Styles - Beautiful ready-to-use visual styles
- Copy Button - One-tap copy of formatted phone numbers
- Visual Feedback - Animated validation and smooth transitions
- Built-in Validation - Country-specific phone number validation
- Highly Customizable - Extensive styling and configuration options
- 52 African Countries - Comprehensive support for African phone numbers
- International Support - Works worldwide with any country
- Multiple Selector Types - BottomSheet, Dialog, or Full Page
- Search Functionality - Quick country search with multi-language support
- Smart Caching - Caches detected country for better performance
- Controller Support - Full programmatic control like TextField
πΈ Screenshots #
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
| Basic | Basic Example | BottomSheet Country | Controller Example | Dialog Country | Validation Example |
π Getting Started #
Installation #
Add this to your pubspec.yaml:
dependencies:
phone_input_plus: ^0.0.1
Then run:
flutter pub get
Import #
import 'package:phone_input_plus/phone_input_plus.dart';
π‘ Basic Usage #
Simple Phone Input #
The most basic usage with auto country detection:
PhoneInputField(
decoration: const InputDecoration(
labelText: 'Phone Number',
border: OutlineInputBorder(),
),
onChanged: (PhoneNumber phone) {
print('International: ${phone.international}');
print('Valid: ${phone.isValid}');
},
)
With Initial Country #
Set a specific initial country:
PhoneInputField(
initialCountry: CountryData.benin,
autoDetect: false, // Disable auto-detection
decoration: const InputDecoration(
labelText: 'Phone Number',
border: OutlineInputBorder(),
),
)
Limited Country List #
Restrict to specific countries (e.g., West Africa only):
PhoneInputField(
countries: const [
CountryData.benin,
CountryData.gabon,
CountryData.senegal,
CountryData.coteDivoire,
CountryData.cameroon,
],
decoration: const InputDecoration(
labelText: 'Phone Number',
border: OutlineInputBorder(),
),
)
π¨ Visual Styles #
Predefined Styles #
Choose from 5 beautiful predefined styles:
// Modern style with animations and feedback
PhoneInputField(
style: PhoneInputStyle.modern,
decoration: const InputDecoration(
labelText: 'Phone Number',
border: OutlineInputBorder(),
),
)
// Minimal style - ultra clean
PhoneInputField(
style: PhoneInputStyle.minimal,
decoration: const InputDecoration(
labelText: 'Phone',
),
)
// Rounded style - iOS-like
PhoneInputField(
style: PhoneInputStyle.rounded,
decoration: const InputDecoration(
labelText: 'Phone',
border: InputBorder.none,
),
)
// Standard and Outlined also available
Available Styles #
| Style | Description | Features |
|---|---|---|
standard |
Clean default style | Simple and professional |
modern |
Animated with feedback | β Validation icon, shake animation, dynamic colors |
minimal |
Ultra clean | Underline only, compact |
rounded |
iOS-like | Rounded corners, background fill |
outlined |
Material emphasized | Bold borders |
Custom Theme #
Create your own custom style:
PhoneInputField(
theme: PhoneInputTheme(
borderRadius: BorderRadius.circular(16),
showValidationIcon: true,
enableShakeAnimation: true,
validBorderColor: Colors.purple,
invalidBorderColor: Colors.orange,
focusedBorderColor: Colors.deepPurple,
borderWidth: 2.5,
),
)
Mix Styles #
Start with a preset and customize:
PhoneInputField(
theme: PhoneInputTheme.modern().copyWith(
validBorderColor: Colors.blue,
borderRadius: BorderRadius.circular(20),
),
)
π Copy Button #
Enable a copy button to let users copy the formatted phone number with one tap:
PhoneInputField(
showCopyButton: true, // Enable copy button
decoration: const InputDecoration(
labelText: 'Phone Number',
border: OutlineInputBorder(),
),
)
Customize Copy Button #
PhoneInputField(
showCopyButton: true,
copyButtonIcon: Icons.content_copy, // Custom icon
copiedMessage: 'Number copied!', // Custom message
)
Combine with Validation #
The copy button works seamlessly with validation icons:
PhoneInputField(
style: PhoneInputStyle.modern, // Has validation icon
showCopyButton: true, // Add copy button
// Both icons appear side-by-side
)
π― Advanced Usage #
Using PhoneController #
For programmatic control, use PhoneController:
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late PhoneController _controller;
@override
void initState() {
super.initState();
_controller = PhoneController(
initialCountry: CountryData.benin,
);
// Listen to changes
_controller.addListener(() {
print('Phone changed: ${_controller.international}');
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
PhoneInputField(
controller: _controller,
decoration: const InputDecoration(
labelText: 'Phone Number',
border: OutlineInputBorder(),
),
),
// Programmatic actions
ElevatedButton(
onPressed: () => _controller.changeCountry(CountryData.france),
child: const Text('Switch to France'),
),
ElevatedButton(
onPressed: () => _controller.clear(),
child: const Text('Clear'),
),
],
);
}
}
Form Validation #
Integrate with Flutter forms:
Form(
key: _formKey,
child: PhoneInputField(
decoration: const InputDecoration(
labelText: 'Phone Number *',
border: OutlineInputBorder(),
),
validator: (phone) {
if (phone == null || phone.isEmpty) {
return 'Phone number is required';
}
if (!phone.isValid) {
return 'Invalid phone number for ${phone.country.nameEn}';
}
return null;
},
onSubmitted: (phone) {
if (_formKey.currentState!.validate()) {
// Submit form
}
},
),
)
β¨οΈ Keyboard Type & Paste Detection #
Intelligent Paste Detection #
By default, PhoneInputField supports intelligent paste detection. When a user pastes an international number like +2290166640219, the widget automatically:
- Detects the country (Benin in this case)
- Changes the country selector
- Extracts the national number
- Formats it correctly
PhoneInputField(
enablePasteDetection: true, // Default
decoration: const InputDecoration(
labelText: 'Phone Number',
hintText: 'Paste +2290166640219',
border: OutlineInputBorder(),
),
)
Keyboard Behavior #
The keyboard type is automatically chosen based on paste detection:
| Paste Detection | Keyboard Type | Behavior |
|---|---|---|
| Enabled (default) | Text keyboard | Allows pasting numbers with + |
| Disabled | Numeric keyboard | Better UX for manual entry |
Disable paste detection for numeric keyboard:
PhoneInputField(
enablePasteDetection: false, // Numeric keyboard
decoration: const InputDecoration(
labelText: 'Phone Number',
border: OutlineInputBorder(),
),
)
Manual Override #
You can manually specify the keyboard type:
PhoneInputField(
keyboardType: TextInputType.phone, // Force numeric keyboard
enablePasteDetection: false, // Disable paste (won't work with numeric)
)
Or try to keep both features with:
PhoneInputField(
keyboardType: const TextInputType.numberWithOptions(signed: true),
enablePasteDetection: true, // May work on some devices
)
Note: The ability to paste numbers with + on numeric keyboards varies by device and OS.
π Country Detection #
The package automatically detects the user's country using multiple strategies:
- IP-based detection (primary) - Uses free IP geolocation APIs
- Device locale (fallback) - Uses the device's locale settings
- Smart caching - Caches the detected country for 24 hours
Disable Auto-Detection #
PhoneInputField(
autoDetect: false,
initialCountry: CountryData.benin,
)
Custom Cache Duration #
PhoneInputField(
detectionCacheDuration: 48, // Cache for 48 hours
)
β Validation #
Built-in Validation #
Each country has specific validation rules:
final phone = PhoneNumber(
country: CountryData.benin,
nationalNumber: '0166640219',
);
print(phone.isValid); // true - valid Benin number
print(phone.formatted); // "01 66 64 02 19"
print(phone.international); // "+2290166640219"
Custom Validator #
Add your own validation logic:
PhoneInputField(
validator: (phone) {
if (phone == null || phone.isEmpty) {
return 'Required';
}
// Custom rule: Only mobile numbers
if (phone.country.code == 'FR' &&
!phone.nationalNumber.startsWith('6') &&
!phone.nationalNumber.startsWith('7')) {
return 'Only mobile numbers allowed';
}
if (!phone.isValid) {
return 'Invalid number';
}
return null;
},
)
π¨ Customization #
Country Button Style #
Customize the country selector button:
PhoneInputField(
countryButtonStyle: const CountryButtonStyle(
showDialCode: false, // Hide dial code
padding: EdgeInsets.all(8),
flagStyle: TextStyle(fontSize: 32), // Bigger flag
),
)
Country Selector Type #
Choose between BottomSheet, Dialog, or Page:
PhoneInputField(
countrySelectorConfig: const CountrySelectorConfig(
type: CountrySelectorType.dialog, // or .bottomSheet, .page
title: 'Choose Country',
searchHint: 'Search...',
locale: 'fr', // Show country names in French
),
)
Disable Auto-Formatting #
Show raw numbers without spaces:
PhoneInputField(
autoFormat: false,
)
Custom Decoration #
Full control over the TextField appearance:
PhoneInputField(
decoration: InputDecoration(
labelText: 'Mobile',
hintText: 'Enter your number',
prefixIcon: const Icon(Icons.phone),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
filled: true,
fillColor: Colors.grey[100],
),
)
π Available Countries #
Quick Access #
// Individual countries (52 African countries)
CountryData.benin
CountryData.nigeria
CountryData.southAfrica
CountryData.kenya
CountryData.ghana
// ... and 47 more African countries
// Non-African
CountryData.france
CountryData.usa
CountryData.canada
Regions #
- Africa: 52 countries (complete coverage)
- Europe: France, Belgium, Switzerland, Germany, Italy, Spain, Portugal, Netherlands, UnitedKingdom, Ireland,
- Americas: USA, Canada, Mexico, Brazil, Argentina, Chile, Colombia, Peru, Venezuela, DominicanRepublic
More countries will be added in future releases based on user feedback.
Search Countries #
// Search by name (English)
final results = CountryData.search('Benin');
// Search by name (French)
final results = CountryData.search('BΓ©nin', locale: 'fr');
// Get country by code
final benin = CountryData.getByCode('BJ');
// Get country by dial code
final benin = CountryData.getByDialCode('+229');
π§ API Reference #
PhoneInputField #
| Parameter | Type | Default | Description |
|---|---|---|---|
controller |
PhoneController? |
null |
Optional controller for programmatic control |
initialCountry |
Country? |
null |
Initial country (ignored if controller provided) |
countries |
List<Country> |
All countries | List of available countries |
autoDetect |
bool |
true |
Auto-detect user's country |
autoFormat |
bool |
true |
Format number as user types |
enablePasteDetection |
bool |
true |
Enable intelligent paste detection |
keyboardType |
TextInputType? |
null |
Keyboard type (auto-detected if null) |
validator |
Function? |
null |
Custom validation function |
onChanged |
Function? |
null |
Callback when number changes |
onCountryChanged |
Function? |
null |
Callback when country changes |
decoration |
InputDecoration? |
null |
TextField decoration |
enabled |
bool |
true |
Enable/disable the field |
readOnly |
bool |
false |
Make field read-only |
style |
PhoneInputStyle? |
null |
Predefined visual style |
theme |
PhoneInputTheme? |
null |
Custom theme configuration |
showCopyButton |
bool |
false |
Show copy button |
copyButtonIcon |
IconData? |
Icons.copy |
Custom copy icon |
copiedMessage |
String? |
null |
Custom copied message |
PhoneController #
| Method | Description |
|---|---|
changeCountry(Country) |
Change the selected country |
updateNumber(String) |
Update the phone number |
clear() |
Clear the number (keep country) |
reset(Country) |
Reset with new country |
| Getter | Type | Description |
|---|---|---|
country |
Country |
Current country |
nationalNumber |
String |
National number (no dial code) |
international |
String |
Full international number |
formatted |
String |
Formatted number |
isValid |
bool |
Is number valid? |
PhoneNumber #
| Property | Type | Description |
|---|---|---|
country |
Country |
The country |
nationalNumber |
String |
National number |
international |
String |
International format |
formatted |
String |
Formatted display |
isValid |
bool |
Validation status |
π€ Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
Adding More Countries #
To add support for additional countries, edit lib/src/core/data/countries_*.dart files.
π License #
This project is licensed under the MIT License - see the LICENSE file for details.
π¬ Support #
For issues, questions, or feature requests, please open an issue.
Made with β€οΈ in Benin π§π―





