Refactor contacts using flutter_contacts, unify InteractiveScale animations and 16px corner radius across welcome and setup complete screens, and compile final release APK

This commit is contained in:
Sridhar R 2026-06-10 14:47:50 +05:30
parent ac1294a150
commit 3f2a804a31
12 changed files with 1580 additions and 1256 deletions

View File

@ -11,6 +11,28 @@ rootProject.layout.buildDirectory.value(newBuildDir)
subprojects { subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir) project.layout.buildDirectory.value(newSubprojectBuildDir)
afterEvaluate {
if (extensions.findByName("android") != null) {
val android = extensions.findByName("android") as com.android.build.gradle.BaseExtension
if (android.namespace == null) {
var manifestPackage: String? = null
val manifestFile = project.projectDir.resolve("src/main/AndroidManifest.xml")
if (manifestFile.exists()) {
val content = manifestFile.readText()
val match = Regex("package=\"([^\"]+)\"").find(content)
if (match != null) {
manifestPackage = match.groupValues[1]
}
}
if (manifestPackage != null) {
android.namespace = manifestPackage
} else {
android.namespace = "co.gittouch." + project.name.replace("-", "_")
}
}
}
}
} }
subprojects { subprojects {
project.evaluationDependsOn(":app") project.evaluationDependsOn(":app")

View File

@ -186,3 +186,119 @@ class _TopAlertWidgetState extends State<_TopAlertWidget> with SingleTickerProvi
); );
} }
} }
class SlideFadeEntrance extends StatefulWidget {
final Widget child;
final Duration delay;
final Duration duration;
const SlideFadeEntrance({
super.key,
required this.child,
this.delay = Duration.zero,
this.duration = const Duration(milliseconds: 500),
});
@override
State<SlideFadeEntrance> createState() => _SlideFadeEntranceState();
}
class _SlideFadeEntranceState extends State<SlideFadeEntrance> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _opacityAnimation;
late Animation<double> _slideAnimation;
Timer? _timer;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: widget.duration,
);
_opacityAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeIn),
);
_slideAnimation = Tween<double>(begin: 12.0, end: 0.0).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic),
);
if (widget.delay == Duration.zero) {
_controller.forward();
} else {
_timer = Timer(widget.delay, () {
if (mounted) {
_controller.forward();
}
});
}
}
@override
void dispose() {
_timer?.cancel();
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Opacity(
opacity: _opacityAnimation.value,
child: Transform.translate(
offset: Offset(0.0, _slideAnimation.value),
child: child,
),
);
},
child: widget.child,
);
}
}
class InteractiveScale extends StatefulWidget {
final Widget child;
final VoidCallback? onTap;
final double hoverScale;
final double tapScale;
const InteractiveScale({
super.key,
required this.child,
this.onTap,
this.hoverScale = 1.03,
this.tapScale = 0.96,
});
@override
State<InteractiveScale> createState() => _InteractiveScaleState();
}
class _InteractiveScaleState extends State<InteractiveScale> {
double _scale = 1.0;
@override
Widget build(BuildContext context) {
return MouseRegion(
onEnter: (_) => setState(() => _scale = widget.hoverScale),
onExit: (_) => setState(() => _scale = 1.0),
child: AnimatedScale(
scale: _scale,
duration: const Duration(milliseconds: 120),
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTapDown: (_) => setState(() => _scale = widget.tapScale),
onTapUp: (_) => setState(() => _scale = widget.hoverScale),
onTapCancel: () => setState(() => _scale = 1.0),
onTap: widget.onTap,
child: widget.child,
),
),
);
}
}

View File

