fix(recommend): 恢复进度拖动前浮层状态
This commit is contained in:
1 parent
c478eebb0e
commit
b6666290ba
7 files changed
+47
-43
No files matched your search
@@ -1184,5 +1184,5 @@ $ flutter test
|
|||||||
- 剧名 19/w600,箭头 20 白色并紧邻剧名;标签行改为 13/w600 亮灰内联布局。
|
- 剧名 19/w600,箭头 20 白色并紧邻剧名;标签行改为 13/w600 亮灰内联布局。
|
||||||
- 简介「展开/收起」沿用现有三语文案;进度条热区恢复 24 并接入拖动自动清屏与预览卡片。
|
- 简介「展开/收起」沿用现有三语文案;进度条热区恢复 24 并接入拖动自动清屏与预览卡片。
|
||||||
- 拖动期间隐藏操作栏、文字及根底栏,顶部导航常驻;松手后持续清屏直到点击画面。
|
- 拖动期间隐藏操作栏、文字及根底栏,顶部导航常驻;松手后持续清屏直到点击画面。
|
||||||
- 进度条固定中心并动画到 6 高,时间气泡限制在左右 12;按当前流程未执行 analyze/test。
|
- 进度条固定中心并动画到 6 高,时间气泡限制在左右 12;松手恢复拖动前浮层状态。
|
||||||
- 修复 `scrubCancelRequest` 误置于 `initState` 的编译错误,并清理推荐页未使用变量;`flutter analyze` 无问题。
|
- 修复 `scrubCancelRequest` 误置于 `initState` 的编译错误,并清理推荐页未使用变量;`flutter analyze` 无问题。
|
||||||
@@ -18,6 +18,7 @@ abstract final class AppColors {
|
|||||||
static const scrim = Color(0xD1000000);
|
static const scrim = Color(0xD1000000);
|
||||||
static const controlScrim = Color(0x80000000);
|
static const controlScrim = Color(0x80000000);
|
||||||
static const controlSoftScrim = Color(0x42000000);
|
static const controlSoftScrim = Color(0x42000000);
|
||||||
|
static const timeBubbleScrim = Color(0xB3000000);
|
||||||
static const scrubPreviewScrim = Color(0xDE000000);
|
static const scrubPreviewScrim = Color(0xDE000000);
|
||||||
static const overlaySoft = Color(0x66000000);
|
static const overlaySoft = Color(0x66000000);
|
||||||
static const overlayBar = Color(0x4D000000);
|
static const overlayBar = Color(0x4D000000);
|
||||||
|
|||||||
@@ -119,7 +119,6 @@ class _PlayerPageState extends State<PlayerPage>
|
|||||||
double _playbackSpeed = 1.0;
|
double _playbackSpeed = 1.0;
|
||||||
bool _boosting = false;
|
bool _boosting = false;
|
||||||
bool _scrubbing = false;
|
bool _scrubbing = false;
|
||||||
bool _clearScreenFromScrub = false;
|
|
||||||
double? _speedBeforeBoost;
|
double? _speedBeforeBoost;
|
||||||
bool? _chromeBeforeBoost;
|
bool? _chromeBeforeBoost;
|
||||||
|
|
||||||
@@ -315,26 +314,24 @@ class _PlayerPageState extends State<PlayerPage>
|
|||||||
widget.onScrubVisibilityChanged?.call(value);
|
widget.onScrubVisibilityChanged?.call(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _finishFeedScrub() {
|
void _finishFeedScrub(bool wasVisible) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_scrubbing = false;
|
_scrubbing = false;
|
||||||
_chromeVisible = false;
|
_chromeVisible = wasVisible;
|
||||||
_clearScreenFromScrub = true;
|
|
||||||
});
|
});
|
||||||
|
widget.onScrubVisibilityChanged?.call(!wasVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _cancelFeedScrub() {
|
void _cancelFeedScrub() {
|
||||||
logic.cancelScrub();
|
logic.cancelScrub();
|
||||||
_scrubbing = false;
|
_scrubbing = false;
|
||||||
_chromeVisible = true;
|
_chromeVisible = true;
|
||||||
_clearScreenFromScrub = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _restoreFeedChrome() {
|
void _restoreFeedChrome() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_scrubbing = false;
|
_scrubbing = false;
|
||||||
_chromeVisible = true;
|
_chromeVisible = true;
|
||||||
_clearScreenFromScrub = false;
|
|
||||||
});
|
});
|
||||||
widget.onScrubVisibilityChanged?.call(false);
|
widget.onScrubVisibilityChanged?.call(false);
|
||||||
}
|
}
|
||||||
@@ -749,11 +746,7 @@ class _PlayerPageState extends State<PlayerPage>
|
|||||||
? null
|
? null
|
||||||
: () => _openUnlockSheet(context, index, episode),
|
: () => _openUnlockSheet(context, index, episode),
|
||||||
),
|
),
|
||||||
if (widget.homeMode &&
|
if (widget.homeMode && !_chromeVisible && !_boosting && !_scrubbing)
|
||||||
!_chromeVisible &&
|
|
||||||
!_boosting &&
|
|
||||||
!_scrubbing &&
|
|
||||||
!_clearScreenFromScrub)
|
|
||||||
Positioned(
|
Positioned(
|
||||||
right: 12,
|
right: 12,
|
||||||
bottom: 56,
|
bottom: 56,
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ class HomeFeedOverlay extends StatefulWidget {
|
|||||||
final VoidCallback onBoostEnd;
|
final VoidCallback onBoostEnd;
|
||||||
final VoidCallback onMore;
|
final VoidCallback onMore;
|
||||||
final VoidCallback? onScrubStart;
|
final VoidCallback? onScrubStart;
|
||||||
final VoidCallback? onScrubEnd;
|
final ValueChanged<bool>? onScrubEnd;
|
||||||
final ValueChanged<bool>? onScrubVisibilityChanged;
|
final ValueChanged<bool>? onScrubVisibilityChanged;
|
||||||
final ValueChanged<Duration>? onSeek;
|
final ValueChanged<Duration>? onSeek;
|
||||||
final int cancelScrubRequest;
|
final int cancelScrubRequest;
|
||||||
@@ -102,14 +102,13 @@ class HomeFeedOverlay extends StatefulWidget {
|
|||||||
|
|
||||||
class _HomeFeedOverlayState extends State<HomeFeedOverlay> {
|
class _HomeFeedOverlayState extends State<HomeFeedOverlay> {
|
||||||
bool _scrubbing = false;
|
bool _scrubbing = false;
|
||||||
bool _clearAfterScrub = false;
|
bool _wasVisibleBeforeScrub = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(covariant HomeFeedOverlay oldWidget) {
|
void didUpdateWidget(covariant HomeFeedOverlay oldWidget) {
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
if (oldWidget.cancelScrubRequest != widget.cancelScrubRequest) {
|
if (oldWidget.cancelScrubRequest != widget.cancelScrubRequest) {
|
||||||
_scrubbing = false;
|
_scrubbing = false;
|
||||||
_clearAfterScrub = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +137,7 @@ class _HomeFeedOverlayState extends State<HomeFeedOverlay> {
|
|||||||
),
|
),
|
||||||
if (widget.boosting)
|
if (widget.boosting)
|
||||||
Positioned(left: 0, right: 0, bottom: 20, child: _boostHint())
|
Positioned(left: 0, right: 0, bottom: 20, child: _boostHint())
|
||||||
else if (widget.visible && !_scrubbing && !_clearAfterScrub)
|
else if (widget.visible && !_scrubbing)
|
||||||
Positioned(
|
Positioned(
|
||||||
left: _pageHorizontal,
|
left: _pageHorizontal,
|
||||||
right: _railRight,
|
right: _railRight,
|
||||||
@@ -176,31 +175,19 @@ class _HomeFeedOverlayState extends State<HomeFeedOverlay> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _startScrub() {
|
void _startScrub() {
|
||||||
if (!_scrubbing || _clearAfterScrub) {
|
_wasVisibleBeforeScrub = widget.visible;
|
||||||
setState(() {
|
if (!_scrubbing) setState(() => _scrubbing = true);
|
||||||
_scrubbing = true;
|
|
||||||
_clearAfterScrub = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
widget.onScrubVisibilityChanged?.call(true);
|
widget.onScrubVisibilityChanged?.call(true);
|
||||||
widget.onScrubStart?.call();
|
widget.onScrubStart?.call();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _endScrub() {
|
void _endScrub() {
|
||||||
if (_scrubbing || !_clearAfterScrub) {
|
final wasVisible = _wasVisibleBeforeScrub;
|
||||||
setState(() {
|
if (_scrubbing) setState(() => _scrubbing = false);
|
||||||
_scrubbing = false;
|
widget.onScrubEnd?.call(wasVisible);
|
||||||
_clearAfterScrub = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
widget.onScrubEnd?.call();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleSurfaceTap() {
|
void _handleSurfaceTap() {
|
||||||
if (_clearAfterScrub) {
|
|
||||||
setState(() => _clearAfterScrub = false);
|
|
||||||
widget.onScrubVisibilityChanged?.call(false);
|
|
||||||
}
|
|
||||||
widget.onTogglePlay();
|
widget.onTogglePlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ class _FeedProgressBarState extends State<_FeedProgressBar> {
|
|||||||
if (showPreview)
|
if (showPreview)
|
||||||
Positioned(
|
Positioned(
|
||||||
left: _timeBubbleLeft(width, _preview!),
|
left: _timeBubbleLeft(width, _preview!),
|
||||||
bottom: widget.hitHeight + 8,
|
bottom: _lineCenterFromBottom + 16,
|
||||||
child: _ScrubTimeBubble(
|
child: _ScrubTimeBubble(
|
||||||
key: const Key('scrub-preview-time-bubble'),
|
key: const Key('scrub-preview-time-bubble'),
|
||||||
position: previewPosition,
|
position: previewPosition,
|
||||||
@@ -377,9 +377,9 @@ class _ScrubTimeBubble extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
width: _timeBubbleWidth,
|
width: _timeBubbleWidth,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 3),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.overlayMedium,
|
color: AppColors.timeBubbleScrim,
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|||||||
@@ -719,6 +719,28 @@ void main() {
|
|||||||
tester.getRect(find.byKey(const Key('home-feed-gesture'))).right - 12,
|
tester.getRect(find.byKey(const Key('home-feed-gesture'))).right - 12,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
expect(
|
||||||
|
tester
|
||||||
|
.getRect(find.byKey(const Key('home-feed-progress-track')))
|
||||||
|
.center
|
||||||
|
.dy -
|
||||||
|
bubbleRect.bottom,
|
||||||
|
closeTo(16, 0.1),
|
||||||
|
);
|
||||||
|
final bubbleContainer = tester.widget<Container>(
|
||||||
|
find.descendant(
|
||||||
|
of: find.byKey(const Key('scrub-preview-time-bubble')),
|
||||||
|
matching: find.byType(Container),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
(bubbleContainer.decoration! as BoxDecoration).color,
|
||||||
|
AppColors.timeBubbleScrim,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
bubbleContainer.padding,
|
||||||
|
const EdgeInsets.symmetric(horizontal: 6, vertical: 3),
|
||||||
|
);
|
||||||
await gesture.moveBy(const Offset(-600, 0));
|
await gesture.moveBy(const Offset(-600, 0));
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
expect(
|
expect(
|
||||||
@@ -765,8 +787,8 @@ void main() {
|
|||||||
);
|
);
|
||||||
await gesture.up();
|
await gesture.up();
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(scrubVisibility.last, isTrue);
|
expect(scrubVisibility.last, isFalse);
|
||||||
expect(find.byKey(const Key('home-feed-title')), findsNothing);
|
expect(find.byKey(const Key('home-feed-title')), findsOneWidget);
|
||||||
expect(seeked, isNotNull);
|
expect(seeked, isNotNull);
|
||||||
expect(seeked!.inSeconds, greaterThan(0));
|
expect(seeked!.inSeconds, greaterThan(0));
|
||||||
expect(
|
expect(
|
||||||
@@ -778,11 +800,6 @@ void main() {
|
|||||||
2,
|
2,
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.tap(find.byKey(const Key('home-feed-gesture')));
|
|
||||||
await tester.pump();
|
|
||||||
expect(scrubVisibility.last, isFalse);
|
|
||||||
expect(find.byKey(const Key('home-feed-title')), findsOneWidget);
|
|
||||||
|
|
||||||
await tester.tap(find.byKey(const Key('home-feed-full-series')));
|
await tester.tap(find.byKey(const Key('home-feed-full-series')));
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
expect(opened, 1);
|
expect(opened, 1);
|
||||||
|
|||||||
@@ -102,9 +102,15 @@ void main() {
|
|||||||
await tester.pump(const Duration(milliseconds: 130));
|
await tester.pump(const Duration(milliseconds: 130));
|
||||||
expect(find.byKey(const Key('recommend-top-nav')), findsOneWidget);
|
expect(find.byKey(const Key('recommend-top-nav')), findsOneWidget);
|
||||||
expect(tester.getCenter(progress).dy, closeTo(progressCenterY, 0.1));
|
expect(tester.getCenter(progress).dy, closeTo(progressCenterY, 0.1));
|
||||||
|
final navVisibility = find.ancestor(
|
||||||
|
of: find.byKey(const Key('home-tabs-bar')),
|
||||||
|
matching: find.byType(Visibility),
|
||||||
|
);
|
||||||
|
expect(tester.widget<Visibility>(navVisibility.first).visible, isFalse);
|
||||||
await scrub.up();
|
await scrub.up();
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(find.byKey(const Key('recommend-top-nav')), findsOneWidget);
|
expect(find.byKey(const Key('recommend-top-nav')), findsOneWidget);
|
||||||
|
expect(tester.widget<Visibility>(navVisibility.first).visible, isTrue);
|
||||||
|
|
||||||
await tester.tap(find.byKey(const Key('recommend-tab-playlist')));
|
await tester.tap(find.byKey(const Key('recommend-tab-playlist')));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|||||||
Reference in new issue
Block a user