diff --git a/lib/pages/player/player_view.dart b/lib/pages/player/player_view.dart index 1318d93..49cca34 100644 --- a/lib/pages/player/player_view.dart +++ b/lib/pages/player/player_view.dart @@ -639,6 +639,7 @@ class _PlayerPageState extends State ); return Stack( fit: StackFit.expand, + clipBehavior: Clip.none, children: [ PlayerRenderStack( pageController: _pageController, @@ -662,6 +663,7 @@ class _PlayerPageState extends State ); final body = Stack( fit: StackFit.expand, + clipBehavior: Clip.none, children: [ content, if (widget.homeMode && @@ -971,6 +973,7 @@ class _PlayerPageState extends State return Stack( fit: StackFit.expand, + clipBehavior: Clip.none, children: [ if (selected && !entryLoading && diff --git a/lib/pages/player/widgets/home_feed_overlay.dart b/lib/pages/player/widgets/home_feed_overlay.dart index 2fd6fc0..ce6dc4f 100644 --- a/lib/pages/player/widgets/home_feed_overlay.dart +++ b/lib/pages/player/widgets/home_feed_overlay.dart @@ -126,6 +126,7 @@ class _HomeFeedOverlayState extends State { return SafeArea( bottom: false, child: Stack( + clipBehavior: Clip.none, children: [ Positioned.fill( child: PlayerTapGestureRegion( diff --git a/lib/pages/player/widgets/player_double_tap_hearts.dart b/lib/pages/player/widgets/player_double_tap_hearts.dart index b071442..3cd039b 100644 --- a/lib/pages/player/widgets/player_double_tap_hearts.dart +++ b/lib/pages/player/widgets/player_double_tap_hearts.dart @@ -4,31 +4,32 @@ import 'package:flutter/material.dart'; import '../../../core/theme/app_colors.dart'; -const int _heartDurationMs = 680; +const int _heartDurationMs = 1300; +const int _lastHeartDelayMs = 200; const double _heartPopPortion = 0.18; const double _heartStartScale = 0.7; -const double _heartHoldPortion = 0.45; +const double _heartHoldPortion = 0.65; const List<_HeartParticle> _hearts = [ _HeartParticle( delay: 0, - travel: 108, + travel: 175, drift: 0, - size: 48, + size: 44, color: AppColors.doubleTapHeartRed, ), _HeartParticle( - delay: 50, - travel: 96, - drift: -28, - size: 36, + delay: 110, + travel: 150, + drift: -32, + size: 34, color: AppColors.doubleTapHeartPink, ), _HeartParticle( - delay: 90, - travel: 118, - drift: 26, - size: 40, + delay: _lastHeartDelayMs, + travel: 165, + drift: 30, + size: 38, color: AppColors.doubleTapHeartRed, ), ]; @@ -38,6 +39,10 @@ class PlayerDoubleTapHeartBurst extends StatefulWidget { const PlayerDoubleTapHeartBurst({super.key, required this.onCompleted}); static const int particleCount = 3; + static const int singleHeartDurationMs = _heartDurationMs; + static const Duration animationDuration = Duration( + milliseconds: _lastHeartDelayMs + _heartDurationMs, + ); final VoidCallback onCompleted; diff --git a/lib/pages/player/widgets/player_overlay.dart b/lib/pages/player/widgets/player_overlay.dart index ebf9e54..0e187e3 100644 --- a/lib/pages/player/widgets/player_overlay.dart +++ b/lib/pages/player/widgets/player_overlay.dart @@ -55,6 +55,7 @@ class PlayerPlaybackOverlay extends StatelessWidget { Widget build(BuildContext context) { return SafeArea( child: Stack( + clipBehavior: Clip.none, children: [ Positioned.fill( child: PlayerTapGestureRegion( diff --git a/lib/pages/player/widgets/player_tap_gesture_region.dart b/lib/pages/player/widgets/player_tap_gesture_region.dart index d28fcf7..32a2132 100644 --- a/lib/pages/player/widgets/player_tap_gesture_region.dart +++ b/lib/pages/player/widgets/player_tap_gesture_region.dart @@ -70,6 +70,7 @@ class _PlayerTapGestureRegionState extends State { onPointerDown: _handlePointerDown, child: Stack( fit: StackFit.expand, + clipBehavior: Clip.none, children: [ GestureDetector( key: widget.gestureKey, diff --git a/test/player/double_tap_like_test.dart b/test/player/double_tap_like_test.dart index cc65410..17ee145 100644 --- a/test/player/double_tap_like_test.dart +++ b/test/player/double_tap_like_test.dart @@ -91,7 +91,7 @@ void main() { await tester.tapAt(center); await tester.pump(); expect(repository.likeTargets, hasLength(1)); - await tester.pump(const Duration(milliseconds: 800)); + await tester.pump(const Duration(milliseconds: 1600)); clock.advance(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500)); diff --git a/test/player/player_double_tap_hearts_test.dart b/test/player/player_double_tap_hearts_test.dart index 420e3f4..f22e9dd 100644 --- a/test/player/player_double_tap_hearts_test.dart +++ b/test/player/player_double_tap_hearts_test.dart @@ -13,12 +13,33 @@ void main() { home: Scaffold(body: PlayerDoubleTapHeartBurst(onCompleted: _noop)), ), ); - await tester.pump(const Duration(milliseconds: 160)); + await tester.pump(const Duration(milliseconds: 300)); expect(find.byIcon(Icons.favorite_rounded), findsNWidgets(3)); - expect(_size(tester, 0), 48); - expect(_size(tester, 1), 36); - expect(_size(tester, 2), 40); + expect(_size(tester, 0), 44); + expect(_size(tester, 1), 34); + expect(_size(tester, 2), 38); + expect( + tester + .widgetList( + find.descendant( + of: find.byType(PlayerDoubleTapHeartBurst), + matching: find.byType(Stack), + ), + ) + .every((stack) => stack.clipBehavior == Clip.none), + isTrue, + ); + }); + + test('heart burst stays long and sparse', () { + expect(PlayerDoubleTapHeartBurst.singleHeartDurationMs, 1300); + expect( + PlayerDoubleTapHeartBurst.animationDuration, + greaterThanOrEqualTo(const Duration(milliseconds: 1200)), + ); + expect(PlayerDoubleTapHeartBurst.particleCount, lessThanOrEqualTo(4)); + expect(PlayerDoubleTapHeartBurst.particleCount, 3); }); } diff --git a/test/player/player_fullscreen_test.dart b/test/player/player_fullscreen_test.dart index 54a1d91..84cab43 100644 --- a/test/player/player_fullscreen_test.dart +++ b/test/player/player_fullscreen_test.dart @@ -679,7 +679,7 @@ void main() { await tester.tapAt(position); } await tester.pump(); - await tester.pump(const Duration(milliseconds: 100)); + await tester.pump(const Duration(milliseconds: 300)); expect(logic.session.userWantsPlay, isTrue); expect(find.byKey(const Key('player-play-toggle')), findsNothing); @@ -699,7 +699,7 @@ void main() { AppColors.doubleTapHeartRed, ); - await tester.pump(const Duration(milliseconds: 800)); + await tester.pump(const Duration(milliseconds: 1600)); expect(find.byKey(const Key('double-tap-heart-0')), findsNothing); expect(logic.session.userWantsPlay, isTrue); }); @@ -722,7 +722,7 @@ void main() { await tester.tapAt(const Offset(80, 300)); await tester.tapAt(const Offset(80, 300)); await tester.pump(); - await tester.pump(const Duration(milliseconds: 100)); + await tester.pump(const Duration(milliseconds: 300)); expect(logic.session.userWantsPlay, isTrue); expect(interactions.likeTargets, hasLength(1));