From 7c07c001cd5ef1050a427bf85ee8bb66f3921525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Delestre?= Date: Wed, 21 Oct 2026 20:48:14 +0200 Subject: [PATCH] fix(mobile): keep header image when starting selection (#32356) * fix(mobile): keep header image when starting selection * fix(mobile): fade out header actions during selection Hide the back button and person options menu with the same fade as the timeline app bar, and block taps on the person name and birthday while assets are selected. --------- Co-authored-by: Santo Shakil (cherry picked from commit 745bdd2edf72ce0f75e748e2d68c37b6ea196cc5) --- .../common/mesmerizing_sliver_app_bar.dart | 105 +++++++------- .../widgets/common/person_sliver_app_bar.dart | 130 +++++++++--------- 2 files changed, 119 insertions(+), 116 deletions(-) diff --git a/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart b/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart index dd14221fc..2da8263b3 100644 --- a/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart @@ -36,64 +36,63 @@ class _MesmerizingSliverAppBarState extends ConsumerState s.isEnabled)); + Widget hideWhenSelecting(Widget child) => IgnorePointer( + ignoring: isMultiSelectEnabled, + child: AnimatedOpacity(duration: Durations.medium1, opacity: isMultiSelectEnabled ? 0 : 1, child: child), + ); - return isMultiSelectEnabled - ? SliverToBoxAdapter( - child: switch (_scrollProgress) { - < 0.8 => const SizedBox(height: 120), - _ => const SizedBox(height: 352), - }, - ) - : SliverAppBar( - expandedHeight: 300.0, - floating: false, - pinned: true, - snap: false, - elevation: 0, - leading: IconButton( - icon: Icon( - Platform.isIOS ? Icons.arrow_back_ios_new_rounded : Icons.arrow_back, - color: Color.lerp(Colors.white, context.primaryColor, _scrollProgress), - shadows: [ - _scrollProgress < 0.95 - ? Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5)) - : const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), - ], - ), - onPressed: () { - context.pop(); - }, - ), - flexibleSpace: Builder( - builder: (context) { - final settings = context.dependOnInheritedWidgetOfExactType(); - final scrollProgress = _calculateScrollProgress(settings); + return SliverAppBar( + expandedHeight: 300.0, + floating: false, + pinned: true, + snap: false, + elevation: 0, + leading: hideWhenSelecting( + IconButton( + icon: Icon( + Platform.isIOS ? Icons.arrow_back_ios_new_rounded : Icons.arrow_back, + color: Color.lerp(Colors.white, context.primaryColor, _scrollProgress), + shadows: [ + _scrollProgress < 0.95 + ? Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5)) + : const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), + ], + ), + onPressed: () { + context.pop(); + }, + ), + ), + flexibleSpace: Builder( + builder: (context) { + final settings = context.dependOnInheritedWidgetOfExactType(); + final scrollProgress = _calculateScrollProgress(settings); - // Update scroll progress for the leading button - WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted && _scrollProgress != scrollProgress) { - setState(() { - _scrollProgress = scrollProgress; - }); - } - }); + // Update scroll progress for the leading button + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted && _scrollProgress != scrollProgress) { + setState(() { + _scrollProgress = scrollProgress; + }); + } + }); - return FlexibleSpaceBar( - centerTitle: true, - title: AnimatedSwitcher( - duration: const Duration(milliseconds: 200), - child: scrollProgress > 0.95 - ? Text( - widget.title, - style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18), - ) - : null, - ), - background: _ExpandedBackground(scrollProgress: scrollProgress, title: widget.title), - ); - }, + return FlexibleSpaceBar( + centerTitle: true, + title: AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: scrollProgress > 0.95 + ? Text( + widget.title, + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18), + ) + : null, ), + background: _ExpandedBackground(scrollProgress: scrollProgress, title: widget.title), ); + }, + ), + ); } } diff --git a/mobile/lib/widgets/common/person_sliver_app_bar.dart b/mobile/lib/widgets/common/person_sliver_app_bar.dart index d26ca789e..4d759f0cd 100644 --- a/mobile/lib/widgets/common/person_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/person_sliver_app_bar.dart @@ -54,6 +54,10 @@ class _MesmerizingSliverAppBarState extends ConsumerState { @override Widget build(BuildContext context) { final isMultiSelectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + Widget hideWhenSelecting(Widget child) => IgnorePointer( + ignoring: isMultiSelectEnabled, + child: AnimatedOpacity(duration: Durations.medium1, opacity: isMultiSelectEnabled ? 0 : 1, child: child), + ); final Color? actionIconColor = Color.lerp(Colors.white, context.primaryColor, _scrollProgress); final List actionIconShadows = [ if (_scrollProgress < 0.95) @@ -62,74 +66,74 @@ class _MesmerizingSliverAppBarState extends ConsumerState { const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), ]; - return isMultiSelectEnabled - ? SliverToBoxAdapter( - child: switch (_scrollProgress) { - < 0.8 => const SizedBox(height: 120), - _ => const SizedBox(height: 352), - }, - ) - : SliverAppBar( - expandedHeight: 300.0, - floating: false, - pinned: true, - snap: false, - elevation: 0, - leading: IconButton( - icon: Icon( - Platform.isIOS ? Icons.arrow_back_ios_new_rounded : Icons.arrow_back, - color: Color.lerp(Colors.white, context.primaryColor, _scrollProgress), - shadows: [ - _scrollProgress < 0.95 - ? Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5)) - : const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), - ], - ), - onPressed: () { - context.pop(); - }, - ), - actions: [ - IconButton( - icon: Icon(Icons.more_vert, color: actionIconColor, shadows: actionIconShadows), - onPressed: widget.onShowOptions, - ), + return SliverAppBar( + expandedHeight: 300.0, + floating: false, + pinned: true, + snap: false, + elevation: 0, + leading: hideWhenSelecting( + IconButton( + icon: Icon( + Platform.isIOS ? Icons.arrow_back_ios_new_rounded : Icons.arrow_back, + color: Color.lerp(Colors.white, context.primaryColor, _scrollProgress), + shadows: [ + _scrollProgress < 0.95 + ? Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5)) + : const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), ], - flexibleSpace: Builder( - builder: (context) { - final settings = context.dependOnInheritedWidgetOfExactType(); - final scrollProgress = _calculateScrollProgress(settings); + ), + onPressed: () { + context.pop(); + }, + ), + ), + actions: [ + hideWhenSelecting( + IconButton( + icon: Icon(Icons.more_vert, color: actionIconColor, shadows: actionIconShadows), + onPressed: widget.onShowOptions, + ), + ), + ], + flexibleSpace: Builder( + builder: (context) { + final settings = context.dependOnInheritedWidgetOfExactType(); + final scrollProgress = _calculateScrollProgress(settings); - // Update scroll progress for the leading button - WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted && _scrollProgress != scrollProgress) { - setState(() { - _scrollProgress = scrollProgress; - }); - } - }); + // Update scroll progress for the leading button + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted && _scrollProgress != scrollProgress) { + setState(() { + _scrollProgress = scrollProgress; + }); + } + }); - return FlexibleSpaceBar( - centerTitle: true, - title: AnimatedSwitcher( - duration: const Duration(milliseconds: 200), - child: scrollProgress > 0.95 - ? Text( - widget.person.name, - style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18), - ) - : null, - ), - background: _ExpandedBackground( - scrollProgress: scrollProgress, - person: widget.person, - onNameTap: widget.onNameTap, - onBirthdayTap: widget.onBirthdayTap, - ), - ); - }, + return FlexibleSpaceBar( + centerTitle: true, + title: AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: scrollProgress > 0.95 + ? Text( + widget.person.name, + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18), + ) + : null, + ), + background: IgnorePointer( + ignoring: isMultiSelectEnabled, + child: _ExpandedBackground( + scrollProgress: scrollProgress, + person: widget.person, + onNameTap: widget.onNameTap, + onBirthdayTap: widget.onBirthdayTap, + ), ), ); + }, + ), + ); } }