diff --git a/lib/presentation/pages/auth/login/login_page.dart b/lib/presentation/pages/auth/login/login_page.dart index ae829fa..dd11d8f 100644 --- a/lib/presentation/pages/auth/login/login_page.dart +++ b/lib/presentation/pages/auth/login/login_page.dart @@ -1,5 +1,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'dart:math' as math; import '../../../../common/extension/extension.dart'; import '../../../../common/theme/theme.dart'; @@ -26,8 +27,13 @@ class _LoginPageState extends State with TickerProviderStateMixin { late AnimationController _fadeController; late AnimationController _slideController; + late AnimationController _backgroundController; + late AnimationController _floatingController; + late Animation _fadeAnimation; late Animation _slideAnimation; + late Animation _backgroundAnimation; + late Animation _floatingAnimation; @override void initState() { @@ -43,6 +49,16 @@ class _LoginPageState extends State with TickerProviderStateMixin { vsync: this, ); + _backgroundController = AnimationController( + duration: const Duration(seconds: 10), + vsync: this, + )..repeat(); + + _floatingController = AnimationController( + duration: const Duration(seconds: 6), + vsync: this, + )..repeat(reverse: true); + _fadeAnimation = Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation(parent: _fadeController, curve: Curves.easeInOut), ); @@ -51,8 +67,19 @@ class _LoginPageState extends State with TickerProviderStateMixin { Tween(begin: const Offset(0, 0.3), end: Offset.zero).animate( CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic), ); + + _backgroundAnimation = Tween( + begin: 0.0, + end: 2 * math.pi, + ).animate(_backgroundController); + + _floatingAnimation = Tween(begin: -20.0, end: 20.0).animate( + CurvedAnimation(parent: _floatingController, curve: Curves.easeInOut), + ); + _emailController.text = 'test@example.com'; _passwordController.text = 'password'; + _fadeController.forward(); _slideController.forward(); } @@ -61,6 +88,8 @@ class _LoginPageState extends State with TickerProviderStateMixin { void dispose() { _fadeController.dispose(); _slideController.dispose(); + _backgroundController.dispose(); + _floatingController.dispose(); _emailController.dispose(); _passwordController.dispose(); super.dispose(); @@ -86,36 +115,164 @@ class _LoginPageState extends State with TickerProviderStateMixin { @override Widget build(BuildContext context) { return Scaffold( - body: Container( - decoration: const BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - colors: AppColor.primaryGradient, - ), - ), - child: SafeArea( - child: Center( - child: SingleChildScrollView( - padding: EdgeInsets.symmetric(horizontal: AppValue.padding), - child: FadeTransition( - opacity: _fadeAnimation, - child: SlideTransition( - position: _slideAnimation, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _buildLogo(context), - SpaceHeight(48), - _buildLoginCard(context), - ], + body: AnimatedBuilder( + animation: Listenable.merge([ + _backgroundController, + _floatingController, + ]), + builder: (context, child) { + return Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: AppColor.primaryGradient, + ), + ), + child: Stack( + children: [ + // Animated background elements + _buildAnimatedBackground(), + + // Main content + SafeArea( + child: Center( + child: SingleChildScrollView( + padding: EdgeInsets.symmetric( + horizontal: AppValue.padding, + ), + child: FadeTransition( + opacity: _fadeAnimation, + child: SlideTransition( + position: _slideAnimation, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildLogo(context), + SpaceHeight(48), + _buildLoginCard(context), + ], + ), + ), + ), + ), ), ), + ], + ), + ); + }, + ), + ); + } + + Widget _buildAnimatedBackground() { + return Stack( + children: [ + // Floating circles + ...List.generate(6, (index) { + final double size = 80 + (index * 40); + final double left = + (index * 60.0) % MediaQuery.of(context).size.width; + final double top = + (index * 120.0) % MediaQuery.of(context).size.height; + + return Positioned( + left: left + math.sin(_backgroundAnimation.value + index) * 30, + top: top + _floatingAnimation.value + (index * 10), + child: Container( + width: size, + height: size, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.white.withOpacity(0.1), + border: Border.all( + color: Colors.white.withOpacity(0.2), + width: 2, + ), + ), + ), + ); + }), + + // Rotating geometric shapes + Positioned( + top: 100, + right: 50, + child: Transform.rotate( + angle: _backgroundAnimation.value, + child: Container( + width: 60, + height: 60, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.08), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: Colors.white.withOpacity(0.15), + width: 1, + ), ), ), ), ), - ), + + Positioned( + bottom: 150, + left: 30, + child: Transform.rotate( + angle: -_backgroundAnimation.value * 0.5, + child: Container( + width: 80, + height: 80, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.06), + shape: BoxShape.circle, + border: Border.all( + color: Colors.white.withOpacity(0.12), + width: 1, + ), + ), + ), + ), + ), + + // Floating particles + ...List.generate(8, (index) { + return Positioned( + left: (index * 45.0) % MediaQuery.of(context).size.width, + top: (index * 80.0) % MediaQuery.of(context).size.height, + child: Transform.translate( + offset: Offset( + math.sin(_backgroundAnimation.value + index * 0.5) * 20, + math.cos(_backgroundAnimation.value + index * 0.3) * 15, + ), + child: Container( + width: 4 + (index % 3) * 2, + height: 4 + (index % 3) * 2, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.white.withOpacity(0.3), + ), + ), + ), + ); + }), + + // Gradient overlay for better text readability + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black.withOpacity(0.1), + Colors.transparent, + ], + ), + ), + ), + ], ); } @@ -127,13 +284,29 @@ class _LoginPageState extends State with TickerProviderStateMixin { style: AppStyle.h1.copyWith( fontWeight: FontWeight.bold, color: AppColor.white, + shadows: [ + Shadow( + offset: const Offset(0, 2), + blurRadius: 10, + color: Colors.black.withOpacity(0.3), + ), + ], ), textAlign: TextAlign.center, ), const SpaceHeight(8), Text( context.lang.login_desc, - style: AppStyle.lg.copyWith(color: AppColor.textLight), + style: AppStyle.lg.copyWith( + color: AppColor.textLight, + shadows: [ + Shadow( + offset: const Offset(0, 1), + blurRadius: 5, + color: Colors.black.withOpacity(0.2), + ), + ], + ), ), ], ); @@ -147,10 +320,17 @@ class _LoginPageState extends State with TickerProviderStateMixin { color: AppColor.white, borderRadius: BorderRadius.circular(24), boxShadow: [ + BoxShadow( + color: AppColor.black.withOpacity(0.15), + blurRadius: 40, + offset: const Offset(0, 20), + spreadRadius: 0, + ), BoxShadow( color: AppColor.black.withOpacity(0.1), - blurRadius: 30, - offset: const Offset(0, 15), + blurRadius: 10, + offset: const Offset(0, 5), + spreadRadius: 0, ), ], ),