@ -248,6 +248,7 @@ class _BillingDashboardScreenState extends State<BillingDashboardScreen> {
padding: const EdgeInsets.only(right: 8.0), padding: const EdgeInsets.only(right: 8.0),
child: Directionality( child: Directionality(
textDirection: TextDirection.rtl, textDirection: TextDirection.rtl,
child: InteractiveScale(
child: ElevatedButton.icon( child: ElevatedButton.icon(
onPressed: _simulateBarcodeScan, onPressed: _simulateBarcodeScan,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
@ -267,6 +268,7 @@ class _BillingDashboardScreenState extends State<BillingDashboardScreen> {
), ),
), ),
), ),
),
if (widget.kotManagement) if (widget.kotManagement)
Container( Container(
margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 12), margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 12),
@ -407,7 +409,13 @@ class _BillingDashboardScreenState extends State<BillingDashboardScreen> {
final isLowStock = widget.inventoryAlerts && remainingStock <= 3; final isLowStock = widget.inventoryAlerts && remainingStock <= 3;
final isOutOfStock = widget.inventoryAlerts && remainingStock <= 0; final isOutOfStock = widget.inventoryAlerts && remainingStock <= 0;
return InkWell( return SlideFadeEntrance(
delay: Duration(milliseconds: index * 40),
duration: const Duration(milliseconds: 300),
child: InteractiveScale(
tapScale: isOutOfStock ? 1.0 : 0.95,
hoverScale: isOutOfStock ? 1.0 : 1.03,
child: InkWell(
onTap: isOutOfStock ? null : () => _handleAddItem(prod), onTap: isOutOfStock ? null : () => _handleAddItem(prod),
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
child: Container( child: Container(
@ -498,6 +506,8 @@ class _BillingDashboardScreenState extends State<BillingDashboardScreen> {
], ],
), ),
), ),
),
),
); );
}, },
), ),
@ -655,12 +665,18 @@ class _BillingDashboardScreenState extends State<BillingDashboardScreen> {
const Divider(color: Color(0xFF334155), height: 16), const Divider(color: Color(0xFF334155), height: 16),
_buildComputationCalculations(theme), _buildComputationCalculations(theme),
const SizedBox(height: 10), const SizedBox(height: 10),
ElevatedButton( InteractiveScale(
hoverScale: _billingList.isNotEmpty ? 1.03 : 1.0,
tapScale: _billingList.isNotEmpty ? 0.96 : 1.0,
child: ElevatedButton(
onPressed: _billingList.isEmpty ? null : _handleReviewAndGenerate, onPressed: _billingList.isEmpty ? null : _handleReviewAndGenerate,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: theme.colorScheme.primary, backgroundColor: theme.colorScheme.primary,
disabledBackgroundColor: const Color(0xFF1E293B), disabledBackgroundColor: const Color(0xFF1E293B),
minimumSize: const Size.fromHeight(48), minimumSize: const Size.fromHeight(48),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
), ),
child: const Row( child: const Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@ -670,6 +686,7 @@ class _BillingDashboardScreenState extends State<BillingDashboardScreen> {
Text('Generate Invoice'), Text('Generate Invoice'),
], ],
), ),
),
) )
] ]
], ],
@ -751,11 +768,17 @@ class _BillingDashboardScreenState extends State<BillingDashboardScreen> {
) )
], ],
), ),
ElevatedButton( InteractiveScale(
hoverScale: _billingList.isNotEmpty ? 1.03 : 1.0,
tapScale: _billingList.isNotEmpty ? 0.96 : 1.0,
child: ElevatedButton(
onPressed: _billingList.isEmpty ? null : _handleReviewAndGenerate, onPressed: _billingList.isEmpty ? null : _handleReviewAndGenerate,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
minimumSize: const Size(180, 44), minimumSize: const Size(180, 44),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
), ),
child: const Row( child: const Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -765,6 +788,7 @@ class _BillingDashboardScreenState extends State<BillingDashboardScreen> {
Text('Review & Invoice', style: TextStyle(fontSize: 12)), Text('Review & Invoice', style: TextStyle(fontSize: 12)),
], ],
), ),
),
) )
], ],
) )
@ -937,7 +961,8 @@ class _BillingDashboardScreenState extends State<BillingDashboardScreen> {
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
ElevatedButton.icon( InteractiveScale(
child: ElevatedButton.icon(
onPressed: () { onPressed: () {
showTopAlert( showTopAlert(
context, context,
@ -951,17 +976,26 @@ class _BillingDashboardScreenState extends State<BillingDashboardScreen> {
backgroundColor: Colors.black, backgroundColor: Colors.black,
foregroundColor: Colors.white, foregroundColor: Colors.white,
minimumSize: const Size.fromHeight(42), minimumSize: const Size.fromHeight(42),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
),
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
ElevatedButton( InteractiveScale(
child: ElevatedButton(
onPressed: _closeReceiptAndReset, onPressed: _closeReceiptAndReset,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: theme.colorScheme.primary, backgroundColor: theme.colorScheme.primary,
minimumSize: const Size.fromHeight(42), minimumSize: const Size.fromHeight(42),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
), ),
child: const Text('New Ledger Sale', style: TextStyle(fontSize: 12)), child: const Text('New Ledger Sale', style: TextStyle(fontSize: 12)),
), ),
),
], ],
), ),
), ),

View File

