From 644531500d42591072fb338810aff0ec2383355a Mon Sep 17 00:00:00 2001 From: efrilm Date: Wed, 13 Aug 2025 00:02:35 +0700 Subject: [PATCH] feat: update profile --- lib/presentation/pages/home/home_page.dart | 6 +- .../pages/profile/profile_page.dart | 101 ++++-- .../pages/profile/widgets/app_setting.dart | 55 ++- .../pages/profile/widgets/header.dart | 321 +++++++++++++++--- 4 files changed, 367 insertions(+), 116 deletions(-) diff --git a/lib/presentation/pages/home/home_page.dart b/lib/presentation/pages/home/home_page.dart index a314379..7c08b08 100644 --- a/lib/presentation/pages/home/home_page.dart +++ b/lib/presentation/pages/home/home_page.dart @@ -106,7 +106,11 @@ class _HomePageState extends State with TickerProviderStateMixin { ], ), ), - titlePadding: const EdgeInsets.only(left: 16, bottom: 16), + titlePadding: const EdgeInsets.only( + left: 20, + right: 12, + bottom: 16, + ), background: AnimatedBuilder( animation: _headerAnimationController, builder: (context, child) { diff --git a/lib/presentation/pages/profile/profile_page.dart b/lib/presentation/pages/profile/profile_page.dart index 51c95c5..0949d45 100644 --- a/lib/presentation/pages/profile/profile_page.dart +++ b/lib/presentation/pages/profile/profile_page.dart @@ -1,7 +1,9 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:line_icons/line_icons.dart'; import '../../../common/theme/theme.dart'; +import '../../components/button/button.dart'; import '../../components/spacer/spacer.dart'; import 'widgets/account_info.dart'; import 'widgets/app_setting.dart'; @@ -23,43 +25,76 @@ class _ProfilePageState extends State { Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColor.background, - appBar: AppBar( - title: Text( - 'Profile', - style: AppStyle.xxl.copyWith( - color: AppColor.textWhite, - fontWeight: FontWeight.w600, + body: CustomScrollView( + slivers: [ + SliverAppBar( + backgroundColor: AppColor.primary, + elevation: 0, + pinned: true, + expandedHeight: 264.0, + flexibleSpace: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + // Calculate the collapse ratio + final double top = constraints.biggest.height; + final double collapsedHeight = + MediaQuery.of(context).padding.top + kToolbarHeight; + final double expandedHeight = 264.0; + final double shrinkRatio = + ((expandedHeight - top) / + (expandedHeight - collapsedHeight)) + .clamp(0.0, 1.0); + + return FlexibleSpaceBar( + background: ProfileHeader(), + titlePadding: const EdgeInsets.only( + left: 20, + right: 12, + bottom: 16, + ), + title: Opacity( + opacity: shrinkRatio, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Profile', + style: AppStyle.xl.copyWith( + fontWeight: FontWeight.w700, + fontSize: 18, + letterSpacing: -0.5, + color: AppColor.white, + ), + ), + ActionIconButton( + onTap: () {}, + icon: LineIcons.userEdit, + ), + ], + ), + ), + ); + }, + ), ), - ), - backgroundColor: AppColor.primary, - elevation: 0, - actions: [ - IconButton( - icon: const Icon(Icons.edit, color: AppColor.textWhite), - onPressed: () { - // Navigate to edit profile - }, + SliverToBoxAdapter( + child: Column( + children: [ + const SpaceHeight(20), + ProfileAccountInfo(), + const SpaceHeight(12), + ProfileBusinessSetting(), + const SpaceHeight(12), + ProfileAppSetting(), + const SpaceHeight(12), + ProfileSupport(), + const SpaceHeight(12), + ProfileDangerZone(), + const SpaceHeight(30), + ], + ), ), ], ), - body: SingleChildScrollView( - child: Column( - children: [ - ProfileHeader(), - const SpaceHeight(20), - ProfileAccountInfo(), - const SpaceHeight(12), - ProfileBusinessSetting(), - const SpaceHeight(12), - ProfileAppSetting(), - const SpaceHeight(12), - ProfileSupport(), - const SpaceHeight(12), - ProfileDangerZone(), - const SpaceHeight(30), - ], - ), - ), ); } } diff --git a/lib/presentation/pages/profile/widgets/app_setting.dart b/lib/presentation/pages/profile/widgets/app_setting.dart index 16b8e44..8f34b27 100644 --- a/lib/presentation/pages/profile/widgets/app_setting.dart +++ b/lib/presentation/pages/profile/widgets/app_setting.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import '../../../../common/theme/theme.dart'; import 'divider.dart'; -import 'profile_switch_tile.dart'; import 'profile_tile.dart'; class ProfileAppSetting extends StatefulWidget { @@ -13,8 +12,8 @@ class ProfileAppSetting extends StatefulWidget { } class _ProfileAppSettingState extends State { - bool _notificationsEnabled = true; - bool _darkModeEnabled = false; + // bool _notificationsEnabled = true; + // bool _darkModeEnabled = false; @override Widget build(BuildContext context) { @@ -46,34 +45,32 @@ class _ProfileAppSettingState extends State { ), ), - ProfileSwitchTile( - icon: Icons.notifications_outlined, - title: 'Notifications', - subtitle: 'Receive app notifications', - value: _notificationsEnabled, - onChanged: (value) { - setState(() { - _notificationsEnabled = value; - }); - }, - ), + // ProfileSwitchTile( + // icon: Icons.notifications_outlined, + // title: 'Notifications', + // subtitle: 'Receive app notifications', + // value: _notificationsEnabled, + // onChanged: (value) { + // setState(() { + // _notificationsEnabled = value; + // }); + // }, + // ), - ProfileDivider(), - - ProfileSwitchTile( - icon: Icons.dark_mode_outlined, - title: 'Dark Mode', - subtitle: 'Switch to dark theme', - value: _darkModeEnabled, - onChanged: (value) { - setState(() { - _darkModeEnabled = value; - }); - }, - ), - - ProfileDivider(), + // ProfileDivider(), + // ProfileSwitchTile( + // icon: Icons.dark_mode_outlined, + // title: 'Dark Mode', + // subtitle: 'Switch to dark theme', + // value: _darkModeEnabled, + // onChanged: (value) { + // setState(() { + // _darkModeEnabled = value; + // }); + // }, + // ), + // ProfileDivider(), ProfileTile( icon: Icons.language_outlined, title: 'Language', diff --git a/lib/presentation/pages/profile/widgets/header.dart b/lib/presentation/pages/profile/widgets/header.dart index 8688b9a..a8ebe71 100644 --- a/lib/presentation/pages/profile/widgets/header.dart +++ b/lib/presentation/pages/profile/widgets/header.dart @@ -3,76 +3,291 @@ import 'package:flutter/material.dart'; import '../../../../common/theme/theme.dart'; import '../../../components/spacer/spacer.dart'; -class ProfileHeader extends StatelessWidget { +class ProfileHeader extends StatefulWidget { const ProfileHeader({super.key}); + @override + State createState() => _ProfileHeaderState(); +} + +class _ProfileHeaderState extends State + with SingleTickerProviderStateMixin { + late AnimationController _animationController; + late Animation _fadeInAnimation; + late Animation _slideAnimation; + late Animation _scaleAnimation; + + @override + void initState() { + super.initState(); + _animationController = AnimationController( + duration: const Duration(milliseconds: 1200), + vsync: this, + ); + + _fadeInAnimation = Tween(begin: 0.0, end: 1.0).animate( + CurvedAnimation( + parent: _animationController, + curve: const Interval(0.0, 0.6, curve: Curves.easeOut), + ), + ); + + _slideAnimation = + Tween(begin: const Offset(0, 0.5), end: Offset.zero).animate( + CurvedAnimation( + parent: _animationController, + curve: const Interval(0.2, 0.8, curve: Curves.easeOutCubic), + ), + ); + + _scaleAnimation = Tween(begin: 0.8, end: 1.0).animate( + CurvedAnimation( + parent: _animationController, + curve: const Interval(0.0, 0.7, curve: Curves.elasticOut), + ), + ); + + _animationController.forward(); + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return Container( width: double.infinity, - decoration: const BoxDecoration( + decoration: BoxDecoration( gradient: LinearGradient( - colors: AppColor.primaryGradient, - begin: Alignment.topCenter, - end: Alignment.bottomCenter, + 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: Column( + child: Stack( + alignment: Alignment.center, children: [ - const SpaceHeight(20), - - // Profile Picture - Container( - width: 100, - height: 100, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all(color: AppColor.textWhite, width: 3), - color: AppColor.primaryLight, + // 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: ClipOval( - child: Container( - color: AppColor.primaryLight, - child: const Icon( - Icons.person, - size: 50, - color: AppColor.textWhite, + ), + + // Main content + Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SpaceHeight(30), + + // 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( + width: 120, + height: 120, + decoration: BoxDecoration( + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: AppColor.textWhite.withOpacity(0.3), + blurRadius: 20, + spreadRadius: 5, + ), + ], + ), + ), + // 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, + ), + ), + ), + ), + // Online status indicator + Positioned( + bottom: 5, + right: 5, + child: Container( + width: 24, + height: 24, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.green, + border: Border.all( + color: AppColor.textWhite, + width: 3, + ), + ), + ), + ), + ], + ), + ); + }, + ), + + 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), + ), + ), + ], + ), ), ), - ), - ), - const SpaceHeight(16), + const SpaceHeight(12), - // Name - Text( - 'John Doe', - style: AppStyle.h5.copyWith( - fontSize: 24, - fontWeight: FontWeight.bold, - color: AppColor.textWhite, - ), - ), - - const SpaceHeight(4), - - // Role Badge - Container( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), - decoration: BoxDecoration( - color: AppColor.textWhite.withOpacity(0.2), - borderRadius: BorderRadius.circular(20), - ), - child: Text( - 'Business Owner', - style: AppStyle.md.copyWith( - color: AppColor.textWhite, - fontWeight: FontWeight.w500, + // 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), + const SpaceHeight(30), + ], + ), ], ), );