diff --git a/docs/plans/PROGRESS.md b/docs/plans/PROGRESS.md index b98bd9f..8a02993 100644 --- a/docs/plans/PROGRESS.md +++ b/docs/plans/PROGRESS.md @@ -1177,3 +1177,10 @@ $ flutter test - 新增三语“观看全集”文案;按当前流程未执行 analyze/test。 - 榜单行支持可选排名:有名次显示 `TOPn·热播榜`,无名次回退“热播榜”;热度值改用 `w` 格式。 - 题材改为空格分隔,集数固定右侧单行;右侧操作栏改为距屏幕边缘 8,底部位置不变。 + +## 2026-10-22 推荐页底部信息二次修正 + +- 按最新反馈移除整行榜单与热度胶囊,信息顺序收敛为标题、剧名、标签、简介、观看全集。 +- 剧名 19/w600,箭头 20 白色并紧邻剧名;标签行改为 13/w600 亮灰内联布局。 +- 简介「展开/收起」沿用现有三语文案;进度条热区恢复 24 并接入拖动自动清屏与预览卡片。 +- 拖动期间隐藏顶部导航、操作栏、文字及根底栏,松手恢复;按当前流程未执行 analyze/test。 diff --git a/lib/core/theme/app_colors.dart b/lib/core/theme/app_colors.dart index 26e9aac..7d55946 100644 --- a/lib/core/theme/app_colors.dart +++ b/lib/core/theme/app_colors.dart @@ -24,6 +24,7 @@ abstract final class AppColors { static const overlayMedium = Color(0x99000000); static const headerScrim = Color(0x59000000); static const overlaySecondary = Color(0xFFC7C7CC); + static const overlayLabel = Color(0xFFE6E6EB); /// 主操作、选中态、播放进度(品牌橙)。 static const blue = Color(0xFFFF6B2C); diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index a19e4ac..f5b44c3 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -749,8 +749,5 @@ "watchedToEpisode": "Watched to Episode {episode}", "@watchedToEpisode": { "placeholders": { "episode": { "type": "int" } } }, "homeWatchAll": "Watch all", - "hotChartLabel": "Trending", - "hotChartRanked": "TOP{rank} · Trending", - "@hotChartRanked": { "placeholders": { "rank": { "type": "int" } } }, "confirmRemove": "Remove this item?" } diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb index 185ecbe..025c1e8 100644 --- a/lib/l10n/app_id.arb +++ b/lib/l10n/app_id.arb @@ -706,8 +706,5 @@ "watchedToEpisode": "Ditonton hingga Episode {episode}", "@watchedToEpisode": { "placeholders": { "episode": { "type": "int" } } }, "homeWatchAll": "Tonton semua", - "hotChartLabel": "Peringkat populer", - "hotChartRanked": "TOP{rank} · Populer", - "@hotChartRanked": { "placeholders": { "rank": { "type": "int" } } }, "confirmRemove": "Hapus item ini?" } diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index e610f00..2b3ae8a 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -3262,18 +3262,6 @@ abstract class AppLocalizations { /// **'Watch all'** String get homeWatchAll; - /// No description provided for @hotChartLabel. - /// - /// In en, this message translates to: - /// **'Trending'** - String get hotChartLabel; - - /// No description provided for @hotChartRanked. - /// - /// In en, this message translates to: - /// **'TOP{rank} · Trending'** - String hotChartRanked(int rank); - /// No description provided for @confirmRemove. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 1d25160..ef3ebad 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -1702,14 +1702,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get homeWatchAll => 'Watch all'; - @override - String get hotChartLabel => 'Trending'; - - @override - String hotChartRanked(int rank) { - return 'TOP$rank · Trending'; - } - @override String get confirmRemove => 'Remove this item?'; } diff --git a/lib/l10n/app_localizations_id.dart b/lib/l10n/app_localizations_id.dart index e807c46..f76ec32 100644 --- a/lib/l10n/app_localizations_id.dart +++ b/lib/l10n/app_localizations_id.dart @@ -1704,14 +1704,6 @@ class AppLocalizationsId extends AppLocalizations { @override String get homeWatchAll => 'Tonton semua'; - @override - String get hotChartLabel => 'Peringkat populer'; - - @override - String hotChartRanked(int rank) { - return 'TOP$rank · Populer'; - } - @override String get confirmRemove => 'Hapus item ini?'; } diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index ffba839..b99bb6e 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -1678,14 +1678,6 @@ class AppLocalizationsZh extends AppLocalizations { @override String get homeWatchAll => '观看全集'; - @override - String get hotChartLabel => '热播榜'; - - @override - String hotChartRanked(int rank) { - return 'TOP$rank·热播榜'; - } - @override String get confirmRemove => '确定移除这条内容吗?'; } diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 795c67b..e98372e 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -668,8 +668,5 @@ "watchedToEpisode": "已看至 第 {episode} 集", "@watchedToEpisode": { "placeholders": { "episode": { "type": "int" } } }, "homeWatchAll": "观看全集", - "hotChartLabel": "热播榜", - "hotChartRanked": "TOP{rank}·热播榜", - "@hotChartRanked": { "placeholders": { "rank": { "type": "int" } } }, "confirmRemove": "确定移除这条内容吗?" } diff --git a/lib/pages/home/home_view.dart b/lib/pages/home/home_view.dart index d8e309e..4586dbb 100644 --- a/lib/pages/home/home_view.dart +++ b/lib/pages/home/home_view.dart @@ -288,6 +288,7 @@ class _HomeTabsState extends State<_HomeTabs> { active: logic.tab == 1, onSearch: () => _openSearch(context), onOpenFilter: () => _pushPage(context, const FilterPage()), + onScrubVisibilityChanged: logic.onImmersiveChanged, ); } diff --git a/lib/pages/player/player_view.dart b/lib/pages/player/player_view.dart index 2197109..ea3026a 100644 --- a/lib/pages/player/player_view.dart +++ b/lib/pages/player/player_view.dart @@ -69,6 +69,7 @@ class PlayerPage extends StatefulWidget { this.showHomeFeedHeader = true, this.onDramaChanged, this.onImmersiveChanged, + this.onScrubVisibilityChanged, this.onOpenFullPlayer, this.onRefreshFeed, this.onLoadMore, @@ -94,6 +95,7 @@ class PlayerPage extends StatefulWidget { final bool showHomeFeedHeader; final ValueChanged? onDramaChanged; final ValueChanged? onImmersiveChanged; + final ValueChanged? onScrubVisibilityChanged; final Future Function(Drama drama, Episode episode)? onOpenFullPlayer; final Future Function()? onRefreshFeed; final Future Function()? onLoadMore; @@ -114,6 +116,7 @@ class _PlayerPageState extends State bool _chromeVisible = true; double _playbackSpeed = 1.0; bool _boosting = false; + bool _scrubbing = false; double? _speedBeforeBoost; bool? _chromeBeforeBoost; @@ -296,6 +299,12 @@ class _PlayerPageState extends State unawaited(logic.setPlaybackSpeed(speed)); } + void _setScrubbing(bool value) { + if (_scrubbing == value) return; + setState(() => _scrubbing = value); + widget.onScrubVisibilityChanged?.call(value); + } + /// 收藏状态确认后同步右侧计数(总数没有独立计数接口,按 ±1 跟随)。 void _applyFavoriteDelta(String dramaId, {required bool nowSaved}) { setState(() { @@ -364,6 +373,7 @@ class _PlayerPageState extends State content, if (widget.homeMode && widget.showHomeFeedHeader && + !_scrubbing && !_boosting && !logic.pip && !logic.pipPreparing) @@ -624,6 +634,7 @@ class _PlayerPageState extends State onEpisodes: () => logic.showEpisodes(context), onMore: _openCurrentMore, onScrubStart: ready ? () => logic.beginScrub(videoPort!) : null, + onScrubVisibilityChanged: _setScrubbing, onSeek: ready ? (position) => logic.seekTo(context, videoPort!, position) : null, @@ -695,7 +706,7 @@ class _PlayerPageState extends State ? null : () => _openUnlockSheet(context, index, episode), ), - if (widget.homeMode && !_chromeVisible && !_boosting) + if (widget.homeMode && !_chromeVisible && !_boosting && !_scrubbing) Positioned( right: 12, bottom: 56, @@ -706,6 +717,7 @@ class _PlayerPageState extends State ), ), if (widget.homeMode && + !_scrubbing && !logic.chromeHidden && selected && !logic.session.userWantsPlay) diff --git a/lib/pages/player/widgets/home_feed_overlay.dart b/lib/pages/player/widgets/home_feed_overlay.dart index 92b000e..e2906b7 100644 --- a/lib/pages/player/widgets/home_feed_overlay.dart +++ b/lib/pages/player/widgets/home_feed_overlay.dart @@ -19,7 +19,7 @@ import 'seek_progress_bar.dart'; part 'home_feed_overlay_parts.dart'; const double _progressHeight = 2; -const double _progressHitHeight = 12; +const double _progressHitHeight = 24; const double _pageHorizontal = 14; const double _railRight = 8; const double _railGap = 12; @@ -54,10 +54,11 @@ class HomeFeedOverlay extends StatefulWidget { required this.onMore, this.likeCount, this.favoriteCount, - this.rankingRank, this.commentCount, this.authorNote = '', this.onScrubStart, + this.onScrubEnd, + this.onScrubVisibilityChanged, this.onSeek, this.thumbnailService, this.spriteAxisService, @@ -71,7 +72,6 @@ class HomeFeedOverlay extends StatefulWidget { final bool liked; final int? likeCount; final int? favoriteCount; - final int? rankingRank; final int? commentCount; final bool saved; final String authorNote; @@ -89,6 +89,8 @@ class HomeFeedOverlay extends StatefulWidget { final VoidCallback onBoostEnd; final VoidCallback onMore; final VoidCallback? onScrubStart; + final VoidCallback? onScrubEnd; + final ValueChanged? onScrubVisibilityChanged; final ValueChanged? onSeek; final PlaybackThumbnailService? thumbnailService; final SpriteThumbnailAxisService? spriteAxisService; @@ -98,6 +100,8 @@ class HomeFeedOverlay extends StatefulWidget { } class _HomeFeedOverlayState extends State { + bool _scrubbing = false; + @override Widget build(BuildContext context) { return SafeArea( @@ -123,7 +127,7 @@ class _HomeFeedOverlayState extends State { ), if (widget.boosting) Positioned(left: 0, right: 0, bottom: 20, child: _boostHint()) - else if (widget.visible) + else if (widget.visible && !_scrubbing) Positioned( left: _pageHorizontal, right: _railRight, @@ -149,7 +153,8 @@ class _HomeFeedOverlayState extends State { locked: widget.locked, height: _progressHeight, hitHeight: _progressHitHeight, - onScrubStart: widget.onScrubStart, + onScrubStart: _startScrub, + onScrubEnd: _endScrub, onSeek: widget.onSeek, thumbnailService: widget.thumbnailService, spriteAxisService: widget.spriteAxisService, @@ -163,6 +168,18 @@ class _HomeFeedOverlayState extends State { ); } + void _startScrub() { + if (!_scrubbing) setState(() => _scrubbing = true); + widget.onScrubVisibilityChanged?.call(true); + widget.onScrubStart?.call(); + } + + void _endScrub() { + if (_scrubbing) setState(() => _scrubbing = false); + widget.onScrubVisibilityChanged?.call(false); + widget.onScrubEnd?.call(); + } + void _handleLongPressStart(LongPressStartDetails details, double width) { final third = width / 3; final dx = details.localPosition.dx; @@ -220,7 +237,6 @@ class _HomeFeedOverlayState extends State { episode: widget.episode, onDetail: widget.onDetail, onOpenFullPlayer: widget.onOpenFullPlayer, - rankingRank: widget.rankingRank, ); } } diff --git a/lib/pages/player/widgets/home_feed_overlay_parts.dart b/lib/pages/player/widgets/home_feed_overlay_parts.dart index 522b535..4a7c6dc 100644 --- a/lib/pages/player/widgets/home_feed_overlay_parts.dart +++ b/lib/pages/player/widgets/home_feed_overlay_parts.dart @@ -16,6 +16,7 @@ class _FeedProgressBar extends StatefulWidget { required this.height, required this.hitHeight, this.onScrubStart, + this.onScrubEnd, this.onSeek, this.thumbnailService, this.spriteAxisService, @@ -29,6 +30,7 @@ class _FeedProgressBar extends StatefulWidget { final double height; final double hitHeight; final VoidCallback? onScrubStart; + final VoidCallback? onScrubEnd; final ValueChanged? onSeek; final PlaybackThumbnailService? thumbnailService; final SpriteThumbnailAxisService? spriteAxisService; @@ -137,8 +139,18 @@ class _FeedProgressBarState extends State<_FeedProgressBar> { onHorizontalDragUpdate: interactive ? (details) => _update(details.localPosition.dx / width) : null, - onHorizontalDragEnd: interactive ? (_) => _commit(_preview) : null, - onHorizontalDragCancel: interactive ? _cancelPreview : null, + onHorizontalDragEnd: interactive + ? (_) { + widget.onScrubEnd?.call(); + _commit(_preview); + } + : null, + onHorizontalDragCancel: interactive + ? () { + widget.onScrubEnd?.call(); + _cancelPreview(); + } + : null, child: Stack( clipBehavior: Clip.none, children: [ diff --git a/lib/pages/player/widgets/home_feed_recommend_panel.dart b/lib/pages/player/widgets/home_feed_recommend_panel.dart index 1fd69b3..5d92347 100644 --- a/lib/pages/player/widgets/home_feed_recommend_panel.dart +++ b/lib/pages/player/widgets/home_feed_recommend_panel.dart @@ -10,9 +10,8 @@ import '../../../l10n/app_localizations.dart'; import '../../home/widgets/home_drama_classification.dart'; const double _headlineFont = 26; -const double _seriesFont = 17; -const double _metaFont = 12; -const double _tagFont = 12; +const double _seriesFont = 19; +const double _tagFont = 13; const double _synopsisFont = 13; const double _watchAllHeight = 44; const double _actionGap = 28; @@ -30,14 +29,12 @@ class HomeFeedInfoPanel extends StatefulWidget { required this.episode, required this.onDetail, required this.onOpenFullPlayer, - this.rankingRank, }); final Drama drama; final Episode episode; final VoidCallback onDetail; final VoidCallback onOpenFullPlayer; - final int? rankingRank; @override State createState() => _HomeFeedInfoPanelState(); @@ -73,15 +70,14 @@ class _HomeFeedInfoPanelState extends State { ), ), const SizedBox(height: 8), - _metaPills(l10n), - if (_hasMeta()) const SizedBox(height: 8), GestureDetector( key: const Key('home-feed-series-name'), behavior: HitTestBehavior.opaque, onTap: widget.onDetail, child: Row( + mainAxisSize: MainAxisSize.min, children: [ - Expanded( + Flexible( child: Text( dramaTitle, maxLines: 1, @@ -94,10 +90,11 @@ class _HomeFeedInfoPanelState extends State { ), ), ), + const SizedBox(width: 4), const Icon( Icons.chevron_right_rounded, - size: 16, - color: AppColors.overlaySecondary, + size: 20, + color: AppColors.textPrimary, ), ], ), @@ -114,81 +111,6 @@ class _HomeFeedInfoPanelState extends State { ); } - bool _hasMeta() { - return isHotDrama(widget.drama) || widget.drama.viewCount > 0; - } - - Widget _metaPills(AppLocalizations l10n) { - final hot = isHotDrama(widget.drama); - final views = widget.drama.viewCount; - final rank = widget.rankingRank; - if (!hot && views <= 0) return const SizedBox.shrink(); - return Wrap( - key: const Key('home-feed-meta-pills'), - spacing: 6, - runSpacing: 4, - children: [ - if (hot) - _pill( - key: const Key('home-feed-ranking-pill'), - background: AppColors.error, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon( - Icons.local_fire_department_rounded, - size: 12, - color: AppColors.textPrimary, - ), - const SizedBox(width: 2), - Text( - rank != null && rank > 0 - ? l10n.hotChartRanked(rank) - : l10n.hotChartLabel, - style: const TextStyle( - color: AppColors.textPrimary, - fontSize: _metaFont, - fontWeight: FontWeight.w600, - ), - ), - ], - ), - ), - if (views > 0) - _pill( - key: const Key('home-feed-view-pill'), - background: AppColors.overlaySoft, - child: Text( - formatPlayCount(views), - style: const TextStyle( - color: AppColors.textPrimary, - fontSize: _metaFont, - fontWeight: FontWeight.w600, - ), - ), - ), - ], - ); - } - - Widget _pill({ - required Key key, - required Color background, - required Widget child, - }) { - return Container( - key: key, - height: 22, - padding: const EdgeInsets.symmetric(horizontal: 8), - alignment: Alignment.center, - decoration: BoxDecoration( - color: background, - borderRadius: BorderRadius.circular(11), - ), - child: child, - ); - } - Widget _tagMetaRow(AppLocalizations l10n, List genres) { return Row( key: const Key('home-feed-tags'), @@ -210,20 +132,21 @@ class _HomeFeedInfoPanelState extends State { ), ), ), - const SizedBox(width: 6), + const SizedBox(width: 8), ], - Expanded( + Flexible( + fit: FlexFit.loose, child: Text( genres.join(' '), maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( - color: AppColors.overlaySecondary, + color: AppColors.overlayLabel, fontSize: _tagFont, ), ), ), - const SizedBox(width: 6), + const SizedBox(width: 8), Text( l10n.favoriteEpisodeProgress( widget.episode.number, @@ -233,8 +156,8 @@ class _HomeFeedInfoPanelState extends State { maxLines: 1, softWrap: false, style: const TextStyle( - color: AppColors.overlaySecondary, - fontSize: _metaFont, + color: AppColors.overlayLabel, + fontSize: _tagFont, ), ), ], @@ -298,7 +221,7 @@ class _HomeFeedInfoPanelState extends State { bottom: 0, child: _synopsisToggle( key: const Key('home-feed-synopsis-more'), - label: l10n.more, + label: l10n.expandSynopsis, ), ), ], diff --git a/lib/pages/recommend/recommend_page.dart b/lib/pages/recommend/recommend_page.dart index 75d2d15..098bfa6 100644 --- a/lib/pages/recommend/recommend_page.dart +++ b/lib/pages/recommend/recommend_page.dart @@ -28,12 +28,14 @@ class RecommendPage extends StatefulWidget { required this.active, required this.onSearch, required this.onOpenFilter, + required this.onScrubVisibilityChanged, }); final HomeFeedController feedController; final bool active; final VoidCallback onSearch; final VoidCallback onOpenFilter; + final ValueChanged onScrubVisibilityChanged; @override State createState() => _RecommendPageState(); @@ -41,6 +43,7 @@ class RecommendPage extends StatefulWidget { class _RecommendPageState extends State { RecommendInnerTab _tab = RecommendInnerTab.feed; + bool _scrubbing = false; @override Widget build(BuildContext context) { @@ -77,20 +80,21 @@ class _RecommendPageState extends State { ), ), ), - Positioned( - top: 0, - left: 0, - right: 0, - child: SafeArea( - bottom: false, - child: RecommendTopNav( - selected: _tab, - onVideo: _tab == RecommendInnerTab.feed, - onSelected: (tab) => setState(() => _tab = tab), - onSearch: widget.onSearch, + if (!_scrubbing) + Positioned( + top: 0, + left: 0, + right: 0, + child: SafeArea( + bottom: false, + child: RecommendTopNav( + selected: _tab, + onVideo: _tab == RecommendInnerTab.feed, + onSelected: (tab) => setState(() => _tab = tab), + onSearch: widget.onSearch, + ), ), ), - ), ], ), ); @@ -136,9 +140,16 @@ class _RecommendPageState extends State { _openDrama(drama, episodeId: episode.id), onRefreshFeed: widget.feedController.refreshCurrent, onLoadMore: widget.feedController.loadMore, + onScrubVisibilityChanged: _setScrubbing, ); } + void _setScrubbing(bool value) { + if (_scrubbing == value) return; + setState(() => _scrubbing = value); + widget.onScrubVisibilityChanged(value); + } + Widget _withTopNavSpace(Widget child) { return SafeArea( bottom: false, diff --git a/test/home/home_preview_layout_test.dart b/test/home/home_preview_layout_test.dart index a34c27d..f6a9c88 100644 --- a/test/home/home_preview_layout_test.dart +++ b/test/home/home_preview_layout_test.dart @@ -23,6 +23,7 @@ import 'package:fruitshorts/l10n/app_localizations.dart'; import 'package:fruitshorts/pages/home/home_feed_channel.dart'; import 'package:fruitshorts/pages/player/widgets/home_feed_header.dart'; import 'package:fruitshorts/pages/player/widgets/home_feed_overlay.dart'; +import 'package:fruitshorts/pages/player/widgets/scrub_preview_card.dart'; import 'package:fruitshorts/pages/player/player_view.dart'; import 'package:video_player/video_player.dart'; @@ -355,6 +356,23 @@ void main() { 'Ignored Episode Title', ); expect(find.text('Feed Drama'), findsOneWidget); + final seriesTitle = tester.widget(find.text('Feed Drama')); + expect(seriesTitle.style?.fontSize, 19); + expect(seriesTitle.style?.fontWeight, FontWeight.w600); + expect(seriesTitle.style?.color, AppColors.textPrimary); + final seriesChevron = tester.widget( + find.descendant( + of: find.byKey(const Key('home-feed-series-name')), + matching: find.byIcon(Icons.chevron_right_rounded), + ), + ); + expect(seriesChevron.size, 20); + expect(seriesChevron.color, AppColors.textPrimary); + expect( + tester.getTopLeft(find.byIcon(Icons.chevron_right_rounded)).dx - + tester.getTopRight(find.text('Feed Drama')).dx, + 4, + ); expect(find.byKey(const Key('home-feed-info-card')), findsNothing); expect(find.byKey(const Key('home-feed-full-series')), findsOneWidget); expect(find.byKey(const Key('home-feed-tags')), findsOneWidget); @@ -371,16 +389,27 @@ void main() { .data, 'Episode 2 / 8 total', ); - expect(find.textContaining('41.9w'), findsOneWidget); - expect(find.byKey(const Key('home-feed-ranking-pill')), findsOneWidget); - expect(find.byKey(const Key('home-feed-view-pill')), findsOneWidget); - expect(find.text('Trending'), findsOneWidget); + expect(find.byKey(const Key('home-feed-meta-pills')), findsNothing); + expect(find.byKey(const Key('home-feed-ranking-pill')), findsNothing); + expect(find.byKey(const Key('home-feed-view-pill')), findsNothing); expect(find.text('Romance Urban'), findsOneWidget); + final genreText = tester.widget(find.text('Romance Urban')); + expect(genreText.style?.fontSize, 13); + expect(genreText.style?.color, AppColors.overlayLabel); final episodeProgress = tester.widget( find.byKey(const Key('home-feed-episode-progress')), ); expect(episodeProgress.maxLines, 1); expect(episodeProgress.softWrap, isFalse); + expect(episodeProgress.style?.fontSize, 13); + expect(episodeProgress.style?.color, AppColors.overlayLabel); + expect( + tester + .getTopLeft(find.byKey(const Key('home-feed-episode-progress'))) + .dx - + tester.getTopRight(find.text('Romance Urban')).dx, + 8, + ); expect( tester.widget(find.byKey(const Key('rail-like-count'))).data, '1006', @@ -567,6 +596,9 @@ void main() { testWidgets('should_seek_and_open_player_from_feed_actions', (tester) async { var opened = 0; Duration? seeked; + final scrubVisibility = []; + final thumbnailService = _nullThumbnailService(); + addTearDown(thumbnailService.dispose); final controller = ValueNotifier( VideoPlayerValue( duration: const Duration(seconds: 100), @@ -616,7 +648,9 @@ void main() { onOpenFullPlayer: () => opened++, onMore: () {}, onScrubStart: () {}, + onScrubVisibilityChanged: scrubVisibility.add, onSeek: (value) => seeked = value, + thumbnailService: thumbnailService, ), ), ), @@ -647,7 +681,7 @@ void main() { ); expect( tester.getSize(find.byKey(const Key('home-feed-progress'))).height, - 12, + greaterThanOrEqualTo(24), ); expect( tester @@ -668,6 +702,10 @@ void main() { ); await gesture.moveBy(const Offset(120, 0)); await tester.pump(); + expect(scrubVisibility.last, isTrue); + expect(find.byKey(const Key('home-feed-title')), findsNothing); + expect(find.byKey(const Key('rail-save')), findsNothing); + expect(find.byType(ScrubPreviewCard), findsOneWidget); expect( tester.getSize(find.byKey(const Key('home-feed-progress-thumb'))), const Size(14, 14), @@ -691,6 +729,8 @@ void main() { ); await gesture.up(); await tester.pumpAndSettle(); + expect(scrubVisibility.last, isFalse); + expect(find.byKey(const Key('home-feed-title')), findsOneWidget); expect(seeked, isNotNull); expect(seeked!.inSeconds, greaterThan(0)); expect(