countrify 1.0.4
countrify: ^1.0.4 copied to clipboard
A beautiful, customizable country picker for Flutter. 245+ countries, 5 display modes, rich theming, flag images, phone codes, 40+ utilities. Zero dependencies.
example/lib/main.dart
import 'package:countrify/countrify.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(const MyApp());
}
// ─── Codeable Brand Colors (gocodeable.com) ───────────────────────────
class CodeableColors {
CodeableColors._();
// ── Primary Blue (main brand color) ──
static const Color blue = Color(0xFF3B82F6);
static const Color blueDark = Color(0xFF1D4ED8);
static const Color blueLight = Color(0xFF60A5FA);
static const Color blueSubtle = Color(0xFFDBEAFE);
static const Color blueFaint = Color(0xFFEFF6FF);
// ── Secondary Green (accent) ──
static const Color green = Color(0xFF22C55E);
static const Color greenDark = Color(0xFF16A34A);
static const Color greenLight = Color(0xFF4ADE80);
static const Color greenSubtle = Color(0xFFDCFCE7);
static const Color greenFaint = Color(0xFFF0FDF4);
// ── Light Theme ──
static const Color lightBg = Color(0xFFFFFFFF);
static const Color lightSurface = Color(0xFFF8FAFC);
static const Color lightSurfaceAlt = Color(0xFFF1F5F9);
static const Color lightText = Color(0xFF111827);
static const Color lightTextSecondary = Color(0xFF6B7280);
static const Color lightTextMuted = Color(0xFF9CA3AF);
static const Color lightBorder = Color(0xFFE2E8F0);
static const Color lightBorderStrong = Color(0xFFCBD5E1);
static const Color lightDivider = Color(0xFFE5E7EB);
// ── Dark Theme ──
static const Color darkBg = Color(0xFF0B0B0F);
static const Color darkSurface = Color(0xFF161619);
static const Color darkSurfaceLight = Color(0xFF1E1E22);
static const Color darkElevated = Color(0xFF222228);
static const Color darkText = Color(0xFFFFFFFF);
static const Color darkTextSecondary = Color(0xFF9CA3AF);
static const Color darkTextMuted = Color(0xFF6B7280);
static const Color darkBorder = Color(0xFF2D2D33);
static const Color darkBorderLight = Color(0xFF3F3F46);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Countrify Example',
debugShowCheckedModeBanner: false,
theme: ThemeData(
brightness: Brightness.light,
scaffoldBackgroundColor: CodeableColors.lightBg,
colorScheme: const ColorScheme.light(
primary: CodeableColors.blue,
onPrimary: Colors.white,
secondary: CodeableColors.green,
onSecondary: Colors.white,
surface: CodeableColors.lightSurface,
onSurface: CodeableColors.lightText,
),
appBarTheme: const AppBarTheme(
backgroundColor: CodeableColors.blue,
foregroundColor: Colors.white,
elevation: 0,
centerTitle: true,
),
useMaterial3: true,
),
home: const MyHomePage(title: 'Countrify Example'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Country? _selectedCountry;
String _phoneNumber = '';
Country? _phoneCountry;
// ─── Light theme (Codeable Blue on white) ─────────────────────────
CountryPickerTheme get _codeableLightTheme => const CountryPickerTheme(
backgroundColor: CodeableColors.lightBg,
headerColor: CodeableColors.blueFaint,
headerTextStyle: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: CodeableColors.lightText,
),
headerIconColor: CodeableColors.lightTextSecondary,
searchBarColor: CodeableColors.lightSurface,
searchTextStyle: TextStyle(
fontSize: 16,
color: CodeableColors.lightText,
),
searchHintStyle: TextStyle(
fontSize: 16,
color: CodeableColors.lightTextMuted,
),
searchIconColor: CodeableColors.lightTextMuted,
searchBarBorderColor: CodeableColors.lightBorder,
searchBarBorderRadius: BorderRadius.all(Radius.circular(12)),
filterBackgroundColor: CodeableColors.lightSurfaceAlt,
filterSelectedColor: CodeableColors.blue,
filterTextColor: CodeableColors.lightText,
filterSelectedTextColor: Colors.white,
filterCheckmarkColor: Colors.white,
filterIconColor: CodeableColors.lightTextSecondary,
filterTextStyle: TextStyle(
fontSize: 14,
color: CodeableColors.lightText,
),
countryItemBackgroundColor: CodeableColors.lightBg,
countryItemSelectedColor: CodeableColors.blueSubtle,
countryItemSelectedBorderColor: CodeableColors.blue,
countryItemSelectedIconColor: CodeableColors.blue,
countryItemBorderRadius: BorderRadius.all(Radius.circular(10)),
countryNameTextStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: CodeableColors.lightText,
),
countrySubtitleTextStyle: TextStyle(
fontSize: 14,
color: CodeableColors.lightTextSecondary,
),
borderColor: CodeableColors.lightBorder,
borderRadius: BorderRadius.all(Radius.circular(20)),
scrollbarThickness: 4.0,
scrollbarRadius: BorderRadius.all(Radius.circular(2)),
shadowColor: Color(0x1A3B82F6),
elevation: 8.0,
animationDuration: Duration(milliseconds: 300),
hapticFeedback: true,
dropdownMenuBackgroundColor: CodeableColors.lightBg,
dropdownMenuElevation: 8,
dropdownMenuBorderRadius: BorderRadius.all(Radius.circular(12)),
dropdownMenuBorderColor: CodeableColors.lightBorder,
dropdownMenuBorderWidth: 1,
);
// ─── Dark theme (Codeable Blue on dark) ───────────────────────────
CountryPickerTheme get _codeableDarkTheme => const CountryPickerTheme(
backgroundColor: CodeableColors.darkSurface,
headerColor: CodeableColors.darkSurfaceLight,
headerTextStyle: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: CodeableColors.darkText,
),
headerIconColor: CodeableColors.darkTextSecondary,
searchBarColor: CodeableColors.darkElevated,
searchTextStyle: TextStyle(
fontSize: 16,
color: CodeableColors.darkText,
),
searchHintStyle: TextStyle(
fontSize: 16,
color: CodeableColors.darkTextMuted,
),
searchIconColor: CodeableColors.darkTextMuted,
searchBarBorderColor: CodeableColors.darkBorder,
searchBarBorderRadius: BorderRadius.all(Radius.circular(12)),
filterBackgroundColor: CodeableColors.darkElevated,
filterSelectedColor: CodeableColors.blueLight,
filterTextColor: CodeableColors.darkTextSecondary,
filterSelectedTextColor: Color(0xFF0B0B0F),
filterCheckmarkColor: Color(0xFF0B0B0F),
filterIconColor: CodeableColors.darkTextMuted,
filterTextStyle: TextStyle(
fontSize: 14,
color: CodeableColors.darkTextSecondary,
),
countryItemBackgroundColor: CodeableColors.darkSurface,
countryItemSelectedColor: Color(0xFF1A2744),
countryItemSelectedBorderColor: CodeableColors.blueLight,
countryItemSelectedIconColor: CodeableColors.blueLight,
countryItemBorderRadius: BorderRadius.all(Radius.circular(10)),
countryNameTextStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: CodeableColors.darkText,
),
countrySubtitleTextStyle: TextStyle(
fontSize: 14,
color: CodeableColors.darkTextSecondary,
),
borderColor: CodeableColors.darkBorder,
borderRadius: BorderRadius.all(Radius.circular(20)),
scrollbarThickness: 4.0,
scrollbarRadius: BorderRadius.all(Radius.circular(2)),
shadowColor: Color(0x333B82F6),
elevation: 12.0,
animationDuration: Duration(milliseconds: 300),
hapticFeedback: true,
dropdownMenuBackgroundColor: CodeableColors.darkSurfaceLight,
dropdownMenuElevation: 12,
dropdownMenuBorderRadius: BorderRadius.all(Radius.circular(12)),
dropdownMenuBorderColor: CodeableColors.darkBorder,
dropdownMenuBorderWidth: 1,
);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 8,
height: 8,
decoration: const BoxDecoration(
color: CodeableColors.green,
shape: BoxShape.circle,
),
),
const SizedBox(width: 8),
Text(
widget.title,
style: const TextStyle(
fontWeight: FontWeight.w600,
letterSpacing: -0.3,
),
),
],
),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
// Hero title
const Text(
'Beautiful Country\nPicker Examples',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.w700,
color: CodeableColors.lightText,
height: 1.2,
letterSpacing: -0.5,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 6),
const Text(
'Powered by Codeable',
style: TextStyle(
fontSize: 14,
color: CodeableColors.blue,
fontWeight: FontWeight.w500,
letterSpacing: 0.5,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
// Selected country display
if (_selectedCountry != null) ...[
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: CodeableColors.blueFaint,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: CodeableColors.blue.withOpacity(0.2),
),
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 6,
height: 6,
decoration: const BoxDecoration(
color: CodeableColors.green,
shape: BoxShape.circle,
),
),
const SizedBox(width: 8),
const Text(
'SELECTED COUNTRY',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: CodeableColors.blue,
letterSpacing: 1.2,
),
),
],
),
const SizedBox(height: 12),
Text(
_selectedCountry!.name,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.w700,
color: CodeableColors.lightText,
),
),
const SizedBox(height: 12),
Wrap(
spacing: 12,
runSpacing: 8,
alignment: WrapAlignment.center,
children: [
_buildInfoChip('Code', _selectedCountry!.alpha2Code),
_buildInfoChip('Capital', _selectedCountry!.capital),
_buildInfoChip('Region', _selectedCountry!.region),
_buildInfoChip(
'Population',
CountryUtils.formatPopulation(
_selectedCountry!.population),
),
],
),
],
),
),
const SizedBox(height: 24),
],
// ─── Dropdown Picker ────────────────────────────────────
_buildSectionTitle('Dropdown Picker'),
const SizedBox(height: 4),
const Text(
'Select a country using the dropdown widget:',
style: TextStyle(
fontSize: 14, color: CodeableColors.lightTextSecondary),
),
const SizedBox(height: 12),
ModalComprehensivePicker.dropdown(
initialCountry: _selectedCountry,
onCountrySelected: _updateSelectedCountry,
onCountryChanged: _updateSelectedCountry,
showPhoneCode: false,
showFlag: true,
showCountryName: true,
theme: _codeableLightTheme,
),
const SizedBox(height: 28),
// ─── Country Dropdown Field ───────────────────────────
_buildSectionTitle('Country Dropdown Field'),
const SizedBox(height: 4),
const Text(
'A form-friendly field that opens a picker on tap:',
style: TextStyle(
fontSize: 14, color: CodeableColors.lightTextSecondary),
),
const SizedBox(height: 12),
CountryDropdownField(
initialCountry: _selectedCountry,
onCountrySelected: _updateSelectedCountry,
onCountryChanged: _updateSelectedCountry,
hintText: 'Select a country',
showPhoneCode: false,
showFlag: true,
searchEnabled: true,
pickerType: PickerDisplayType.bottomSheet,
theme: _codeableLightTheme,
),
const SizedBox(height: 28),
// ─── Basic Pickers ──────────────────────────────────────
_buildSectionTitle('Basic Pickers'),
const SizedBox(height: 8),
_buildBlueButton(
label: 'Bottom Sheet Picker',
icon: CountrifyIcons.smartphone,
onPressed: _showBottomSheetPicker,
),
const SizedBox(height: 10),
_buildOutlinedBlueButton(
label: 'Dialog Picker',
icon: CountrifyIcons.messageSquare,
onPressed: _showDialogPicker,
),
const SizedBox(height: 10),
_buildOutlinedBlueButton(
label: 'Full Screen Picker',
icon: CountrifyIcons.maximize,
onPressed: _showFullScreenPicker,
),
const SizedBox(height: 10),
_buildOutlinedBlueButton(
label: 'Customized Picker',
icon: CountrifyIcons.palette,
onPressed: _showCustomizedPicker,
),
const SizedBox(height: 10),
_buildOutlinedBlueButton(
label: 'Filtered Picker (Europe)',
icon: CountrifyIcons.globe,
onPressed: _showFilteredPicker,
),
const SizedBox(height: 28),
// ─── Comprehensive Pickers ──────────────────────────────
_buildSectionTitle('Comprehensive Pickers'),
const SizedBox(height: 8),
_buildBlueButton(
label: 'Comprehensive Bottom Sheet',
icon: CountrifyIcons.rocket,
onPressed: _showComprehensiveBottomSheet,
),
const SizedBox(height: 10),
_buildBlueButton(
label: 'Comprehensive Dialog',
icon: CountrifyIcons.messageCircle,
onPressed: _showComprehensiveDialog,
),
const SizedBox(height: 10),
_buildBlueButton(
label: 'Comprehensive Full Screen',
icon: CountrifyIcons.expand,
onPressed: _showComprehensiveFullScreen,
),
const SizedBox(height: 10),
_buildGreenButton(
label: 'Phone Code Picker',
icon: CountrifyIcons.phone,
onPressed: _showPhoneCodePicker,
),
const SizedBox(height: 28),
// ─── Phone Number Field ─────────────────────────────────
_buildSectionTitle('Phone Number Field'),
const SizedBox(height: 4),
const Text(
'Inline dropdown to pick country code:',
style: TextStyle(
fontSize: 14, color: CodeableColors.lightTextSecondary),
),
const SizedBox(height: 12),
PhoneNumberField(
hintText: 'Enter phone number',
labelText: 'Phone',
theme: _codeableLightTheme,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(15),
],
onPhoneNumberChanged: (phoneNumber, country) {
setState(() {
_phoneNumber = phoneNumber;
_phoneCountry = country;
});
},
onCountryChanged: (country) {
setState(() {
_phoneCountry = country;
});
},
),
if (_phoneNumber.isNotEmpty && _phoneCountry != null) ...[
const SizedBox(height: 12),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: CodeableColors.greenFaint,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: CodeableColors.green.withValues(alpha: 0.3),
),
),
child: Row(
children: [
const Icon(
CountrifyIcons.phone,
size: 18,
color: CodeableColors.green,
),
const SizedBox(width: 10),
Expanded(
child: Text(
'+${_phoneCountry!.callingCodes.first} $_phoneNumber',
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: CodeableColors.lightText,
letterSpacing: 0.5,
),
),
),
],
),
),
],
const SizedBox(height: 16),
// Custom styled variant
const Text(
'With custom styling and larger flags:',
style: TextStyle(
fontSize: 14, color: CodeableColors.lightTextSecondary),
),
const SizedBox(height: 12),
PhoneNumberField(
hintText: 'Phone number',
theme: _codeableLightTheme,
showDropdownIcon: true,
flagSize: const Size(28, 20),
fieldBorderRadius: BorderRadius.circular(16),
dropdownMaxHeight: 300,
dialCodeTextStyle: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w700,
color: CodeableColors.blue,
),
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
maxLength: 12,
onPhoneNumberChanged: (phoneNumber, country) {},
),
const SizedBox(height: 28),
// ─── Flag Customization ─────────────────────────────────
_buildSectionTitle('Flag Customization'),
const SizedBox(height: 8),
_buildOutlinedBlueButton(
label: 'Circular Flags',
icon: CountrifyIcons.circle,
onPressed: _showCircularFlagsPicker,
),
const SizedBox(height: 10),
_buildOutlinedBlueButton(
label: 'Rounded Flags',
icon: CountrifyIcons.squareRoundCorner,
onPressed: _showRoundedFlagsPicker,
),
const SizedBox(height: 10),
_buildOutlinedBlueButton(
label: 'Shadow Flags',
icon: CountrifyIcons.sparkles,
onPressed: _showShadowFlagsPicker,
),
const SizedBox(height: 28),
// ─── Theme Examples ─────────────────────────────────────
_buildSectionTitle('Theme Examples'),
const SizedBox(height: 8),
_buildDarkButton(
label: 'Dark Theme',
icon: CountrifyIcons.moon,
onPressed: _showDarkThemePicker,
),
const SizedBox(height: 10),
_buildGreenButton(
label: 'Custom Color Theme',
icon: CountrifyIcons.paintbrush,
onPressed: _showCustomColorThemePicker,
),
const SizedBox(height: 28),
// ─── Data & Utilities ───────────────────────────────────
_buildSectionTitle('Data & Utilities'),
const SizedBox(height: 8),
_buildOutlinedBlueButton(
label: 'Country Data Examples',
icon: CountrifyIcons.chartBar,
onPressed: _showCountryData,
),
const SizedBox(height: 10),
_buildOutlinedBlueButton(
label: 'Country Statistics',
icon: CountrifyIcons.chartLine,
onPressed: _showCountryStatistics,
),
const SizedBox(height: 40),
],
),
),
);
}
// ─── Info Chip (light style) ──────────────────────────────────────
Widget _buildInfoChip(String label, String value) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: CodeableColors.lightBg,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: CodeableColors.lightBorder),
),
child: Column(
children: [
Text(
label,
style: const TextStyle(
fontSize: 10,
fontWeight: FontWeight.w600,
color: CodeableColors.lightTextMuted,
letterSpacing: 0.5,
),
),
const SizedBox(height: 2),
Text(
value,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: CodeableColors.lightText,
),
),
],
),
);
}
// ─── Section Title (blue accent bar) ──────────────────────────────
Widget _buildSectionTitle(String title) {
return Row(
children: [
Container(
width: 3,
height: 20,
decoration: BoxDecoration(
color: CodeableColors.blue,
borderRadius: BorderRadius.circular(2),
),
),
const SizedBox(width: 10),
Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
color: CodeableColors.lightText,
letterSpacing: -0.3,
),
),
],
);
}
// ─── Blue filled button (primary CTA) ─────────────────────────────
Widget _buildBlueButton({
required String label,
required IconData icon,
required VoidCallback onPressed,
}) {
return SizedBox(
height: 52,
child: ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: CodeableColors.blue,
foregroundColor: Colors.white,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, size: 20),
const SizedBox(width: 10),
Text(
label,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
letterSpacing: -0.2,
),
),
],
),
),
);
}
// ─── Blue outlined button (secondary) ─────────────────────────────
Widget _buildOutlinedBlueButton({
required String label,
required IconData icon,
required VoidCallback onPressed,
}) {
return SizedBox(
height: 52,
child: OutlinedButton(
onPressed: onPressed,
style: OutlinedButton.styleFrom(
foregroundColor: CodeableColors.blue,
side: const BorderSide(color: CodeableColors.lightBorderStrong),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, size: 20, color: CodeableColors.blue),
const SizedBox(width: 10),
Text(
label,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: CodeableColors.lightText,
letterSpacing: -0.2,
),
),
],
),
),
);
}
// ─── Green filled button (accent) ─────────────────────────────────
Widget _buildGreenButton({
required String label,
required IconData icon,
required VoidCallback onPressed,
}) {
return SizedBox(
height: 52,
child: ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: CodeableColors.green,
foregroundColor: Colors.white,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, size: 20),
const SizedBox(width: 10),
Text(
label,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
letterSpacing: -0.2,
),
),
],
),
),
);
}
// ─── Dark button (for dark theme demo) ────────────────────────────
Widget _buildDarkButton({
required String label,
required IconData icon,
required VoidCallback onPressed,
}) {
return SizedBox(
height: 52,
child: ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: CodeableColors.darkSurface,
foregroundColor: CodeableColors.darkText,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, size: 20, color: CodeableColors.blueLight),
const SizedBox(width: 10),
Text(
label,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
letterSpacing: -0.2,
),
),
],
),
),
);
}
void _updateSelectedCountry(Country? country) {
if (country != null) {
setState(() {
_selectedCountry = country;
});
}
}
// ─── Basic Picker Methods ─────────────────────────────────────────
void _showBottomSheetPicker() async {
final country = await ModalCountryPicker.showBottomSheet(
context: context,
initialCountry: _selectedCountry,
);
_updateSelectedCountry(country);
}
void _showDialogPicker() async {
final country = await ModalCountryPicker.showDialogPicker(
context: context,
initialCountry: _selectedCountry,
);
_updateSelectedCountry(country);
}
void _showFullScreenPicker() async {
final country = await ModalCountryPicker.showFullScreen(
context: context,
initialCountry: _selectedCountry,
);
_updateSelectedCountry(country);
}
void _showCustomizedPicker() async {
final country = await ModalCountryPicker.showBottomSheet(
context: context,
initialCountry: _selectedCountry,
);
_updateSelectedCountry(country);
}
void _showFilteredPicker() async {
final country = await ModalComprehensivePicker.showBottomSheet(
context: context,
initialCountry: _selectedCountry,
config: const CountryPickerConfig(
includeRegions: ['Europe'],
),
showPhoneCode: true,
searchEnabled: true,
theme: _codeableLightTheme,
);
_updateSelectedCountry(country);
}
// ─── Comprehensive Picker Methods ─────────────────────────────────
void _showComprehensiveBottomSheet() async {
final country = await ModalComprehensivePicker.showBottomSheet(
context: context,
initialCountry: _selectedCountry,
showPhoneCode: true,
searchEnabled: true,
theme: _codeableLightTheme,
);
_updateSelectedCountry(country);
}
void _showComprehensiveDialog() async {
final country = await ModalComprehensivePicker.showDialog(
context: context,
initialCountry: _selectedCountry,
showPhoneCode: true,
searchEnabled: true,
theme: _codeableLightTheme,
);
_updateSelectedCountry(country);
}
void _showComprehensiveFullScreen() async {
final country = await ModalComprehensivePicker.showFullScreen(
context: context,
initialCountry: _selectedCountry,
showPhoneCode: true,
searchEnabled: true,
theme: _codeableLightTheme,
);
_updateSelectedCountry(country);
}
void _showPhoneCodePicker() async {
final country = await ModalComprehensivePicker.showBottomSheet(
context: context,
initialCountry: _selectedCountry,
showPhoneCode: true,
searchEnabled: true,
theme: _codeableLightTheme,
);
_updateSelectedCountry(country);
}
// ─── Flag Customization Methods ───────────────────────────────────
void _showCircularFlagsPicker() async {
final country = await ModalComprehensivePicker.showBottomSheet(
context: context,
initialCountry: _selectedCountry,
showPhoneCode: true,
searchEnabled: true,
theme: _codeableLightTheme,
config: const CountryPickerConfig(
flagShape: FlagShape.circular,
flagSize: Size(40, 40),
),
);
_updateSelectedCountry(country);
}
void _showRoundedFlagsPicker() async {
final country = await ModalComprehensivePicker.showBottomSheet(
context: context,
initialCountry: _selectedCountry,
showPhoneCode: true,
searchEnabled: true,
theme: _codeableLightTheme,
config: const CountryPickerConfig(
flagShape: FlagShape.rounded,
flagSize: Size(40, 28),
),
);
_updateSelectedCountry(country);
}
void _showShadowFlagsPicker() async {
final country = await ModalComprehensivePicker.showBottomSheet(
context: context,
initialCountry: _selectedCountry,
showPhoneCode: true,
searchEnabled: true,
theme: _codeableLightTheme,
config: const CountryPickerConfig(
flagShape: FlagShape.rectangular,
flagSize: Size(42, 30),
flagBorderRadius: BorderRadius.all(Radius.circular(6)),
flagShadowColor: Color(0x333B82F6),
flagShadowBlur: 8,
flagShadowOffset: Offset(0, 3),
),
);
_updateSelectedCountry(country);
}
// ─── Theme Methods ────────────────────────────────────────────────
void _showDarkThemePicker() async {
final country = await ModalComprehensivePicker.showBottomSheet(
context: context,
initialCountry: _selectedCountry,
showPhoneCode: true,
searchEnabled: true,
theme: _codeableDarkTheme,
);
_updateSelectedCountry(country);
}
void _showCustomColorThemePicker() async {
final country = await ModalComprehensivePicker.showBottomSheet(
context: context,
initialCountry: _selectedCountry,
showPhoneCode: true,
searchEnabled: true,
theme: CountryPickerTheme.custom(
primaryColor: CodeableColors.green,
backgroundColor: CodeableColors.greenFaint,
surfaceColor: CodeableColors.greenSubtle,
onSurfaceColor: CodeableColors.lightText,
isDark: false,
),
);
_updateSelectedCountry(country);
}
// ─── Data Methods ─────────────────────────────────────────────────
void _showCountryData() {
showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: CodeableColors.lightBg,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
title: const Text(
'Country Data Examples',
style: TextStyle(
color: CodeableColors.lightText,
fontWeight: FontWeight.w700,
),
),
content: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
_dialogInfoRow('Total Countries',
'${CountryUtils.getAllCountries().length}'),
_dialogInfoRow(
'Regions', CountryUtils.getAllRegions().join(', ')),
_dialogInfoRow('Most Populous',
CountryUtils.getMostPopulousCountry()?.name ?? 'N/A'),
_dialogInfoRow('Largest by Area',
CountryUtils.getLargestCountry()?.name ?? 'N/A'),
_dialogInfoRow('European Countries',
'${CountryUtils.getCountriesByRegion('Europe').length}'),
_dialogInfoRow('Asian Countries',
'${CountryUtils.getCountriesByRegion('Asia').length}'),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
style: TextButton.styleFrom(
foregroundColor: CodeableColors.blue,
),
child: const Text('Close'),
),
],
),
);
}
void _showCountryStatistics() {
final countries = CountryUtils.getAllCountries();
final totalPopulation =
countries.fold<int>(0, (sum, country) => sum + country.population);
final totalArea =
countries.fold<double>(0, (sum, country) => sum + country.area);
showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: CodeableColors.lightBg,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
title: const Text(
'Country Statistics',
style: TextStyle(
color: CodeableColors.lightText,
fontWeight: FontWeight.w700,
),
),
content: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
_dialogInfoRow('Total Countries', '${countries.length}'),
_dialogInfoRow('Total Population',
CountryUtils.formatPopulation(totalPopulation)),
_dialogInfoRow('Total Area', CountryUtils.formatArea(totalArea)),
_dialogInfoRow(
'Avg Population',
CountryUtils.formatPopulation(
totalPopulation ~/ countries.length)),
_dialogInfoRow('Avg Area',
CountryUtils.formatArea(totalArea / countries.length)),
_dialogInfoRow(
'Regions', '${CountryUtils.getAllRegions().length}'),
_dialogInfoRow('With Phone Codes',
'${countries.where((c) => c.callingCodes.isNotEmpty).length}'),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
style: TextButton.styleFrom(
foregroundColor: CodeableColors.blue,
),
child: const Text('Close'),
),
],
),
);
}
Widget _dialogInfoRow(String label, String value) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 4,
height: 4,
margin: const EdgeInsets.only(top: 7),
decoration: const BoxDecoration(
color: CodeableColors.blue,
shape: BoxShape.circle,
),
),
const SizedBox(width: 8),
Expanded(
child: RichText(
text: TextSpan(
children: [
TextSpan(
text: '$label: ',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: CodeableColors.lightTextSecondary,
),
),
TextSpan(
text: value,
style: const TextStyle(
fontSize: 14,
color: CodeableColors.lightText,
),
),
],
),
),
),
],
),
);
}
}