@ -146,7 +146,8 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
Widget _buildDocChip(String label, IconData icon) { Widget _buildDocChip(String label, IconData icon) {
final theme = Theme.of(context); final theme = Theme.of(context);
final isSelected = _selectedDocs.contains(label); final isSelected = _selectedDocs.contains(label);
return InkWell( return InteractiveScale(
child: InkWell(
onTap: () { onTap: () {
setState(() { setState(() {
if (isSelected) { if (isSelected) {
@ -188,6 +189,7 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
], ],
), ),
), ),
),
); );
} }
@ -337,7 +339,8 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
ElevatedButton.icon( InteractiveScale(
child: ElevatedButton.icon(
onPressed: () => _handleImagePick(docName, ImageSource.camera), onPressed: () => _handleImagePick(docName, ImageSource.camera),
icon: const Icon(Icons.camera_alt, size: 14), icon: const Icon(Icons.camera_alt, size: 14),
label: const Text('Take Photo'), label: const Text('Take Photo'),
@ -348,11 +351,13 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
elevation: 1, elevation: 1,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)), borderRadius: BorderRadius.circular(16)),
),
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
OutlinedButton.icon( InteractiveScale(
child: OutlinedButton.icon(
onPressed: () => _handleImagePick(docName, ImageSource.gallery), onPressed: () => _handleImagePick(docName, ImageSource.gallery),
icon: const Icon(Icons.photo_library, size: 14), icon: const Icon(Icons.photo_library, size: 14),
label: const Text('From Gallery'), label: const Text('From Gallery'),
@ -361,7 +366,8 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
side: const BorderSide(color: Color(0xFFAF101A)), side: const BorderSide(color: Color(0xFFAF101A)),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)), borderRadius: BorderRadius.circular(16)),
),
), ),
), ),
], ],
@ -438,6 +444,11 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0), padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SlideFadeEntrance(
delay: Duration.zero,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -452,8 +463,16 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
'Verify your business details to start selling.', 'Verify your business details to start selling.',
style: theme.textTheme.bodyMedium, style: theme.textTheme.bodyMedium,
), ),
],
),
),
const SizedBox(height: 16), const SizedBox(height: 16),
SlideFadeEntrance(
delay: const Duration(milliseconds: 100),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text( Text(
'Business/Shop Name *', 'Business/Shop Name *',
style: theme.textTheme.labelSmall?.copyWith( style: theme.textTheme.labelSmall?.copyWith(
@ -469,8 +488,16 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
), ),
style: const TextStyle(fontWeight: FontWeight.bold), style: const TextStyle(fontWeight: FontWeight.bold),
), ),
],
),
),
const SizedBox(height: 16), const SizedBox(height: 16),
SlideFadeEntrance(
delay: const Duration(milliseconds: 180),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text( Text(
'Business Type *', 'Business Type *',
style: theme.textTheme.labelSmall?.copyWith( style: theme.textTheme.labelSmall?.copyWith(
@ -479,7 +506,6 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
GridView.builder( GridView.builder(
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
@ -494,7 +520,8 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
final cat = _categories[index]; final cat = _categories[index];
final isSelected = _selectedCategory == cat['id']; final isSelected = _selectedCategory == cat['id'];
return InkWell( return InteractiveScale(
child: InkWell(
onTap: () { onTap: () {
setState(() { setState(() {
_selectedCategory = cat['id']; _selectedCategory = cat['id'];
@ -545,11 +572,20 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
], ],
), ),
), ),
),
); );
}, },
), ),
],
),
),
const SizedBox(height: 16), const SizedBox(height: 16),
SlideFadeEntrance(
delay: const Duration(milliseconds: 260),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -583,7 +619,8 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
), ),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
ElevatedButton( InteractiveScale(
child: ElevatedButton(
onPressed: () { onPressed: () {
showTopAlert( showTopAlert(
context, context,
@ -602,6 +639,7 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
), ),
child: const Text('Verify'), child: const Text('Verify'),
), ),
),
], ],
), ),
const SizedBox(height: 6), const SizedBox(height: 6),
@ -609,8 +647,16 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
"Auto-fetches address & legal name on verification.", "Auto-fetches address & legal name on verification.",
style: TextStyle(fontSize: 11, color: Colors.grey[500]), style: TextStyle(fontSize: 11, color: Colors.grey[500]),
), ),
],
),
),
const SizedBox(height: 16), const SizedBox(height: 16),
SlideFadeEntrance(
delay: const Duration(milliseconds: 340),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text( Text(
'Shop Location *', 'Shop Location *',
style: theme.textTheme.labelSmall?.copyWith( style: theme.textTheme.labelSmall?.copyWith(
@ -619,7 +665,8 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
InkWell( InteractiveScale(
child: InkWell(
onTap: () { onTap: () {
showTopAlert( showTopAlert(
context, context,
@ -683,8 +730,17 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
), ),
), ),
), ),
),
],
),
),
const SizedBox(height: 16), const SizedBox(height: 16),
SlideFadeEntrance(
delay: const Duration(milliseconds: 420),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text( Text(
'Identity / License Documents (Select all that apply)', 'Identity / License Documents (Select all that apply)',
style: theme.textTheme.labelSmall?.copyWith( style: theme.textTheme.labelSmall?.copyWith(
@ -704,8 +760,15 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
_buildDocChip('PAN', Icons.credit_card), _buildDocChip('PAN', Icons.credit_card),
], ],
), ),
],
),
),
const SizedBox(height: 8), const SizedBox(height: 8),
..._selectedDocs.map((doc) {
SlideFadeEntrance(
delay: const Duration(milliseconds: 500),
child: Column(
children: _selectedDocs.map((doc) {
IconData docIcon = Icons.description; IconData docIcon = Icons.description;
if (doc == 'GSTIN') docIcon = Icons.description; if (doc == 'GSTIN') docIcon = Icons.description;
if (doc == 'FSSAI') docIcon = Icons.restaurant; if (doc == 'FSSAI') docIcon = Icons.restaurant;
@ -713,9 +776,16 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
if (doc == 'Udhyog Aadhar') docIcon = Icons.domain; if (doc == 'Udhyog Aadhar') docIcon = Icons.domain;
if (doc == 'PAN') docIcon = Icons.credit_card; if (doc == 'PAN') docIcon = Icons.credit_card;
return _buildUploadSection(doc, docIcon); return _buildUploadSection(doc, docIcon);
}), }).toList(),
),
),
const SizedBox(height: 16), const SizedBox(height: 16),
SlideFadeEntrance(
delay: const Duration(milliseconds: 580),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text( Text(
'Shop Facade Photo *', 'Shop Facade Photo *',
style: theme.textTheme.labelSmall?.copyWith( style: theme.textTheme.labelSmall?.copyWith(
@ -724,10 +794,12 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
InkWell( InteractiveScale(
child: InkWell(
onTap: () { onTap: () {
_showFacadePhotoOptions(); _showFacadePhotoOptions();
}, },
borderRadius: BorderRadius.circular(16),
child: Container( child: Container(
width: double.infinity, width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 24), padding: const EdgeInsets.symmetric(vertical: 24),
@ -774,6 +846,10 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
), ),
), ),
), ),
),
],
),
),
const SizedBox(height: 12), const SizedBox(height: 12),
], ],
), ),
@ -781,8 +857,13 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
), ),
), ),
Padding( SlideFadeEntrance(
delay: const Duration(milliseconds: 660),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0), padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
child: InteractiveScale(
hoverScale: _isNameValid ? 1.03 : 1.0,
tapScale: _isNameValid ? 0.96 : 1.0,
child: ElevatedButton( child: ElevatedButton(
onPressed: _isNameValid onPressed: _isNameValid
? () { ? () {
@ -803,9 +884,14 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
backgroundColor: _isNameValid ? theme.colorScheme.primary : Colors.grey[200], backgroundColor: _isNameValid ? theme.colorScheme.primary : Colors.grey[200],
foregroundColor: _isNameValid ? Colors.white : Colors.grey[400], foregroundColor: _isNameValid ? Colors.white : Colors.grey[400],
elevation: _isNameValid ? 2 : 0, elevation: _isNameValid ? 2 : 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
), ),
child: const Text('Continue to Verification'), child: const Text('Continue to Verification'),
), ),
),
),
) )
], ],
), ),

