phone_flag_input 0.0.1
phone_flag_input: ^0.0.1 copied to clipboard
A beautiful and customizable phone number input widget with country flag selector and international phone number formatting for Flutter.
import 'package:flutter/material.dart';
import 'package:phone_flag_input/phone_flag_input.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Phone Flag Input Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.light,
),
useMaterial3: true,
),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.dark,
),
useMaterial3: true,
),
home: const PhoneInputDemo(),
);
}
}
class PhoneInputDemo extends StatefulWidget {
const PhoneInputDemo({super.key});
@override
State<PhoneInputDemo> createState() => _PhoneInputDemoState();
}
class _PhoneInputDemoState extends State<PhoneInputDemo> {
PhoneNumber? _phoneNumber;
PhoneNumber? _customPhone;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(
title: const Text('Phone Flag Input'),
backgroundColor: theme.colorScheme.inversePrimary,
centerTitle: true,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Basic Example
_buildSection(
title: 'Basic Usage',
description: 'Simple phone input with default settings',
child: PhoneInput(
initialCountry: Country.unitedStates,
onChanged: (phone) {
setState(() {
_phoneNumber = phone;
});
},
),
),
if (_phoneNumber != null) ...[
const SizedBox(height: 16),
_buildInfoCard('Entered Phone Number', [
'Country: ${_phoneNumber!.country.name}',
'Dial Code: ${_phoneNumber!.country.dialCode}',
'Number: ${_phoneNumber!.number}',
'Full Number: ${_phoneNumber!.fullNumber}',
]),
],
const SizedBox(height: 32),
// With Initial Value
_buildSection(
title: 'With Initial Value',
description: 'Pre-filled phone number',
child: PhoneInput(
initialValue: PhoneNumber(Country.india, '9876543210'),
onChanged: (phone) => debugPrint('Phone: ${phone.fullNumber}'),
),
),
const SizedBox(height: 32),
// Custom Theme
_buildSection(
title: 'Custom Theme',
description: 'Customized appearance with larger flags',
child: PhoneInput(
initialCountry: Country.unitedKingdom,
theme: const PhoneInputTheme(
flagHeight: 24,
flagWidth: 36,
maxWidth: 250,
borderRadius: BorderRadius.all(Radius.circular(16)),
),
phoneHint: 'Enter phone number',
onChanged: (phone) {
setState(() {
_customPhone = phone;
});
},
),
),
if (_customPhone != null && _customPhone!.number.isNotEmpty) ...[
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: theme.colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Icon(Icons.check_circle, color: theme.colorScheme.primary),
const SizedBox(width: 12),
Expanded(
child: Text(
'Valid phone: ${_customPhone!.fullNumber}',
style: TextStyle(
color: theme.colorScheme.onPrimaryContainer,
fontWeight: FontWeight.w500,
),
),
),
],
),
),
],
const SizedBox(height: 32),
// Limited Countries
_buildSection(
title: 'Limited Countries',
description: 'Only show specific countries in the picker',
child: PhoneInput(
countries: const [
Country.unitedStates,
Country.canada,
Country.unitedKingdom,
Country.australia,
Country.india,
Country.germany,
Country.france,
Country.japan,
],
searchHint: 'Search from available countries...',
onChanged: (phone) => debugPrint('Phone: ${phone.fullNumber}'),
),
),
const SizedBox(height: 32),
// Disabled State
_buildSection(
title: 'Disabled State',
description: 'Read-only phone input',
child: PhoneInput(
initialValue: PhoneNumber(Country.germany, '1234567890'),
enabled: false,
onChanged: (phone) {},
),
),
const SizedBox(height: 48),
// All Countries Reference
Text(
'Available Countries',
style: theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
'The package includes ${Country.values.length} countries. Here are a few examples:',
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
),
),
const SizedBox(height: 16),
Wrap(
spacing: 8,
runSpacing: 8,
children:
Country.values.take(20).map((country) {
return Chip(
avatar: ClipRRect(
borderRadius: BorderRadius.circular(4),
child: Image.network(
'https://flagcdn.com/w40/${country.code.toLowerCase()}.png',
width: 24,
height: 16,
fit: BoxFit.cover,
errorBuilder:
(_, __, ___) => const SizedBox(width: 24),
),
),
label: Text(
'${country.name} (${country.dialCode})',
style: const TextStyle(fontSize: 12),
),
);
}).toList(),
),
const SizedBox(height: 24),
],
),
),
);
}
Widget _buildSection({
required String title,
required String description,
required Widget child,
}) {
final theme = Theme.of(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
description,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurface.withValues(alpha: 0.6),
),
),
const SizedBox(height: 16),
child,
],
);
}
Widget _buildInfoCard(String title, List<String> items) {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: theme.colorScheme.outline.withValues(alpha: 0.2),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: theme.textTheme.labelLarge?.copyWith(
fontWeight: FontWeight.bold,
color: theme.colorScheme.primary,
),
),
const SizedBox(height: 12),
...items.map(
(item) => Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Text(item, style: theme.textTheme.bodyMedium),
),
),
],
),
);
}
}