From bb06bd8ac6002a3dd0161d8e378f5ada37ac4d95 Mon Sep 17 00:00:00 2001 From: Sicherhaven Date: Sat, 4 Apr 2026 17:49:37 +0530 Subject: [PATCH] =?UTF-8?q?feat:=20UX-005=20=E2=80=94=20Hero=20transitions?= =?UTF-8?q?,=20fade=20screen=20load,=20AnimatedList=20leaderboard=20stagge?= =?UTF-8?q?r?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/screens/contribute_screen.dart | 12 ++++++++++-- lib/screens/home_screen.dart | 28 +++++++++++++++++----------- lib/screens/learn_more_screen.dart | 30 +++++++++++++++++++++++------- 3 files changed, 50 insertions(+), 20 deletions(-) diff --git a/lib/screens/contribute_screen.dart b/lib/screens/contribute_screen.dart index 68551b5..b955245 100644 --- a/lib/screens/contribute_screen.dart +++ b/lib/screens/contribute_screen.dart @@ -14,6 +14,7 @@ import 'package:share_plus/share_plus.dart'; import '../core/app_decoration.dart'; import '../features/gamification/models/gamification_models.dart'; import '../features/gamification/providers/gamification_provider.dart'; +import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import '../widgets/bouncing_loader.dart'; import '../widgets/glass_card.dart'; import '../widgets/landscape_section_header.dart'; @@ -2146,12 +2147,19 @@ class _ContributeScreenState extends State ), ), - // Ranked list (rank 4+) + // Ranked list (rank 4+) with stagger animation SliverList( delegate: SliverChildBuilderDelegate( (ctx, i) { final entry = entries.length > 3 ? entries[i + 3] : entries[i]; - return _buildRankRow(entry); + return AnimationConfiguration.staggeredList( + position: i, + duration: const Duration(milliseconds: 375), + child: SlideAnimation( + verticalOffset: 40.0, + child: FadeInAnimation(child: _buildRankRow(entry)), + ), + ); }, childCount: entries.length > 3 ? entries.length - 3 : 0, ), diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 7676fdf..d92936f 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -1353,9 +1353,11 @@ class _HomeScreenState extends State with SingleTickerProviderStateM }, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8), - child: ClipRRect( - borderRadius: BorderRadius.circular(radius), - child: Stack( + child: Hero( + tag: 'event-hero-${event.id}', + child: ClipRRect( + borderRadius: BorderRadius.circular(radius), + child: Stack( fit: StackFit.expand, children: [ // ── Layer 0: Event image (full-bleed) ── @@ -1487,6 +1489,7 @@ class _HomeScreenState extends State with SingleTickerProviderStateM ), ), ), + ), ); } @@ -1770,14 +1773,16 @@ class _HomeScreenState extends State with SingleTickerProviderStateM Navigator.push(context, MaterialPageRoute(builder: (_) => LearnMoreScreen(eventId: event.id, initialEvent: event))); } }, - child: Container( - width: 150, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16), - ), - clipBehavior: Clip.antiAlias, - child: Stack( - fit: StackFit.expand, + child: Hero( + tag: 'event-hero-${event.id}', + child: Container( + width: 150, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + ), + clipBehavior: Clip.antiAlias, + child: Stack( + fit: StackFit.expand, children: [ // Background image img != null && img.isNotEmpty @@ -1836,6 +1841,7 @@ class _HomeScreenState extends State with SingleTickerProviderStateM ), ], ), + ), ), ); } diff --git a/lib/screens/learn_more_screen.dart b/lib/screens/learn_more_screen.dart index 8002909..a213ba7 100644 --- a/lib/screens/learn_more_screen.dart +++ b/lib/screens/learn_more_screen.dart @@ -28,9 +28,12 @@ class LearnMoreScreen extends StatefulWidget { State createState() => _LearnMoreScreenState(); } -class _LearnMoreScreenState extends State { +class _LearnMoreScreenState extends State with SingleTickerProviderStateMixin { final EventsService _service = EventsService(); + late final AnimationController _fadeController; + late final Animation _fade; + void _navigateToCheckout() { if (!AuthGuard.requireLogin(context, reason: 'Sign in to book this event.')) return; if (_event == null) return; @@ -68,10 +71,13 @@ class _LearnMoreScreenState extends State { @override void initState() { super.initState(); + _fadeController = AnimationController(vsync: this, duration: const Duration(milliseconds: 350)); + _fade = CurvedAnimation(parent: _fadeController, curve: Curves.easeIn); _pageNotifier = ValueNotifier(0); if (widget.initialEvent != null) { _event = widget.initialEvent; _loading = false; + _fadeController.forward(); WidgetsBinding.instance.addPostFrameCallback((_) { _startAutoScroll(); // Fetch full event details in background to get important_information, images, etc. @@ -88,6 +94,7 @@ class _LearnMoreScreenState extends State { _pageController.dispose(); _pageNotifier.dispose(); _mapController?.dispose(); + _fadeController.dispose(); super.dispose(); } @@ -132,7 +139,10 @@ class _LearnMoreScreenState extends State { if (!mounted) return; setState(() => _error = userFriendlyError(e)); } finally { - if (mounted) setState(() => _loading = false); + if (mounted) { + setState(() => _loading = false); + _fadeController.forward(); + } } } @@ -602,8 +612,10 @@ class _LearnMoreScreenState extends State { ), ) : null, - body: Stack( - children: [ + body: FadeTransition( + opacity: _fade, + child: Stack( + children: [ // ── Scrollable content (carousel + card scroll together) ── SingleChildScrollView( child: Column( @@ -735,6 +747,7 @@ class _LearnMoreScreenState extends State { ), ], ), + ), ); } @@ -876,9 +889,11 @@ class _LearnMoreScreenState extends State { left: 20, right: 20, bottom: 16, - child: ClipRRect( - borderRadius: BorderRadius.circular(20), - child: PageView.builder( + child: Hero( + tag: 'event-hero-${widget.eventId}', + child: ClipRRect( + borderRadius: BorderRadius.circular(20), + child: PageView.builder( controller: _pageController, onPageChanged: (i) => _pageNotifier.value = i, itemCount: images.length, @@ -900,6 +915,7 @@ class _LearnMoreScreenState extends State { ), ), ), + ), // ---- No-image placeholder ---- if (images.isEmpty)