View File

@ -116,6 +116,11 @@ class _CustomizeSetupScreenState extends State<CustomizeSetupScreen> {
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0), padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SlideFadeEntrance(
delay: Duration.zero,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -130,10 +135,15 @@ class _CustomizeSetupScreenState extends State<CustomizeSetupScreen> {
'Turn on the features you need for your shop. You can always change these later in settings.', 'Turn on the features you need for your shop. You can always change these later in settings.',
style: theme.textTheme.bodyMedium, style: theme.textTheme.bodyMedium,
), ),
const SizedBox(height: 16), ],
),
),
const SizedBox(height: 20),
// Toggle 1: Barcode // Toggle 1: Barcode
_buildToggleCard( SlideFadeEntrance(
delay: const Duration(milliseconds: 100),
child: _buildToggleCard(
Icons.qr_code_scanner, Icons.qr_code_scanner,
'Barcode Scanner', 'Barcode Scanner',
'Do you use a Barcode Scanner?', 'Do you use a Barcode Scanner?',
@ -141,10 +151,13 @@ class _CustomizeSetupScreenState extends State<CustomizeSetupScreen> {
(val) => setState(() => _barcode = val), (val) => setState(() => _barcode = val),
theme, theme,
), ),
),
const SizedBox(height: 12), const SizedBox(height: 12),
// Toggle 2: KOT // Toggle 2: KOT
_buildToggleCard( SlideFadeEntrance(
delay: const Duration(milliseconds: 180),
child: _buildToggleCard(
Icons.receipt_long, Icons.receipt_long,
'KOT Management', 'KOT Management',
'Do you need Kitchen Order Ticket management?', 'Do you need Kitchen Order Ticket management?',
@ -152,10 +165,13 @@ class _CustomizeSetupScreenState extends State<CustomizeSetupScreen> {
(val) => setState(() => _kot = val), (val) => setState(() => _kot = val),
theme, theme,
), ),
),
const SizedBox(height: 12), const SizedBox(height: 12),
// Toggle 3: Inventory stock tracking // Toggle 3: Inventory stock tracking
_buildToggleCard( SlideFadeEntrance(
delay: const Duration(milliseconds: 260),
child: _buildToggleCard(
Icons.inventory_2, Icons.inventory_2,
'Inventory Alerts', 'Inventory Alerts',
'Get notified when stock is running low.', 'Get notified when stock is running low.',
@ -163,6 +179,7 @@ class _CustomizeSetupScreenState extends State<CustomizeSetupScreen> {
(val) => setState(() => _inventory = val), (val) => setState(() => _inventory = val),
theme, theme,
), ),
),
], ],
), ),
), ),
@ -170,8 +187,11 @@ class _CustomizeSetupScreenState extends State<CustomizeSetupScreen> {
), ),
// Pinned action keys // Pinned action keys
Padding( SlideFadeEntrance(
delay: const Duration(milliseconds: 340),
child: Padding(
padding: EdgeInsets.all(padding), padding: EdgeInsets.all(padding),
child: InteractiveScale(
child: ElevatedButton( child: ElevatedButton(
onPressed: () { onPressed: () {
Navigator.push( Navigator.push(
@ -190,9 +210,14 @@ class _CustomizeSetupScreenState extends State<CustomizeSetupScreen> {
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(54), minimumSize: const Size.fromHeight(54),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
), ),
child: const Text('Save & Continue'), child: const Text('Save & Continue'),
), ),
),
),
) )
], ],
), ),
@ -210,7 +235,9 @@ class _CustomizeSetupScreenState extends State<CustomizeSetupScreen> {
ValueChanged<bool> onChanged, ValueChanged<bool> onChanged,
ThemeData theme, ThemeData theme,
) { ) {
return Container( return InteractiveScale(
onTap: () => onChanged(!value),
child: Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
@ -269,6 +296,7 @@ class _CustomizeSetupScreenState extends State<CustomizeSetupScreen> {
), ),
], ],
), ),
),
); );
} }
} }

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:contacts_service/contacts_service.dart'; import 'package:flutter_contacts/flutter_contacts.dart';
import '../theme.dart'; import '../theme.dart';
import '../route_transitions.dart'; import '../route_transitions.dart';
import 'otp_verification_screen.dart'; import 'otp_verification_screen.dart';
@ -16,8 +16,8 @@ class MockContact {
} }
class MockPhone { class MockPhone {
final String? value; final String number;
MockPhone(this.value); MockPhone(this.number);
} }
class MobileLoginScreen extends StatefulWidget { class MobileLoginScreen extends StatefulWidget {
@ -103,7 +103,7 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
]; ];
} else { } else {
try { try {
final List<Contact> list = await ContactsService.getContacts(withThumbnails: false); final List<Contact> list = await FlutterContacts.getContacts(withProperties: true, withPhoto: false);
return list; return list;
} catch (e) { } catch (e) {
return []; return [];
@ -166,7 +166,7 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
setDialogState(() { setDialogState(() {
filteredContacts = allContacts.where((contact) { filteredContacts = allContacts.where((contact) {
final name = (contact.displayName ?? '').toLowerCase(); final name = (contact.displayName ?? '').toLowerCase();
final phone = (contact.phones.isNotEmpty ? contact.phones.first.value ?? '' : '').toLowerCase(); final phone = (contact.phones.isNotEmpty ? contact.phones.first.number : '').toLowerCase();
return name.contains(query.toLowerCase()) || phone.contains(query.toLowerCase()); return name.contains(query.toLowerCase()) || phone.contains(query.toLowerCase());
}).toList(); }).toList();
}); });
@ -183,7 +183,7 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
itemBuilder: (context, index) { itemBuilder: (context, index) {
final contact = filteredContacts[index]; final contact = filteredContacts[index];
final name = contact.displayName ?? 'Unknown'; final name = contact.displayName ?? 'Unknown';
final phone = contact.phones.isNotEmpty ? contact.phones.first.value : 'No number'; final phone = contact.phones.isNotEmpty ? contact.phones.first.number : 'No number';
return ListTile( return ListTile(
title: Text(name), title: Text(name),
subtitle: Text(phone), subtitle: Text(phone),
@ -253,6 +253,11 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
Expanded( Expanded(
child: SingleChildScrollView( child: SingleChildScrollView(
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SlideFadeEntrance(
delay: Duration.zero,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -296,7 +301,6 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
}), }),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Text( Text(
'Welcome Back', 'Welcome Back',
style: theme.textTheme.headlineLarge?.copyWith( style: theme.textTheme.headlineLarge?.copyWith(
@ -308,9 +312,14 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
'Enter your mobile number to sign in or create a new account.', 'Enter your mobile number to sign in or create a new account.',
style: theme.textTheme.bodyMedium, style: theme.textTheme.bodyMedium,
), ),
],
),
),
const SizedBox(height: 16), const SizedBox(height: 16),
Container( SlideFadeEntrance(
delay: const Duration(milliseconds: 100),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFFFAFFD1).withOpacity(0.8), color: const Color(0xFFFAFFD1).withOpacity(0.8),
@ -332,10 +341,13 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
], ],
), ),
), ),
),
const SizedBox(height: 16), const SizedBox(height: 16),
Container( SlideFadeEntrance(
delay: const Duration(milliseconds: 200),
child: Container(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFFF3F3F3), color: const Color(0xFFF3F3F3),
@ -446,9 +458,15 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
], ],
), ),
), ),
),
SizedBox(height: gap), SizedBox(height: gap),
SlideFadeEntrance(
delay: const Duration(milliseconds: 300),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row( Row(
children: [ children: [
const Expanded(child: Divider(color: Color(0xFFE4BEBA))), const Expanded(child: Divider(color: Color(0xFFE4BEBA))),
@ -466,9 +484,7 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
const Expanded(child: Divider(color: Color(0xFFE4BEBA))), const Expanded(child: Divider(color: Color(0xFFE4BEBA))),
], ],
), ),
SizedBox(height: gap), SizedBox(height: gap),
Text( Text(
'Mobile Number', 'Mobile Number',
style: theme.textTheme.labelSmall?.copyWith( style: theme.textTheme.labelSmall?.copyWith(
@ -477,7 +493,6 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
Container( Container(
height: 56, height: 56,
decoration: BoxDecoration( decoration: BoxDecoration(
@ -541,7 +556,6 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
], ],
), ),
), ),
Expanded( Expanded(
child: TextField( child: TextField(
controller: _controller, controller: _controller,
@ -571,16 +585,24 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
], ],
), ),
), ),
],
),
),
SizedBox(height: gap * 2), SizedBox(height: gap * 2),
], ],
), ),
), ),
), ),
Column( SlideFadeEntrance(
delay: const Duration(milliseconds: 400),
child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
ElevatedButton( InteractiveScale(
hoverScale: _isValid ? 1.03 : 1.0,
tapScale: _isValid ? 0.96 : 1.0,
child: ElevatedButton(
onPressed: _isValid onPressed: _isValid
? () { ? () {
Navigator.push( Navigator.push(
@ -599,13 +621,15 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
foregroundColor: _isValid ? Colors.white : Colors.grey[400], foregroundColor: _isValid ? Colors.white : Colors.grey[400],
elevation: _isValid ? 2 : 0, elevation: _isValid ? 2 : 0,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(16),
), ),
), ),
child: const Text('Send OTP'), child: const Text('Send OTP'),
), ),
),
SizedBox(height: gap / 2), SizedBox(height: gap / 2),
OutlinedButton( InteractiveScale(
child: OutlinedButton(
onPressed: () { onPressed: () {
showTopAlert( showTopAlert(
context, context,
@ -617,7 +641,7 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
minimumSize: const Size.fromHeight(56), minimumSize: const Size.fromHeight(56),
side: const BorderSide(color: Color(0xFFAF101A), width: 2), side: const BorderSide(color: Color(0xFFAF101A), width: 2),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(16),
), ),
), ),
child: Row( child: Row(
@ -636,8 +660,10 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
], ],
), ),
), ),
),
], ],
), ),
),
], ],
), ),
), ),

