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'; import '../../../components/button/button.dart'; import '../../../components/spacer/spacer.dart'; import '../../../router/app_router.gr.dart'; import 'widgets/email_field.dart'; import 'widgets/password_field.dart'; @RoutePage() class LoginPage extends StatefulWidget { const LoginPage({super.key}); @override State createState() => _LoginPageState(); } class _LoginPageState extends State with TickerProviderStateMixin { final _formKey = GlobalKey(); final _emailController = TextEditingController(); final _passwordController = TextEditingController(); bool _isLoading = false; 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() { super.initState(); _fadeController = AnimationController( duration: const Duration(milliseconds: 1500), vsync: this, ); _slideController = AnimationController( duration: const Duration(milliseconds: 1200), 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), ); _slideAnimation = 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(); } @override void dispose() { _fadeController.dispose(); _slideController.dispose(); _backgroundController.dispose(); _floatingController.dispose(); _emailController.dispose(); _passwordController.dispose(); super.dispose(); } Future _handleLogin() async { if (_formKey.currentState!.validate()) { setState(() { _isLoading = true; }); // Simulasi proses login await Future.delayed(const Duration(seconds: 2)); setState(() { _isLoading = false; }); context.router.replace(const MainRoute()); } } @override Widget build(BuildContext context) { return Scaffold( 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, ], ), ), ), ], ); } Widget _buildLogo(BuildContext context) { return Column( children: [ Text( context.lang.login_header, 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, shadows: [ Shadow( offset: const Offset(0, 1), blurRadius: 5, color: Colors.black.withOpacity(0.2), ), ], ), ), ], ); } Widget _buildLoginCard(BuildContext context) { return Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 24), decoration: BoxDecoration( 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: 10, offset: const Offset(0, 5), spreadRadius: 0, ), ], ), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ LoginEmailField(controller: _emailController), const SpaceHeight(24), LoginPasswordField(controller: _passwordController), const SpaceHeight(16), _buildForgetPassword(context), const SpaceHeight(32), _buildLoginButton(), ], ), ), ); } Widget _buildForgetPassword(BuildContext context) { return Align( alignment: Alignment.centerRight, child: GestureDetector( onTap: () {}, child: Text( '${context.lang.forgot_password}?', style: AppStyle.md.copyWith( color: AppColor.primary, fontWeight: FontWeight.w600, ), ), ), ); } Widget _buildLoginButton() { return AppElevatedButton( text: context.lang.sign_in, isLoading: _isLoading, onPressed: _isLoading ? null : _handleLogin, ); } }