From 45a3e381c05723244963dcaf7bff4f6dc4e23e7b Mon Sep 17 00:00:00 2001 From: "sridhar.r" Date: Wed, 10 Jun 2026 14:08:47 +0530 Subject: [PATCH] Animate SetupCompleteScreen elements sequentially and add button scaling --- .../lib/screens/setup_complete_screen.dart | 583 +++++++++++------- 1 file changed, 344 insertions(+), 239 deletions(-) diff --git a/flutter_app/lib/screens/setup_complete_screen.dart b/flutter_app/lib/screens/setup_complete_screen.dart index 039365a..5dd86a3 100644 --- a/flutter_app/lib/screens/setup_complete_screen.dart +++ b/flutter_app/lib/screens/setup_complete_screen.dart @@ -3,7 +3,7 @@ import '../theme.dart'; import '../route_transitions.dart'; import 'billing_dashboard_screen.dart'; -class SetupCompleteScreen extends StatelessWidget { +class SetupCompleteScreen extends StatefulWidget { final String businessName; final String businessType; final String gstin; @@ -21,6 +21,71 @@ class SetupCompleteScreen extends StatelessWidget { this.inventoryAlerts = true, }); + @override + State createState() => _SetupCompleteScreenState(); +} + +class _SetupCompleteScreenState extends State with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _checkScaleAnimation; + late Animation _cardSlideAnimation; + late Animation _cardOpacityAnimation; + late Animation _btnSlideAnimation; + late Animation _btnOpacityAnimation; + double _btnScale = 1.0; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 1400), + ); + + _checkScaleAnimation = Tween(begin: 0.0, end: 1.0).animate( + CurvedAnimation( + parent: _controller, + curve: const Interval(0.0, 0.45, curve: Curves.elasticOut), + ), + ); + + _cardSlideAnimation = Tween(begin: 32.0, end: 0.0).animate( + CurvedAnimation( + parent: _controller, + curve: const Interval(0.25, 0.75, curve: Curves.easeOutCubic), + ), + ); + + _cardOpacityAnimation = Tween(begin: 0.0, end: 1.0).animate( + CurvedAnimation( + parent: _controller, + curve: const Interval(0.25, 0.65, curve: Curves.easeIn), + ), + ); + + _btnSlideAnimation = Tween(begin: 24.0, end: 0.0).animate( + CurvedAnimation( + parent: _controller, + curve: const Interval(0.55, 1.0, curve: Curves.easeOutCubic), + ), + ); + + _btnOpacityAnimation = Tween(begin: 0.0, end: 1.0).animate( + CurvedAnimation( + parent: _controller, + curve: const Interval(0.55, 0.85, curve: Curves.easeIn), + ), + ); + + _controller.forward(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + String _getCategoryDisplayName(String id) { final cleanId = id.replaceAll('category/', '').trim().toLowerCase(); switch (cleanId) { @@ -41,7 +106,7 @@ class SetupCompleteScreen extends StatelessWidget { @override Widget build(BuildContext context) { final theme = Theme.of(context); - final categoryText = _getCategoryDisplayName(businessType); + final categoryText = _getCategoryDisplayName(widget.businessType); final gap = context.responsiveGap; return Scaffold( @@ -82,259 +147,299 @@ class SetupCompleteScreen extends StatelessWidget { children: [ const SizedBox(height: 24), - // Circular Green Check badge - Container( - width: 96, - height: 96, - decoration: const BoxDecoration( - color: Colors.white, - shape: BoxShape.circle, - boxShadow: [ - BoxShadow( - color: Colors.black12, - blurRadius: 16, - offset: Offset(0, 8), - ) - ], - ), - child: Center( + // Circular Green Check badge with Elastic Entrance Animation + ScaleTransition( + scale: _checkScaleAnimation, child: Container( - width: 72, - height: 72, + width: 96, + height: 96, decoration: const BoxDecoration( - color: Colors.green, - shape: BoxShape.circle, - ), - child: const Icon( - Icons.check, color: Colors.white, - size: 40, - ), - ), - ), - ), - - SizedBox(height: gap), - Text( - 'Setup Complete! 🎉', - textAlign: TextAlign.center, - style: theme.textTheme.headlineLarge?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 8), - Text( - 'Your billing dashboard is ready for action.', - textAlign: TextAlign.center, - style: theme.textTheme.bodyMedium, - ), - - SizedBox(height: gap * 1.5), - - // Glassmorphism summary Card - Container( - width: double.infinity, - padding: const EdgeInsets.all(20), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(20), - border: Border.all(color: Colors.grey[200]!), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.04), - blurRadius: 20, - offset: const Offset(0, 8), - ) - ], - ), - child: Column( - children: [ - // Row 1: Store name - Row( - children: [ - Container( - width: 44, - height: 44, - decoration: BoxDecoration( - color: Colors.grey[100], - shape: BoxShape.circle, - ), - child: const Icon(Icons.storefront, color: Colors.black87), - ), - const SizedBox(width: 14), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'BUSINESS NAME', - style: TextStyle( - fontSize: 10, - fontWeight: FontWeight.bold, - color: Colors.grey, - letterSpacing: 1.0, - ), - ), - const SizedBox(height: 4), - Text( - businessName, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.black87, - ), - ), - ], - ), + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: Colors.black12, + blurRadius: 16, + offset: Offset(0, 8), ) ], ), - - // Divider line - Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0), - child: Divider(color: Colors.grey[200]), - ), - - // Row 2: Category type - Row( - children: [ - Container( - width: 44, - height: 44, - decoration: BoxDecoration( - color: Colors.grey[100], - shape: BoxShape.circle, - ), - child: const Icon(Icons.category, color: Colors.black87), + child: Center( + child: Container( + width: 72, + height: 72, + decoration: const BoxDecoration( + color: Colors.green, + shape: BoxShape.circle, ), - const SizedBox(width: 14), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'BUSINESS TYPE', - style: TextStyle( - fontSize: 10, - fontWeight: FontWeight.bold, - color: Colors.grey, - letterSpacing: 1.0, - ), - ), - const SizedBox(height: 4), - Text( - categoryText, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: Colors.black87, - ), - ), - ], - ), - ) - ], - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0), - child: Divider(color: Colors.grey[200]), - ), - Row( - children: [ - Container( - width: 44, - height: 44, - decoration: BoxDecoration( - color: Colors.grey[100], - shape: BoxShape.circle, - ), - child: const Icon(Icons.settings, color: Colors.black87), + child: const Icon( + Icons.check, + color: Colors.white, + size: 40, ), - const SizedBox(width: 14), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'ACTIVE CONFIGURATIONS', - style: TextStyle( - fontSize: 10, - fontWeight: FontWeight.bold, - color: Colors.grey, - letterSpacing: 1.0, - ), - ), - const SizedBox(height: 4), - Text( - [ - if (barcodeScanner) 'Barcode Enabled', - if (kotManagement) 'KOT/Kitchen Order', - if (inventoryAlerts) 'Low Stock Warnings', - if (!barcodeScanner && !kotManagement && !inventoryAlerts) 'Standard Register', - ].join(', '), - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: Colors.black87, - ), - ), - ], - ), - ) - ], - ) - ], - ), - ), - - const SizedBox(height: 24), - - // Primary Launcher CTA - ElevatedButton( - onPressed: () { - showTopAlert( - context, - 'Launching Register Dashboard... Enjoy billing!', - icon: Icons.rocket_launch_outlined, - ); - Navigator.push( - context, - FadeSlidePageRoute( - child: BillingDashboardScreen( - businessName: businessName, - businessType: businessType, - gstin: gstin, - barcodeScanner: barcodeScanner, - kotManagement: kotManagement, - inventoryAlerts: inventoryAlerts, ), ), - ); - }, - style: ElevatedButton.styleFrom( - minimumSize: const Size.fromHeight(56), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), ), ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: const [ - Icon(Icons.receipt, size: 20), - SizedBox(width: 8), - Text('Create Your First Bill'), - ], + + SizedBox(height: gap), + Text( + 'Setup Complete! 🎉', + textAlign: TextAlign.center, + style: theme.textTheme.headlineLarge?.copyWith( + fontWeight: FontWeight.bold, + ), ), - ), - const SizedBox(height: 24), - ], + const SizedBox(height: 8), + Text( + 'Your billing dashboard is ready for action.', + textAlign: TextAlign.center, + style: theme.textTheme.bodyMedium, + ), + + SizedBox(height: gap * 1.5), + + // Glassmorphism summary Card with Fade + Slide Transition + AnimatedBuilder( + animation: _controller, + builder: (context, child) { + return Transform.translate( + offset: Offset(0.0, _cardSlideAnimation.value), + child: Opacity( + opacity: _cardOpacityAnimation.value, + child: child, + ), + ); + }, + child: Container( + width: double.infinity, + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + border: Border.all(color: Colors.grey[200]!), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.04), + blurRadius: 20, + offset: const Offset(0, 8), + ) + ], + ), + child: Column( + children: [ + // Row 1: Store name + Row( + children: [ + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: Colors.grey[100], + shape: BoxShape.circle, + ), + child: const Icon(Icons.storefront, color: Colors.black87), + ), + const SizedBox(width: 14), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'BUSINESS NAME', + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.bold, + color: Colors.grey, + letterSpacing: 1.0, + ), + ), + const SizedBox(height: 4), + Text( + widget.businessName, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + ], + ), + ) + ], + ), + + // Divider line + Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Divider(color: Colors.grey[200]), + ), + + // Row 2: Category type + Row( + children: [ + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: Colors.grey[100], + shape: BoxShape.circle, + ), + child: const Icon(Icons.category, color: Colors.black87), + ), + const SizedBox(width: 14), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'BUSINESS TYPE', + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.bold, + color: Colors.grey, + letterSpacing: 1.0, + ), + ), + const SizedBox(height: 4), + Text( + categoryText, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + ], + ), + ) + ], + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Divider(color: Colors.grey[200]), + ), + Row( + children: [ + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: Colors.grey[100], + shape: BoxShape.circle, + ), + child: const Icon(Icons.settings, color: Colors.black87), + ), + const SizedBox(width: 14), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'ACTIVE CONFIGURATIONS', + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.bold, + color: Colors.grey, + letterSpacing: 1.0, + ), + ), + const SizedBox(height: 4), + Text( + [ + if (widget.barcodeScanner) 'Barcode Enabled', + if (widget.kotManagement) 'KOT/Kitchen Order', + if (widget.inventoryAlerts) 'Low Stock Warnings', + if (!widget.barcodeScanner && !widget.kotManagement && !widget.inventoryAlerts) 'Standard Register', + ].join(', '), + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + ], + ), + ) + ], + ) + ], + ), + ), + ), + + const SizedBox(height: 24), + + // Primary Launcher CTA with Slide + Fade + Hover/Tap Scale micro-animations + AnimatedBuilder( + animation: _controller, + builder: (context, child) { + return Transform.translate( + offset: Offset(0.0, _btnSlideAnimation.value), + child: Opacity( + opacity: _btnOpacityAnimation.value, + child: child, + ), + ); + }, + child: MouseRegion( + 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( + onPressed: () { + showTopAlert( + context, + 'Launching Register Dashboard... Enjoy billing!', + icon: Icons.rocket_launch_outlined, + ); + Navigator.push( + context, + FadeSlidePageRoute( + child: BillingDashboardScreen( + businessName: widget.businessName, + businessType: widget.businessType, + gstin: widget.gstin, + barcodeScanner: widget.barcodeScanner, + kotManagement: widget.kotManagement, + inventoryAlerts: widget.inventoryAlerts, + ), + ), + ); + }, + style: ElevatedButton.styleFrom( + minimumSize: const Size.fromHeight(56), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: const [ + Icon(Icons.receipt, size: 20), + SizedBox(width: 8), + Text('Create Your First Bill'), + ], + ), + ), + ), + ), + ), + ), + const SizedBox(height: 24), + ], + ), ), ), ), ), ), - ), - ); -} + ); + } }