View File

@ -123,8 +123,10 @@ class _OtpVerificationScreenState extends State<OtpVerificationScreen> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
// Alert envelope pulse symbol // Alert envelope pulse symbol with SlideFadeEntrance
Center( SlideFadeEntrance(
delay: Duration.zero,
child: Center(
child: Container( child: Container(
width: 96, width: 96,
height: 96, height: 96,
@ -155,16 +157,22 @@ class _OtpVerificationScreenState extends State<OtpVerificationScreen> {
), ),
), ),
), ),
),
const SizedBox(height: 16), const SizedBox(height: 16),
Text( SlideFadeEntrance(
delay: const Duration(milliseconds: 100),
child: Text(
'Verifying your number', 'Verifying your number',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: theme.textTheme.headlineMedium?.copyWith( style: theme.textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
),
const SizedBox(height: 16), const SizedBox(height: 16),
Row( SlideFadeEntrance(
delay: const Duration(milliseconds: 150),
child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
@ -173,7 +181,7 @@ class _OtpVerificationScreenState extends State<OtpVerificationScreen> {
style: theme.textTheme.bodyMedium, style: theme.textTheme.bodyMedium,
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
GestureDetector( InteractiveScale(
onTap: () => Navigator.pop(context), onTap: () => Navigator.pop(context),
child: Text( child: Text(
'Edit', 'Edit',
@ -186,11 +194,14 @@ class _OtpVerificationScreenState extends State<OtpVerificationScreen> {
), ),
], ],
), ),
),
const SizedBox(height: 16), const SizedBox(height: 20),
// 6 Custom Inputs Fields // 6 Custom Inputs Fields with SlideFadeEntrance
Row( SlideFadeEntrance(
delay: const Duration(milliseconds: 200),
child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(11, (index) { children: List.generate(11, (index) {
if (index.isOdd) { if (index.isOdd) {
@ -213,11 +224,11 @@ class _OtpVerificationScreenState extends State<OtpVerificationScreen> {
counterText: '', counterText: '',
contentPadding: const EdgeInsets.symmetric(vertical: 12), contentPadding: const EdgeInsets.symmetric(vertical: 12),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: theme.colorScheme.outlineVariant), borderSide: BorderSide(color: theme.colorScheme.outlineVariant),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: theme.colorScheme.primary, width: 2), borderSide: BorderSide(color: theme.colorScheme.primary, width: 2),
), ),
), ),
@ -230,15 +241,18 @@ class _OtpVerificationScreenState extends State<OtpVerificationScreen> {
); );
}), }),
), ),
),
const SizedBox(height: 16), const SizedBox(height: 24),
// Auto detect loader // Auto detect loader card with SlideFadeEntrance
Container( SlideFadeEntrance(
delay: const Duration(milliseconds: 250),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerLow, color: theme.colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(16),
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@ -261,11 +275,14 @@ class _OtpVerificationScreenState extends State<OtpVerificationScreen> {
], ],
), ),
), ),
),
const SizedBox(height: 16), const SizedBox(height: 24),
// Resending timer action column // Resending timer action row
Row( SlideFadeEntrance(
delay: const Duration(milliseconds: 300),
child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const Icon(Icons.schedule, size: 16, color: Colors.grey), const Icon(Icons.schedule, size: 16, color: Colors.grey),
@ -278,29 +295,40 @@ class _OtpVerificationScreenState extends State<OtpVerificationScreen> {
), ),
], ],
), ),
),
const SizedBox(height: 16), const SizedBox(height: 16),
// WhatsApp custom CTA button // WhatsApp custom CTA button with InteractiveScale
ElevatedButton( SlideFadeEntrance(
onPressed: () {}, delay: const Duration(milliseconds: 350),
style: ElevatedButton.styleFrom( child: InteractiveScale(
backgroundColor: Colors.white, child: OutlinedButton.icon(
foregroundColor: Colors.black87, onPressed: () {
side: const BorderSide(color: Color(0xFFE2E2E2)), showTopAlert(
elevation: 1, context,
minimumSize: const Size(200, 48), 'WhatsApp verification request sent!',
), icon: Icons.chat_bubble_outline,
child: Row( );
mainAxisSize: MainAxisSize.min, },
children: const [ icon: const Icon(Icons.forum, color: Colors.green, size: 18),
Text( label: const Text(
'Resend via WhatsApp', 'Resend via WhatsApp',
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
style: OutlinedButton.styleFrom(
backgroundColor: Colors.white,
side: const BorderSide(color: Color(0xFFE2E2E2)),
elevation: 1,
minimumSize: const Size(220, 48),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
), ),
), ),
],
), ),
) )
], ],
@ -326,9 +354,22 @@ class _OtpVerificationScreenState extends State<OtpVerificationScreen> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
_buildFooterAction(Icons.help_outline, 'Help'), _buildFooterAction(Icons.help_outline, 'Help', () {
_buildFooterAction(Icons.save, 'Save Draft'), showTopAlert(
TextButton.icon( context,
'Support agent contacted. We will call you shortly.',
icon: Icons.support_agent_outlined,
);
}),
_buildFooterAction(Icons.save, 'Save Draft', () {
showTopAlert(
context,
'Onboarding draft saved successfully!',
icon: Icons.save_outlined,
);
}),
InteractiveScale(
child: TextButton.icon(
onPressed: () { onPressed: () {
Navigator.push( Navigator.push(
context, context,
@ -347,7 +388,8 @@ class _OtpVerificationScreenState extends State<OtpVerificationScreen> {
backgroundColor: theme.colorScheme.primary, backgroundColor: theme.colorScheme.primary,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(16),
),
), ),
), ),
) )
@ -363,8 +405,10 @@ class _OtpVerificationScreenState extends State<OtpVerificationScreen> {
); );
} }
Widget _buildFooterAction(IconData icon, String label) { Widget _buildFooterAction(IconData icon, String label, VoidCallback onTap) {
return Column( return InteractiveScale(
onTap: onTap,
child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon(icon, color: Colors.grey[600], size: 22), Icon(icon, color: Colors.grey[600], size: 22),
@ -378,6 +422,7 @@ class _OtpVerificationScreenState extends State<OtpVerificationScreen> {
), ),
) )
], ],
),
); );
} }
} }

