From d334bb8f170c2b50af90db1629740df039de8861 Mon Sep 17 00:00:00 2001 From: efrilm Date: Sat, 16 Aug 2025 00:12:30 +0700 Subject: [PATCH] feat: update profile home --- lib/common/painter/wave_painter.dart | 42 ++ .../pages/profile/widgets/header.dart | 509 +++++++++++------- 2 files changed, 363 insertions(+), 188 deletions(-) create mode 100644 lib/common/painter/wave_painter.dart diff --git a/lib/common/painter/wave_painter.dart b/lib/common/painter/wave_painter.dart new file mode 100644 index 0000000..750a7f5 --- /dev/null +++ b/lib/common/painter/wave_painter.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'dart:math' as math; + +class WavePainter extends CustomPainter { + final double animation; + final Color color; + + WavePainter({required this.animation, required this.color}); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = color + ..strokeWidth = 1.5 + ..style = PaintingStyle.stroke; + + final path = Path(); + + // Create simplified wave patterns (reduced from 3 to 2 waves) + for (int i = 0; i < 2; i++) { + path.reset(); + final double waveHeight = 15 + (i * 8); + final double frequency = 0.015 + (i * 0.008); + final double phase = animation + (i * math.pi / 2); + + path.moveTo(0, size.height * 0.4 + (i * 40)); + + for (double x = 0; x <= size.width; x += 8) { + final y = + size.height * 0.4 + + (i * 40) + + math.sin((x * frequency) + phase) * waveHeight; + path.lineTo(x, y); + } + + canvas.drawPath(path, paint..color = color.withOpacity(0.2 - (i * 0.05))); + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => true; +} diff --git a/lib/presentation/pages/profile/widgets/header.dart b/lib/presentation/pages/profile/widgets/header.dart index a8ebe71..9377c8d 100644 --- a/lib/presentation/pages/profile/widgets/header.dart +++ b/lib/presentation/pages/profile/widgets/header.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import 'dart:math' as math; +import '../../../../common/painter/wave_painter.dart'; import '../../../../common/theme/theme.dart'; import '../../../components/spacer/spacer.dart'; @@ -11,20 +13,45 @@ class ProfileHeader extends StatefulWidget { } class _ProfileHeaderState extends State - with SingleTickerProviderStateMixin { + with TickerProviderStateMixin { late AnimationController _animationController; + late AnimationController _particleController; + late AnimationController _waveController; + late AnimationController _breathController; + late Animation _fadeInAnimation; late Animation _slideAnimation; late Animation _scaleAnimation; + late Animation _particleAnimation; + late Animation _waveAnimation; + late Animation _breathAnimation; @override void initState() { super.initState(); + + // Main content animations _animationController = AnimationController( duration: const Duration(milliseconds: 1200), vsync: this, ); + _particleController = AnimationController( + duration: const Duration(seconds: 8), + vsync: this, + )..repeat(); + + _waveController = AnimationController( + duration: const Duration(seconds: 6), + vsync: this, + )..repeat(); + + _breathController = AnimationController( + duration: const Duration(seconds: 4), + vsync: this, + )..repeat(reverse: true); + + // Content animations _fadeInAnimation = Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation( parent: _animationController, @@ -47,82 +74,179 @@ class _ProfileHeaderState extends State ), ); + _particleAnimation = Tween( + begin: 0.0, + end: 2 * math.pi, + ).animate(_particleController); + + _waveAnimation = Tween( + begin: 0.0, + end: 2 * math.pi, + ).animate(_waveController); + + _breathAnimation = Tween(begin: 0.8, end: 1.2).animate( + CurvedAnimation(parent: _breathController, curve: Curves.easeInOut), + ); + _animationController.forward(); } @override void dispose() { _animationController.dispose(); + _particleController.dispose(); + _waveController.dispose(); + _breathController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { - return Container( - width: double.infinity, - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - ...AppColor.primaryGradient, - AppColor.primaryGradient.last.withOpacity(0.8), - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ), - boxShadow: [ - BoxShadow( - color: AppColor.primaryGradient.first.withOpacity(0.3), - blurRadius: 20, - offset: const Offset(0, 10), + return AnimatedBuilder( + animation: Listenable.merge([ + _particleController, + _waveController, + _breathController, + ]), + builder: (context, child) { + return Container( + width: double.infinity, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + ...AppColor.primaryGradient, + AppColor.primaryGradient.last.withOpacity(0.8), + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + boxShadow: [ + BoxShadow( + color: AppColor.primaryGradient.first.withOpacity(0.3), + blurRadius: 20, + offset: const Offset(0, 10), + ), + ], ), - ], - ), - child: Stack( - alignment: Alignment.center, - children: [ - // Background decoration with floating circles - ...List.generate( - 6, - (index) => Positioned( - top: (index * 30.0) + 20, - right: (index * 40.0) - 50, - child: AnimatedBuilder( - animation: _animationController, - builder: (context, child) { - return Transform.rotate( - angle: _animationController.value * 2 * 3.14159 * 0.5, - child: Container( - width: 20 + (index * 5.0), - height: 20 + (index * 5.0), - decoration: BoxDecoration( - shape: BoxShape.circle, - color: AppColor.textWhite.withOpacity(0.1), - ), + child: Stack( + alignment: Alignment.center, + children: [ + // Simplified animated background + _buildAnimatedBackground(), + + // Main content + _buildMainContent(), + ], + ), + ); + }, + ); + } + + Widget _buildAnimatedBackground() { + return Stack( + children: [ + // Floating particles with orbital motion + ...List.generate(12, (index) { + final double radius = 80 + (index * 20); + final double angle = _particleAnimation.value + (index * 0.5); + final double centerX = MediaQuery.of(context).size.width * 0.5; + final double centerY = 150; + + return Positioned( + left: centerX + math.cos(angle) * radius - 6, + top: centerY + math.sin(angle) * (radius * 0.6) - 6, + child: Transform.scale( + scale: _breathAnimation.value * 0.5, + child: Container( + width: 3 + (index % 4), + height: 3 + (index % 4), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: AppColor.textWhite.withOpacity(0.6), + boxShadow: [ + BoxShadow( + color: AppColor.textWhite.withOpacity(0.3), + blurRadius: 8, + spreadRadius: 1, ), - ); - }, + ], + ), ), ), + ); + }), + + // Wave patterns + Positioned.fill( + child: CustomPaint( + painter: WavePainter( + animation: _waveAnimation.value, + color: AppColor.textWhite.withOpacity(0.1), + ), ), + ), - // Main content - Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SpaceHeight(30), + // Sparkle effects + ...List.generate(6, (index) { + return Positioned( + left: (index * 80.0) % MediaQuery.of(context).size.width, + top: 50 + (index * 30.0), + child: Transform.rotate( + angle: _particleAnimation.value * 2 + index, + child: Transform.scale( + scale: math.sin(_particleAnimation.value + index) * 0.5 + 1, + child: Icon( + Icons.auto_awesome, + size: 12 + (index % 3) * 4, + color: AppColor.textWhite.withOpacity(0.4), + ), + ), + ), + ); + }), - // Profile Picture with enhanced design - AnimatedBuilder( - animation: _scaleAnimation, - builder: (context, child) { - return Transform.scale( - scale: _scaleAnimation.value, - child: Stack( - alignment: Alignment.center, - children: [ - // Outer glow effect - Container( + // Gradient overlay for depth + Container( + decoration: BoxDecoration( + gradient: RadialGradient( + center: const Alignment(0, -0.3), + radius: 1.2, + colors: [ + Colors.transparent, + AppColor.primaryGradient.first.withOpacity(0.1), + Colors.transparent, + ], + ), + ), + ), + ], + ); + } + + Widget _buildMainContent() { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SpaceHeight(30), + + // Profile Picture with simplified design + AnimatedBuilder( + animation: _scaleAnimation, + builder: (context, child) { + return Transform.scale( + scale: _scaleAnimation.value, + child: Stack( + alignment: Alignment.center, + children: [ + // Outer glow effect with breathing animation + AnimatedBuilder( + animation: _breathAnimation, + builder: (context, child) { + return Transform.scale( + scale: _breathAnimation.value * 0.1 + 1, + child: Container( width: 120, height: 120, decoration: BoxDecoration( @@ -136,49 +260,54 @@ class _ProfileHeaderState extends State ], ), ), - // Profile picture container - Container( - width: 110, - height: 110, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: AppColor.textWhite, - width: 4, - ), - gradient: LinearGradient( - colors: [ - AppColor.primaryLight, - AppColor.primaryLight.withOpacity(0.8), - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ), - ), - child: ClipOval( - child: Container( - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - AppColor.primaryLight, - AppColor.primaryLight.withOpacity(0.7), - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), - ), - child: const Icon( - Icons.person, - size: 55, - color: AppColor.textWhite, - ), - ), + ); + }, + ), + // Profile picture container + Container( + width: 110, + height: 110, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all(color: AppColor.textWhite, width: 4), + gradient: LinearGradient( + colors: [ + AppColor.primaryLight, + AppColor.primaryLight.withOpacity(0.8), + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + ), + child: ClipOval( + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + AppColor.primaryLight, + AppColor.primaryLight.withOpacity(0.7), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, ), ), - // Online status indicator - Positioned( - bottom: 5, - right: 5, + child: const Icon( + Icons.person, + size: 55, + color: AppColor.textWhite, + ), + ), + ), + ), + // Online status indicator with subtle pulse + Positioned( + bottom: 5, + right: 5, + child: AnimatedBuilder( + animation: _breathController, + builder: (context, child) { + return Transform.scale( + scale: _breathAnimation.value * 0.2 + 1, child: Container( width: 24, height: 24, @@ -189,107 +318,111 @@ class _ProfileHeaderState extends State color: AppColor.textWhite, width: 3, ), + boxShadow: [ + BoxShadow( + color: Colors.green.withOpacity(0.6), + blurRadius: 8, + spreadRadius: 2, + ), + ], ), ), - ), - ], + ); + }, ), - ); - }, + ), + ], ), + ); + }, + ), - const SpaceHeight(20), + const SpaceHeight(20), - // Name with animation - SlideTransition( - position: _slideAnimation, - child: FadeTransition( - opacity: _fadeInAnimation, - child: Column( - children: [ - Text( - 'John Doe', - style: AppStyle.h5.copyWith( - fontWeight: FontWeight.bold, - color: AppColor.textWhite, - shadows: [ - Shadow( - color: Colors.black.withOpacity(0.3), - offset: const Offset(0, 2), - blurRadius: 4, - ), - ], - ), - ), - const SpaceHeight(4), - Container( - height: 2, - width: 60, - decoration: BoxDecoration( - color: AppColor.textWhite.withOpacity(0.7), - borderRadius: BorderRadius.circular(1), - ), + // Name with animation + SlideTransition( + position: _slideAnimation, + child: FadeTransition( + opacity: _fadeInAnimation, + child: Column( + children: [ + Text( + 'John Doe', + style: AppStyle.h5.copyWith( + fontWeight: FontWeight.bold, + color: AppColor.textWhite, + shadows: [ + Shadow( + color: Colors.black.withOpacity(0.3), + offset: const Offset(0, 2), + blurRadius: 4, ), ], ), ), - ), - - const SpaceHeight(12), - - // Enhanced Role Badge - FadeTransition( - opacity: _fadeInAnimation, - child: SlideTransition( - position: _slideAnimation, - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 20, - vertical: 10, - ), - decoration: BoxDecoration( - color: AppColor.textWhite.withOpacity(0.25), - borderRadius: BorderRadius.circular(25), - border: Border.all( - color: AppColor.textWhite.withOpacity(0.3), - width: 1, - ), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.1), - blurRadius: 10, - offset: const Offset(0, 4), - ), - ], - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.business_center, - size: 16, - color: AppColor.textWhite.withOpacity(0.9), - ), - const SpaceHeight(6), - Text( - 'Business Owner', - style: AppStyle.md.copyWith( - color: AppColor.textWhite, - fontWeight: FontWeight.w600, - letterSpacing: 0.5, - ), - ), - ], - ), + const SpaceHeight(4), + Container( + height: 2, + width: 60, + decoration: BoxDecoration( + color: AppColor.textWhite.withOpacity(0.7), + borderRadius: BorderRadius.circular(1), ), ), - ), - - const SpaceHeight(30), - ], + ], + ), ), - ], - ), + ), + + const SpaceHeight(12), + + // Enhanced Role Badge + FadeTransition( + opacity: _fadeInAnimation, + child: SlideTransition( + position: _slideAnimation, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), + decoration: BoxDecoration( + color: AppColor.textWhite.withOpacity(0.25), + borderRadius: BorderRadius.circular(25), + border: Border.all( + color: AppColor.textWhite.withOpacity(0.3), + width: 1, + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 10, + offset: const Offset(0, 4), + ), + ], + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.business_center, + size: 16, + color: AppColor.textWhite.withOpacity(0.9), + ), + const SpaceHeight(6), + Text( + 'Business Owner', + style: AppStyle.md.copyWith( + color: AppColor.textWhite, + fontWeight: FontWeight.w600, + letterSpacing: 0.5, + ), + ), + ], + ), + ), + ), + ), + + const SpaceHeight(30), + ], ); } }