super_daily_extensions 1.1.3
super_daily_extensions: ^1.1.3 copied to clipboard
A Flutter toolkit featuring country & city pickers, form validators, universal auto-masking, custom UI widgets, and daily developer extensions.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:super_daily_extensions/super_daily_extensions.dart';
void main() {
runApp(const ExampleApp());
}
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'super_daily_extensions Showcase',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF6C63FF),
brightness: Brightness.light,
),
useMaterial3: true,
),
home: const ExampleHomeScreen(),
);
}
}
class ExampleHomeScreen extends StatefulWidget {
const ExampleHomeScreen({super.key});
@override
State<ExampleHomeScreen> createState() => _ExampleHomeScreenState();
}
class _ExampleHomeScreenState extends State<ExampleHomeScreen>
with SingleTickerProviderStateMixin {
late TabController _tabController;
// ─── Country State ───
Country? _selectedCountry;
String? _selectedCity;
String _phoneNumber = '';
// ─── Pickers State ───
DateTime? _selectedDate = DateTime.now();
TimeOfDay? _selectedTime = const TimeOfDay(hour: 10, minute: 30);
DateTime? _startDate = DateTime.now();
DateTime? _endDate = DateTime.now().add(const Duration(days: 7));
// ─── Button Loading State ───
bool _isButtonLoading = false;
// ─── Form & Validation State ───
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _confirmPasswordController = TextEditingController();
final _cnicController = TextEditingController();
final _phoneController = TextEditingController();
bool _useSimpleValidators = true;
int _selectedIdLength = 13;
String _selectedIdMask = '#####-#######-#';
String _selectedIdHint = '35202-1234567-1 (Auto-formatted)';
// ─── Sample Base64 image (small blue square png) ───
static const String _sampleBase64 =
'iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAADZJREFUeJztwTEBAAAAwqD1T20LL6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4HUAFPAAATG+yP4AAAAASUVORK5CYII=';
@override
void initState() {
super.initState();
_tabController = TabController(length: 6, vsync: this);
}
@override
void dispose() {
_tabController.dispose();
_emailController.dispose();
_passwordController.dispose();
_confirmPasswordController.dispose();
_cnicController.dispose();
_phoneController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const CustomText(
'Super Daily Extensions',
variant: CustomTextVariant.heading,
),
centerTitle: true,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(50),
child: CustomContainer(
height: 42,
margin: const EdgeInsets.only(left: 12, right: 12, bottom: 0),
padding: const EdgeInsets.all(2),
radius: 8,
color: Colors.grey.shade100,
border: Border.all(color: Colors.grey.shade300, width: 0.8),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.03),
blurRadius: 6,
offset: const Offset(0, 2),
),
],
child: TabBar(
controller: _tabController,
isScrollable: true,
tabAlignment: TabAlignment.start,
dividerColor: Colors.transparent,
indicatorSize: TabBarIndicatorSize.tab,
splashBorderRadius: BorderRadius.circular(8),
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: const Color(0xFF6C63FF),
),
labelColor: Colors.white,
unselectedLabelColor: Colors.grey.shade700,
labelStyle: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 13,
),
unselectedLabelStyle: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 13,
),
tabs: const [
Tab(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.widgets_outlined, size: 16),
SizedBox(width: 6),
Text('UI Widgets'),
],
),
),
),
Tab(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.verified_user_outlined, size: 16),
SizedBox(width: 6),
Text('Validators & Shortcuts'),
],
),
),
),
Tab(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.calendar_today_outlined, size: 16),
SizedBox(width: 6),
Text('Pickers & Media'),
],
),
),
),
Tab(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.notifications_active_outlined, size: 16),
SizedBox(width: 6),
Text('Dialogs & Alerts'),
],
),
),
),
Tab(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.public_outlined, size: 16),
SizedBox(width: 6),
Text('Countries & Cities'),
],
),
),
),
Tab(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.table_chart_outlined, size: 16),
SizedBox(width: 6),
Text('Data Table'),
],
),
),
),
],
),
),
),
),
body: TabBarView(
controller: _tabController,
children: [
_buildUIWidgetsTab(),
_buildValidatorsTab(),
_buildPickersTab(),
_buildDialogsTab(),
_buildCountryTab(),
_buildDataTableTab(),
],
),
);
}
// ══════════════════════════════════════════════════════════════════════════
// TAB 1: UI WIDGETS (Buttons, Container, Text, TextFields)
// ══════════════════════════════════════════════════════════════════════════
Widget _buildUIWidgetsTab() {
return SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ─── 1. Custom Buttons ───
CustomText(
'1. CustomButton & Variants',
fontSize: 16,
variant: CustomTextVariant.heading,
),
CustomGap(height: 8),
CustomButton.primary(
title: 'Primary Button',
icon: Icons.check_circle_outline,
onPressed: () =>
context.showCustomSnackBar('Primary button tapped!'),
),
CustomGap(height: 10),
CustomButton.outlined(
title: 'Outlined Button',
icon: Icons.refresh,
onPressed: () =>
context.showCustomSnackBar('Outlined button tapped!'),
),
CustomGap(height: 10),
CustomButton.text(
title: 'Text Button (Flat)',
icon: Icons.arrow_forward,
onPressed: () => context.showCustomSnackBar('Text button tapped!'),
),
CustomGap(height: 10),
Row(
children: [
Expanded(
child: CustomButton(
title: 'Loading Button',
isLoading: _isButtonLoading,
onPressed: () {
setState(() => _isButtonLoading = true);
Future.delayed(const Duration(seconds: 2), () {
if (mounted) setState(() => _isButtonLoading = false);
});
},
),
),
CustomGap(
width: 12,
),
Switch(
value: _isButtonLoading,
onChanged: (val) => setState(() => _isButtonLoading = val),
),
],
),
CustomGap(height: 10),
const Divider(),
CustomGap(height: 10),
// ─── 2. CustomTextField ───
CustomText(
'2. CustomTextField with Status Borders',
fontSize: 16,
variant: CustomTextVariant.heading,
),
CustomGap(height: 16),
const CustomTextField(
labelText: 'Normal Status Field',
hintText: 'Enter text here',
prefixIcon: Icon(Icons.edit_outlined),
status: TextFieldStatus.normal,
),
CustomGap(height: 10),
const CustomTextField(
labelText: 'Success Status Field',
hintText: 'user@example.com',
prefixIcon: Icon(Icons.check_circle_outline, color: Colors.green),
status: TextFieldStatus.success,
),
CustomGap(height: 10),
const CustomTextField(
labelText: 'Warning Status Field',
hintText: 'Weak password',
prefixIcon: Icon(Icons.warning_amber_outlined, color: Colors.amber),
status: TextFieldStatus.warning,
),
CustomGap(height: 10),
const CustomTextField(
labelText: 'Error Status Field',
hintText: 'Invalid phone number',
prefixIcon: Icon(Icons.error_outline, color: Colors.red),
status: TextFieldStatus.error,
),
CustomGap(height: 10),
const CustomTextField(
labelText: 'Rounded Variant',
hintText: 'Search anything...',
variant: TextFieldVariant.rounded,
prefixIcon: Icon(Icons.search),
),
CustomGap(height: 10),
const CustomTextField.password(
labelText: 'Password Field with Eye Toggle',
hintText: 'Tap eye icon to show/hide password',
),
CustomGap(height: 10),
const CustomTextField(
labelText: 'Credit Card (Universal Mask)',
hintText: '#### #### #### ####',
mask: '#### #### #### ####',
prefixIcon: Icon(Icons.credit_card_outlined),
),
CustomGap(height: 10),
const Divider(),
CustomGap(height: 10),
// ─── 3. CustomContainer ───
CustomText(
'3. CustomContainer & Widget Extensions',
fontSize: 16,
variant: CustomTextVariant.heading,
),
CustomGap(height: 16),
CustomContainer(
w: 90,
radius: 12,
gradient: const LinearGradient(
colors: [Color(0xFF6C63FF), Color(0xFF3F3D56)],
),
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const CustomText(
'CustomContainer with Gradient',
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 16,
),
CustomGap(height: 8),
const CustomText(
'Width is responsive (90% of safe screen width).',
color: Colors.white70,
fontSize: 13,
),
],
),
),
CustomGap(height: 16),
// Chained extension usage:
CustomText('Widget wrapped via .toCustomContainer() extension')
.toCustomContainer(
radius: 10,
color: Colors.deepPurple.withValues(alpha: 0.08),
padding: const EdgeInsets.all(14),
border: Border.all(color: Colors.deepPurple.withValues(alpha: 0.3)),
),
],
),
);
}
// ══════════════════════════════════════════════════════════════════════════
// TAB 2: VALIDATORS & SHORTCUTS (Forms, Regex Helpers, Keyboard & Theme)
// ══════════════════════════════════════════════════════════════════════════
Widget _buildValidatorsTab() {
return SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header / Intro Card
CustomContainer(
radius: 12,
color: const Color(0xFF6C63FF).withValues(alpha: 0.08),
border: Border.all(
color: const Color(0xFF6C63FF).withValues(alpha: 0.25),
),
padding: const EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.shield_outlined,
color: Color(0xFF6C63FF), size: 20),
const CustomSizedBox(width: 8),
const CustomText(
'Form Validation Suite',
variant: CustomTextVariant.heading,
fontSize: 15,
color: Color(0xFF6C63FF),
),
],
),
const CustomSizedBox(height: 6),
const CustomText(
'Eliminates 50+ lines of regex boilerplate. Switch mode below to see both usage styles:',
fontSize: 12,
color: Colors.black87,
),
const CustomSizedBox(height: 10),
// Toggle Mode Bar
CustomContainer(
radius: 8,
color: Colors.white,
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
border: Border.all(color: Colors.grey.shade300),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: CustomText(
_useSimpleValidators
? '1. Simple Way (FormValidators Class)'
: '2. Custom Way (String? Extension)',
fontSize: 12,
fontWeight: FontWeight.w600,
color: const Color(0xFF6C63FF),
),
),
Switch(
value: _useSimpleValidators,
activeTrackColor: const Color(0xFF6C63FF),
onChanged: (val) {
setState(() => _useSimpleValidators = val);
},
),
],
),
),
],
),
),
const CustomSizedBox(height: 14),
// Code Syntax Snippet Preview Card
CustomContainer(
radius: 8,
color: Colors.grey.shade900,
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.code,
color: Colors.greenAccent, size: 16),
const CustomSizedBox(width: 6),
CustomText(
_useSimpleValidators
? 'Simple Way: Pass ready-made validator directly'
: 'Chainable Way: Null-safe ?? chaining',
color: Colors.greenAccent,
fontSize: 12,
fontWeight: FontWeight.w600,
),
],
),
const CustomSizedBox(height: 6),
CustomText(
_useSimpleValidators
? 'validator: FormValidators.email()\nvalidator: FormValidators.password(minLength: 8)\nvalidator: FormValidators.cnic()'
: 'validator: (val) => val.validateRequired(\'Required\')\n ?? val.validateEmail(\'Invalid email\')',
color: Colors.white70,
fontSize: 11,
),
],
),
),
const CustomSizedBox(height: 16),
// 1. Email Field
CustomTextField(
controller: _emailController,
labelText: 'Email Address',
hintText: 'name@example.com',
keyboardType: TextInputType.emailAddress,
prefixIcon: const Icon(Icons.email_outlined),
validator: _useSimpleValidators
? FormValidators.email()
: (val) =>
val.validateRequired('Email is required') ??
val.validateEmail(
invalidMessage: 'Please enter a valid email'),
),
const CustomSizedBox(height: 12),
// 2. Password Field (with eye toggle)
CustomTextField.password(
controller: _passwordController,
labelText: 'Password',
hintText: 'Min 8 chars, 1 digit, 1 special char',
validator: _useSimpleValidators
? FormValidators.password(minLength: 8)
: (val) =>
val.validateRequired('Password is required') ??
val.validatePassword(minLength: 8),
),
const CustomSizedBox(height: 12),
// 3. Confirm Password Field (with eye toggle)
CustomTextField.password(
controller: _confirmPasswordController,
labelText: 'Confirm Password',
hintText: 'Re-enter your password',
prefixIcon: const Icon(Icons.lock_reset),
validator: _useSimpleValidators
? FormValidators.confirmPassword(
() => _passwordController.text,
'Passwords do not match',
)
: (val) =>
val.validateRequired('Please confirm password') ??
val.validateMatch(
_passwordController.text,
'Passwords do not match',
),
),
const CustomSizedBox(height: 12),
// 4. CNIC / National ID Field (with Country Length Switcher)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const CustomText(
'National ID Format (Any Country):',
fontSize: 12,
fontWeight: FontWeight.w600,
),
CustomText(
'$_selectedIdLength Digits',
fontSize: 11,
color: const Color(0xFF6C63FF),
fontWeight: FontWeight.bold,
),
],
),
const CustomSizedBox(height: 6),
Wrap(
spacing: 6,
children: [
ChoiceChip(
label: const Text('Pakistan (13)',
style: TextStyle(fontSize: 11)),
selected: _selectedIdLength == 13,
selectedColor: const Color(0xFF6C63FF).withValues(alpha: 0.2),
onSelected: (selected) {
if (selected) {
setState(() {
_selectedIdLength = 13;
_selectedIdMask = '#####-#######-#';
_selectedIdHint = '35202-1234567-1 (13 digits)';
_cnicController.clear();
});
}
},
),
ChoiceChip(
label:
const Text('11 Digits', style: TextStyle(fontSize: 11)),
selected: _selectedIdLength == 11,
selectedColor: const Color(0xFF6C63FF).withValues(alpha: 0.2),
onSelected: (selected) {
if (selected) {
setState(() {
_selectedIdLength = 11;
_selectedIdMask = '###-####-####';
_selectedIdHint = '123-4567-8901 (11 digits)';
_cnicController.clear();
});
}
},
),
ChoiceChip(
label: const Text('9 Digits (US SSN)',
style: TextStyle(fontSize: 11)),
selected: _selectedIdLength == 9,
selectedColor: const Color(0xFF6C63FF).withValues(alpha: 0.2),
onSelected: (selected) {
if (selected) {
setState(() {
_selectedIdLength = 9;
_selectedIdMask = '###-##-####';
_selectedIdHint = '123-45-6789 (9 digits)';
_cnicController.clear();
});
}
},
),
],
),
const CustomSizedBox(height: 8),
CustomTextField(
key: ValueKey(_selectedIdMask),
controller: _cnicController,
labelText: 'National ID / CNIC ($_selectedIdLength digits)',
hintText: _selectedIdHint,
mask: _selectedIdMask,
prefixIcon: const Icon(Icons.badge_outlined),
validator: _useSimpleValidators
? FormValidators.nationalId(length: _selectedIdLength)
: (val) =>
val.validateRequired('National ID is required') ??
val.validateNationalId(length: _selectedIdLength),
),
const CustomSizedBox(height: 12),
// 5. Phone Field
CustomTextField(
controller: _phoneController,
labelText: 'Phone Number',
hintText: '+92 300 1234567',
keyboardType: TextInputType.phone,
prefixIcon: const Icon(Icons.phone_outlined),
validator: _useSimpleValidators
? FormValidators.phone()
: (val) =>
val.validateRequired('Phone is required') ??
val.validatePhone(),
),
const CustomSizedBox(height: 16),
// Submit / Validate Button
Row(
children: [
Expanded(
flex: 3,
child: CustomButton.primary(
title: 'Validate Form',
icon: Icons.check_circle_outline,
onPressed: () {
if (_formKey.currentState!.validate()) {
context.showSuccessDialog(
title: 'Validation Passed! 🎉',
message:
'All fields are 100% valid using ${_useSimpleValidators ? "FormValidators" : "Chainable Extensions"}!',
);
} else {
context.showCustomSnackBar(
'Please fix the highlighted errors',
backgroundColor: Colors.red.shade700,
icon: Icons.error_outline,
);
}
},
),
),
const CustomSizedBox(width: 8),
Expanded(
flex: 2,
child: CustomButton.outlined(
title: 'Reset',
icon: Icons.clear_all,
onPressed: () {
_formKey.currentState?.reset();
_emailController.clear();
_passwordController.clear();
_confirmPasswordController.clear();
_cnicController.clear();
_phoneController.clear();
context.hideKeyboard();
},
),
),
],
),
const CustomSizedBox(height: 20),
const Divider(),
const CustomSizedBox(height: 14),
// ─── KEYBOARD & THEME SHORTCUTS ───
const CustomText(
'Keyboard & Theme Shortcuts',
variant: CustomTextVariant.heading,
fontSize: 16,
),
const CustomSizedBox(height: 6),
const CustomText(
'Quick access helpers directly available on BuildContext:',
fontSize: 12,
color: Colors.grey,
),
const CustomSizedBox(height: 12),
// Live Badges Row
Row(
children: [
Expanded(
child: CustomContainer(
radius: 8,
padding: const EdgeInsets.all(10),
color: Colors.grey.shade100,
border: Border.all(color: Colors.grey.shade300),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const CustomText('context.isKeyboardOpen',
fontSize: 10, color: Colors.grey),
const CustomSizedBox(height: 4),
Row(
children: [
Icon(
context.isKeyboardOpen
? Icons.check_circle
: Icons.cancel_outlined,
size: 14,
color: context.isKeyboardOpen
? Colors.green
: Colors.grey,
),
const CustomSizedBox(width: 4),
CustomText(
context.isKeyboardOpen ? 'Open' : 'Closed',
fontWeight: FontWeight.bold,
fontSize: 12,
),
],
),
],
),
),
),
const CustomSizedBox(width: 8),
Expanded(
child: CustomContainer(
radius: 8,
padding: const EdgeInsets.all(10),
color: Colors.grey.shade100,
border: Border.all(color: Colors.grey.shade300),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const CustomText('context.isDarkMode',
fontSize: 10, color: Colors.grey),
const CustomSizedBox(height: 4),
Row(
children: [
Icon(
context.isDarkMode
? Icons.dark_mode
: Icons.light_mode,
size: 14,
color: Colors.amber.shade700,
),
const CustomSizedBox(width: 4),
CustomText(
context.isDarkMode ? 'Dark' : 'Light',
fontWeight: FontWeight.bold,
fontSize: 12,
),
],
),
],
),
),
),
const CustomSizedBox(width: 8),
Expanded(
child: CustomContainer(
radius: 8,
padding: const EdgeInsets.all(10),
color: Colors.grey.shade100,
border: Border.all(color: Colors.grey.shade300),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const CustomText('context.primaryColor',
fontSize: 10, color: Colors.grey),
const CustomSizedBox(height: 4),
Row(
children: [
Container(
width: 12,
height: 12,
decoration: BoxDecoration(
color: context.primaryColor,
shape: BoxShape.circle,
),
),
const CustomSizedBox(width: 4),
const CustomText(
'Active',
fontWeight: FontWeight.bold,
fontSize: 12,
),
],
),
],
),
),
),
],
),
const CustomSizedBox(height: 12),
// Hide Keyboard Action Button
CustomButton.outlined(
title: 'Dismiss Keyboard (context.hideKeyboard())',
icon: Icons.keyboard_hide_outlined,
onPressed: () {
context.hideKeyboard();
context.showCustomSnackBar('context.hideKeyboard() called!');
},
),
const SizedBox(height: 24),
],
),
),
);
}
// ══════════════════════════════════════════════════════════════════════════
// TAB 3: PICKERS & MEDIA (Date, Time, Range, Image Preview)
// ══════════════════════════════════════════════════════════════════════════
Widget _buildPickersTab() {
return SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ─── 1. Date Picker Field ───
CustomText(
'1. CustomDatePickerField',
fontSize: 14,
variant: CustomTextVariant.heading,
),
CustomGap(height: 16),
CustomDatePickerField(
selectedDate: _selectedDate,
labelText: 'Selected Date',
dateFormat: 'dd/MM/yyyy',
onDateSelected: (date) {
setState(() => _selectedDate = date);
context
.showCustomSnackBar('Date chosen: ${date.toDisplayDate()}');
},
),
CustomGap(height: 12),
const Divider(),
CustomGap(height: 12),
// ─── 2. Time Picker Field ───
CustomText(
'2. CustomTimePickerField',
fontSize: 14,
variant: CustomTextVariant.heading,
),
CustomGap(height: 12),
CustomTimePickerField(
selectedTime: _selectedTime,
labelText: 'Selected Time',
timeFormat: 'hh:mm a',
onTimeSelected: (time) {
setState(() => _selectedTime = time);
context
.showCustomSnackBar('Time chosen: ${time.formatPattern()}');
},
),
CustomGap(height: 12),
const Divider(),
CustomGap(height: 12),
// ─── 3. Date Range Picker ───
CustomText(
'3. CustomDateRangePicker',
fontSize: 14,
variant: CustomTextVariant.heading,
),
CustomGap(height: 12),
CustomDateRangePicker(
startDate: _startDate,
endDate: _endDate,
labelText: 'Booking Date Range',
onRangeSelected: (start, end) {
setState(() {
_startDate = start;
_endDate = end;
});
context.showCustomSnackBar(
'Range: ${start?.toDisplayDate()} - ${end?.toDisplayDate()}',
);
},
),
CustomGap(height: 12),
const Divider(),
CustomGap(height: 12),
// ─── 4. Image Preview ───
CustomText(
'4. CustomImagePreview & Zoom Modal',
fontSize: 14,
variant: CustomTextVariant.heading,
),
CustomGap(height: 8),
Row(
children: [
CustomImagePreview(
base64Image: _sampleBase64,
thumbWidth: 120,
thumbHeight: 120,
label: 'Sample Thumbnail',
),
CustomGap(width: 20),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomText(
'Tap the thumbnail to test full-screen zoom, pan, and reset controls.',
fontSize: 14,
color: Colors.grey.shade700),
CustomGap(height: 8),
CustomText('Supports base64 strings and hero transitions.',
fontSize: 14, color: Colors.grey.shade700),
],
),
),
],
),
CustomGap(height: 24),
const Divider(),
CustomGap(height: 16),
],
),
);
}
// ══════════════════════════════════════════════════════════════════════════
// TAB 3: DIALOGS & ALERTS (Success, Error, Confirm, Snackbars)
// ══════════════════════════════════════════════════════════════════════════
Widget _buildDialogsTab() {
return SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomText(
'1. Animated Context Dialogs',
fontSize: 14,
variant: CustomTextVariant.heading,
),
CustomGap(height: 8),
CustomButton.primary(
title: 'Show Success Dialog',
icon: Icons.check_circle_outline,
color: Colors.green.shade700,
onPressed: () {
context.showSuccessDialog(
title: 'Order Confirmed',
message:
'Your order #84920 has been placed and confirmed successfully!',
);
},
),
CustomGap(height: 8),
CustomButton.primary(
title: 'Show Error Dialog',
icon: Icons.error_outline,
color: Colors.red.shade700,
onPressed: () {
context.showErrorDialog(
title: 'Payment Failed',
message:
'We were unable to process your card. Please try again.',
);
},
),
CustomGap(height: 8),
CustomButton.primary(
title: 'Show Warning Dialog',
icon: Icons.warning_amber_outlined,
color: Colors.orange.shade800,
onPressed: () {
context.showWarningDialog(
title: 'Storage Low',
message: 'Your device has less than 500 MB remaining space.',
);
},
),
CustomGap(height: 8),
CustomButton.outlined(
title: 'Show Confirm Dialog',
icon: Icons.help_outline,
onPressed: () {
context.showConfirmDialog(
title: 'Delete Account?',
message: 'This will permanently remove all your data. Proceed?',
isDanger: true,
confirmText: 'Delete',
cancelText: 'Cancel',
onConfirm: () {
context.showCustomSnackBar('Item deleted successfully.');
},
);
},
),
CustomGap(height: 12),
const Divider(),
CustomGap(height: 12),
// ─── Floating Snackbars ───
CustomText(
'2. Custom Floating SnackBars',
fontSize: 14,
variant: CustomTextVariant.heading,
),
CustomGap(height: 8),
CustomButton(
title: 'Show Standard SnackBar',
icon: Icons.chat_bubble_outline,
onPressed: () {
context.showCustomSnackBar(
'This is a modern floating CustomSnackBar notification!',
);
},
),
CustomGap(height: 8),
CustomButton(
title: 'Show Success SnackBar',
icon: Icons.check,
color: Colors.green.shade700,
onPressed: () {
CustomSnackBar.showSuccess(
context,
'File uploaded successfully!',
);
},
),
CustomGap(height: 8),
CustomButton(
title: 'Show Error SnackBar',
icon: Icons.close,
color: Colors.red.shade700,
onPressed: () {
CustomSnackBar.showError(
context,
'Connection lost. Please reconnect.',
);
},
),
CustomGap(height: 8),
CustomButton(
title: 'Show Warning SnackBar',
icon: Icons.warning_amber,
color: Colors.orange.shade800,
onPressed: () {
CustomSnackBar.showWarning(
context,
'Unsaved changes may be lost.',
);
},
),
CustomGap(height: 8),
],
),
);
}
// ══════════════════════════════════════════════════════════════════════════
// TAB 4: COUNTRIES & CITIES
// ══════════════════════════════════════════════════════════════
Widget _buildCountryTab() {
return SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CountryDropdown(
selectedCountry: _selectedCountry,
showPrefixIcon: false,
onChanged: (country) {
setState(() {
_selectedCountry = country;
_selectedCity = null;
});
},
),
CustomGap(height: 10),
CityDropdown(
country: _selectedCountry,
selectedCity: _selectedCity,
showPrefixIcon: false,
onChanged: (city) {
setState(() => _selectedCity = city);
},
),
CustomGap(height: 10),
CountryPhoneField(
selectedCountry: _selectedCountry,
onPhoneChanged: (fullPhone, rawNumber, dialCode) {
setState(() => _phoneNumber = fullPhone);
},
),
CustomGap(height: 20),
if (_selectedCountry != null || _selectedCity != null) ...[
CustomContainer(
w: 100,
padding: const EdgeInsets.all(16),
radius: 12,
color: Colors.blue.withValues(alpha: 0.08),
border: Border.all(color: Colors.blue.withValues(alpha: 0.2)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomText('Selection Details:',
fontSize: 16, fontWeight: FontWeight.bold),
8.h,
if (_selectedCountry != null)
CustomText(
'Country: ${_selectedCountry!.flag} ${_selectedCountry!.name} (${_selectedCountry!.dialCode})',
fontSize: 14,
color: Colors.grey.shade700),
if (_selectedCity != null)
CustomText('City: $_selectedCity',
fontSize: 14, color: Colors.grey.shade700),
if (_phoneNumber.isNotEmpty)
CustomText('Phone: $_phoneNumber',
fontSize: 14, color: Colors.grey.shade700),
CustomGap(height: 8),
if (_selectedCity != null)
CustomText(
'Is "$_selectedCity" in Pakistan? ${_selectedCity!.isCityOf('PK')}',
fontSize: 14,
color: Colors.indigo,
),
],
),
),
CustomGap(height: 20),
],
],
),
);
}
// ══════════════════════════════════════════════════════════════════════════
// TAB 5: PROFESSIONAL DATA TABLE
// ══════════════════════════════════════════════════════════════════════════
Widget _buildDataTableTab() {
final columns = [
DataTableColumn(label: 'ID', field: 'id', sortable: true),
DataTableColumn(label: 'Name', field: 'name', sortable: true),
DataTableColumn(label: 'Role', field: 'role', sortable: true),
DataTableColumn(label: 'Country', field: 'country'),
DataTableColumn(label: 'Joined', field: 'joined', isDate: true),
DataTableColumn(label: 'Active', field: 'active'),
];
final data = [
{
'id': '101',
'name': 'Ali Khan',
'role': 'Senior Flutter Dev',
'country': '🇵🇰 Pakistan',
'joined': '2023-01-15',
'active': true,
},
{
'id': '102',
'name': 'Sara Ahmed',
'role': 'UI/UX Designer',
'country': '🇦🇪 UAE',
'joined': '2023-06-20',
'active': true,
},
{
'id': '103',
'name': 'John Doe',
'role': 'Backend Engineer',
'country': '🇺🇸 USA',
'joined': '2024-02-10',
'active': false,
},
{
'id': '104',
'name': 'Fatima Noor',
'role': 'Product Manager',
'country': '🇬🇧 UK',
'joined': '2024-05-01',
'active': true,
},
];
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomText(
'ProfessionalDataTable Demo',
fontSize: 20,
fontWeight: FontWeight.bold,
),
CustomGap(height: 8),
Expanded(
child: ProfessionalDataTable(
columns: columns,
data: data,
buildActions: (row) => [
IconButton(
icon: const Icon(Icons.edit, size: 20, color: Colors.blue),
tooltip: 'Edit ${row['name']}',
onPressed: () {
context.showCustomSnackBar('Editing ${row['name']}');
},
),
IconButton(
icon: const Icon(Icons.delete_outline,
size: 20, color: Colors.red),
tooltip: 'Delete ${row['name']}',
onPressed: () {
context.showConfirmDialog(
title: 'Delete ${row['name']}?',
message: 'Are you sure you want to delete this row?',
isDanger: true,
onConfirm: () {
context.showCustomSnackBar('${row['name']} deleted.');
},
);
},
),
],
),
),
],
),
);
}
}