View File

@ -32,7 +32,6 @@ class _SetupCompleteScreenState extends State<SetupCompleteScreen> with SingleTi
late Animation<double> _cardOpacityAnimation; late Animation<double> _cardOpacityAnimation;
late Animation<double> _btnSlideAnimation; late Animation<double> _btnSlideAnimation;
late Animation<double> _btnOpacityAnimation; late Animation<double> _btnOpacityAnimation;
double _btnScale = 1.0;
@override @override
void initState() { void initState() {
@ -382,16 +381,7 @@ class _SetupCompleteScreenState extends State<SetupCompleteScreen> with SingleTi
), ),
); );
}, },
child: MouseRegion( child: InteractiveScale(
onEnter: (_) => setState(() => _btnScale = 1.03),
onExit: (_) => setState(() => _btnScale = 1.0),
child: AnimatedScale(
scale: _btnScale,
duration: const Duration(milliseconds: 150),
child: GestureDetector(
onTapDown: (_) => setState(() => _btnScale = 0.96),
onTapUp: (_) => setState(() => _btnScale = 1.03),
onTapCancel: () => setState(() => _btnScale = 1.0),
child: ElevatedButton( child: ElevatedButton(
onPressed: () { onPressed: () {
showTopAlert( showTopAlert(
@ -416,7 +406,7 @@ class _SetupCompleteScreenState extends State<SetupCompleteScreen> with SingleTi
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(56), minimumSize: const Size.fromHeight(56),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(16),
), ),
), ),
child: Row( child: Row(
@ -430,8 +420,6 @@ class _SetupCompleteScreenState extends State<SetupCompleteScreen> with SingleTi
), ),
), ),
), ),
),
),
const SizedBox(height: 24), const SizedBox(height: 24),
], ],
), ),

View File

@ -17,7 +17,6 @@ class _WelcomeScreenState extends State<WelcomeScreen> with SingleTickerProvider
late Animation<double> _splashContainerOpacityAnimation; late Animation<double> _splashContainerOpacityAnimation;
late Animation<double> _contentFadeAnimation; late Animation<double> _contentFadeAnimation;
late Animation<Offset> _contentSlideAnimation; late Animation<Offset> _contentSlideAnimation;
double _btnScale = 1.0;
@override @override
void initState() { void initState() {
@ -491,17 +490,7 @@ class _WelcomeScreenState extends State<WelcomeScreen> with SingleTickerProvider
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: context.isMobile ? CrossAxisAlignment.center : CrossAxisAlignment.start, crossAxisAlignment: context.isMobile ? CrossAxisAlignment.center : CrossAxisAlignment.start,
children: [ children: [
MouseRegion( InteractiveScale(
onEnter: (_) => setState(() => _btnScale = 1.03),
onExit: (_) => setState(() => _btnScale = 1.0),
child: GestureDetector(
onTapDown: (_) => setState(() => _btnScale = 0.95),
onTapUp: (_) => setState(() => _btnScale = 1.03),
onTapCancel: () => setState(() => _btnScale = 1.0),
child: AnimatedScale(
scale: _btnScale,
duration: const Duration(milliseconds: 150),
curve: Curves.easeOut,
child: ElevatedButton( child: ElevatedButton(
onPressed: () { onPressed: () {
Navigator.push( Navigator.push(
@ -523,8 +512,6 @@ class _WelcomeScreenState extends State<WelcomeScreen> with SingleTickerProvider
), ),
), ),
), ),
),
),
const SizedBox(height: 16), const SizedBox(height: 16),
Row( Row(
mainAxisAlignment: context.isMobile ? MainAxisAlignment.center : MainAxisAlignment.start, mainAxisAlignment: context.isMobile ? MainAxisAlignment.center : MainAxisAlignment.start,

View File

@ -122,7 +122,7 @@ class PozoTheme {
minimumSize: const Size.fromHeight(54), minimumSize: const Size.fromHeight(54),
elevation: 2, elevation: 2,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(16),
), ),
textStyle: GoogleFonts.plusJakartaSans( textStyle: GoogleFonts.plusJakartaSans(
fontSize: 16, fontSize: 16,

View File

@ -41,14 +41,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.19.1" version: "1.19.1"
contacts_service:
dependency: "direct main"
description:
name: contacts_service
sha256: f6d5ea33b31dfcdcd2e65d8abdc836502e04ddb0f66a96aa726fa9891ea9671e
url: "https://pub.dev"
source: hosted
version: "0.6.3"
cross_file: cross_file:
dependency: transitive dependency: transitive
description: description:
@ -126,6 +118,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_contacts:
dependency: "direct main"
description:
name: flutter_contacts
sha256: "388d32cd33f16640ee169570128c933b45f3259bddbfae7a100bb49e5ffea9ae"
url: "https://pub.dev"
source: hosted
version: "1.1.9+2"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -424,14 +424,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.8" version: "2.1.8"
quiver:
dependency: transitive
description:
name: quiver
sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2
url: "https://pub.dev"
source: hosted
version: "3.2.2"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter

View File

@ -12,7 +12,7 @@ dependencies:
google_fonts: ^5.1.0 google_fonts: ^5.1.0
image_picker: ^1.0.4 image_picker: ^1.0.4
permission_handler: ^11.0.1 permission_handler: ^11.0.1
contacts_service: ^0.6.3 flutter_contacts: ^1.1.9
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: