merge: 统一暂停按钮并自动隐藏控制层
This commit is contained in:
commit
e03ea76505
5 files changed
+353
-171
No files matched your search
+228
-170
@@ -24,6 +24,7 @@ import '../../l10n/app_localizations.dart';
|
|||||||
import 'player_binding.dart';
|
import 'player_binding.dart';
|
||||||
import 'player_logic.dart';
|
import 'player_logic.dart';
|
||||||
import 'widgets/home_feed_overlay.dart';
|
import 'widgets/home_feed_overlay.dart';
|
||||||
|
import 'widgets/player_center_toggle.dart';
|
||||||
import 'widgets/player_featured_comment.dart';
|
import 'widgets/player_featured_comment.dart';
|
||||||
import 'widgets/seek_progress_bar.dart';
|
import 'widgets/seek_progress_bar.dart';
|
||||||
import '../../core/data/repositories/watch_history_repository.dart';
|
import '../../core/data/repositories/watch_history_repository.dart';
|
||||||
@@ -318,23 +319,11 @@ class _PlayerPageState extends State<PlayerPage> with WidgetsBindingObserver {
|
|||||||
playing: logic.session.userWantsPlay,
|
playing: logic.session.userWantsPlay,
|
||||||
pip: logic.pip,
|
pip: logic.pip,
|
||||||
))
|
))
|
||||||
const Center(
|
Center(
|
||||||
key: Key('home-feed-center-play'),
|
child: PlayerCenterToggle(
|
||||||
child: IgnorePointer(
|
key: const Key('home-feed-center-play'),
|
||||||
child: DecoratedBox(
|
playing: false,
|
||||||
decoration: BoxDecoration(
|
onTap: () => logic.session.setUserWantsPlay(true),
|
||||||
color: AppColors.blue,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.all(10),
|
|
||||||
child: Icon(
|
|
||||||
Icons.play_arrow_rounded,
|
|
||||||
size: 48,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (logic.pip || logic.pipPreparing)
|
if (logic.pip || logic.pipPreparing)
|
||||||
@@ -571,7 +560,7 @@ class _UnlockCard extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PlaybackOverlay extends StatelessWidget {
|
class _PlaybackOverlay extends StatefulWidget {
|
||||||
const _PlaybackOverlay({
|
const _PlaybackOverlay({
|
||||||
required this.l10n,
|
required this.l10n,
|
||||||
required this.drama,
|
required this.drama,
|
||||||
@@ -614,123 +603,185 @@ class _PlaybackOverlay extends StatelessWidget {
|
|||||||
final VoidCallback? onScrubStart;
|
final VoidCallback? onScrubStart;
|
||||||
final Future<void> Function(Duration)? onSeek;
|
final Future<void> Function(Duration)? onSeek;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_PlaybackOverlay> createState() => _PlaybackOverlayState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PlaybackOverlayState extends State<_PlaybackOverlay> {
|
||||||
|
Timer? _hideTimer;
|
||||||
|
bool _controlsVisible = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_scheduleHide();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_hideTimer?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _scheduleHide() {
|
||||||
|
_hideTimer?.cancel();
|
||||||
|
_hideTimer = Timer(const Duration(milliseconds: 2500), () {
|
||||||
|
if (mounted) setState(() => _controlsVisible = false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _reveal() {
|
||||||
|
setState(() => _controlsVisible = true);
|
||||||
|
_scheduleHide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _togglePlay() {
|
||||||
|
widget.onTogglePlay();
|
||||||
|
_reveal();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onVideoTap() {
|
||||||
|
if (!_controlsVisible) {
|
||||||
|
_reveal();
|
||||||
|
} else {
|
||||||
|
_togglePlay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _run(VoidCallback action) {
|
||||||
|
action();
|
||||||
|
_reveal();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: Column(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
_topBar(context),
|
Positioned.fill(
|
||||||
Expanded(
|
child: GestureDetector(
|
||||||
child: Stack(
|
key: const Key('player-video-tap'),
|
||||||
children: [
|
behavior: HitTestBehavior.opaque,
|
||||||
Center(
|
onTap: _onVideoTap,
|
||||||
child: GestureDetector(
|
|
||||||
key: const Key('player-play-toggle'),
|
|
||||||
onTap: onTogglePlay,
|
|
||||||
child: Container(
|
|
||||||
width: 52,
|
|
||||||
height: 52,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Color(0x80000000),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
playing
|
|
||||||
? Icons.pause_rounded
|
|
||||||
: Icons.play_arrow_rounded,
|
|
||||||
color: AppColors.onPrimary,
|
|
||||||
size: 26,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Positioned(
|
|
||||||
right: 10,
|
|
||||||
bottom: 14,
|
|
||||||
child: Text(
|
|
||||||
'1.0x',
|
|
||||||
style: TextStyle(color: Color(0xFFDDDDDD), fontSize: 9),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
if (_controlsVisible)
|
||||||
padding: const EdgeInsets.fromLTRB(14, 0, 14, 12),
|
Column(
|
||||||
child: Column(
|
key: const Key('player-controls-visible'),
|
||||||
children: [
|
children: [
|
||||||
SeekProgressBar(
|
_topBar(context),
|
||||||
valueListenable: controller,
|
Expanded(
|
||||||
semanticsLabel: l10n.seekVideo,
|
child: Stack(
|
||||||
showTimes: false,
|
children: [
|
||||||
onScrubStart: onScrubStart,
|
Center(
|
||||||
onSeek: onSeek,
|
child: PlayerCenterToggle(
|
||||||
),
|
key: const Key('player-play-toggle'),
|
||||||
const SizedBox(height: 6),
|
playing: widget.playing,
|
||||||
Row(
|
onTap: _togglePlay,
|
||||||
children: [
|
),
|
||||||
Expanded(
|
|
||||||
child: _PlayerAction(
|
|
||||||
key: const Key('player-episodes'),
|
|
||||||
icon: Icons.view_list_rounded,
|
|
||||||
label: l10n.episodes,
|
|
||||||
onTap: onEpisodes,
|
|
||||||
),
|
),
|
||||||
),
|
const Positioned(
|
||||||
const SizedBox(width: 8),
|
right: 10,
|
||||||
Expanded(
|
bottom: 14,
|
||||||
child: _PlayerAction(
|
child: Text(
|
||||||
key: const Key('player-comments'),
|
'1.0x',
|
||||||
icon: Icons.chat_bubble_outline_rounded,
|
style: TextStyle(
|
||||||
label: l10n.comments,
|
color: Color(0xFFDDDDDD),
|
||||||
onTap: onComments,
|
fontSize: 9,
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Expanded(
|
|
||||||
child: SizedBox(
|
|
||||||
height: 32,
|
|
||||||
child: FilledButton(
|
|
||||||
key: const Key('player-next'),
|
|
||||||
onPressed: onNext,
|
|
||||||
style: FilledButton.styleFrom(
|
|
||||||
backgroundColor: AppColors.blue,
|
|
||||||
disabledBackgroundColor: AppColors.raised,
|
|
||||||
disabledForegroundColor: AppColors.subtle,
|
|
||||||
foregroundColor: AppColors.onPrimary,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(9),
|
|
||||||
),
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
textStyle: const TextStyle(
|
|
||||||
fontSize: 11,
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
child: Text(
|
|
||||||
l10n.nextEpisode,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 2),
|
|
||||||
const Icon(Icons.skip_next_rounded, size: 14),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
key: const Key('player-controls-bottom'),
|
||||||
|
padding: const EdgeInsets.fromLTRB(14, 8, 14, 12),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SeekProgressBar(
|
||||||
|
key: const Key('player-progress'),
|
||||||
|
valueListenable: widget.controller,
|
||||||
|
semanticsLabel: widget.l10n.seekVideo,
|
||||||
|
showTimes: false,
|
||||||
|
onScrubStart: widget.onScrubStart,
|
||||||
|
onSeek: widget.onSeek,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: _PlayerAction(
|
||||||
|
key: const Key('player-episodes'),
|
||||||
|
icon: Icons.view_list_rounded,
|
||||||
|
label: widget.l10n.episodes,
|
||||||
|
onTap: () => _run(widget.onEpisodes),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: _PlayerAction(
|
||||||
|
key: const Key('player-comments'),
|
||||||
|
icon: Icons.chat_bubble_outline_rounded,
|
||||||
|
label: widget.l10n.comments,
|
||||||
|
onTap: () => _run(widget.onComments),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: SizedBox(
|
||||||
|
height: 32,
|
||||||
|
child: FilledButton(
|
||||||
|
key: const Key('player-next'),
|
||||||
|
onPressed: widget.onNext == null
|
||||||
|
? null
|
||||||
|
: () => _run(widget.onNext!),
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
backgroundColor: AppColors.blue,
|
||||||
|
disabledBackgroundColor: AppColors.raised,
|
||||||
|
disabledForegroundColor: AppColors.subtle,
|
||||||
|
foregroundColor: AppColors.onPrimary,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(9),
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
textStyle: const TextStyle(
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
widget.l10n.nextEpisode,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 2),
|
||||||
|
const Icon(
|
||||||
|
Icons.skip_next_rounded,
|
||||||
|
size: 14,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
PlayerFeaturedComment(
|
||||||
|
drama: widget.drama,
|
||||||
|
onViewAll: () => _run(widget.onComments),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
|
||||||
PlayerFeaturedComment(drama: drama, onViewAll: onComments),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -738,43 +789,47 @@ class _PlaybackOverlay extends StatelessWidget {
|
|||||||
|
|
||||||
Widget _topBar(BuildContext context) {
|
Widget _topBar(BuildContext context) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
|
key: const Key('player-controls-top'),
|
||||||
height: 46,
|
height: 46,
|
||||||
child: Row(
|
child: Padding(
|
||||||
children: [
|
padding: const EdgeInsets.symmetric(horizontal: 14),
|
||||||
if (onBack != null)
|
child: Row(
|
||||||
_TopAction(
|
children: [
|
||||||
key: const Key('player-back'),
|
if (widget.onBack != null)
|
||||||
tooltip: l10n.backToSeries,
|
_TopAction(
|
||||||
icon: Icons.chevron_left_rounded,
|
key: const Key('player-back'),
|
||||||
onPressed: onBack!,
|
tooltip: widget.l10n.backToSeries,
|
||||||
)
|
icon: Icons.chevron_left_rounded,
|
||||||
else
|
onPressed: () => _run(widget.onBack!),
|
||||||
const SizedBox(width: 28),
|
)
|
||||||
const SizedBox(width: 8),
|
else
|
||||||
Expanded(
|
const SizedBox(width: 28),
|
||||||
child: Text(
|
const SizedBox(width: 8),
|
||||||
'${localizedDramaTitle(l10n, drama)} · '
|
Expanded(
|
||||||
'${l10n.episodeLabel(episode.number)}',
|
child: Text(
|
||||||
key: const Key('player-title'),
|
'${localizedDramaTitle(widget.l10n, widget.drama)} · '
|
||||||
maxLines: 1,
|
'${widget.l10n.episodeLabel(widget.episode.number)}',
|
||||||
overflow: TextOverflow.ellipsis,
|
key: const Key('player-title'),
|
||||||
textAlign: TextAlign.center,
|
maxLines: 1,
|
||||||
style: const TextStyle(
|
overflow: TextOverflow.ellipsis,
|
||||||
fontFamily: 'Outfit',
|
textAlign: TextAlign.center,
|
||||||
color: AppColors.onSurface,
|
style: const TextStyle(
|
||||||
fontSize: 13,
|
fontFamily: 'Outfit',
|
||||||
fontWeight: FontWeight.w700,
|
color: AppColors.onSurface,
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(width: 8),
|
||||||
const SizedBox(width: 8),
|
_TopAction(
|
||||||
_TopAction(
|
key: const Key('player-more'),
|
||||||
key: const Key('player-more'),
|
tooltip: widget.l10n.more,
|
||||||
tooltip: l10n.more,
|
icon: Icons.more_horiz_rounded,
|
||||||
icon: Icons.more_horiz_rounded,
|
onPressed: () => _openMore(context),
|
||||||
onPressed: () => _openMore(context),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -794,62 +849,65 @@ class _PlaybackOverlay extends StatelessWidget {
|
|||||||
Icons.download_outlined,
|
Icons.download_outlined,
|
||||||
color: AppColors.secondary,
|
color: AppColors.secondary,
|
||||||
),
|
),
|
||||||
title: Text(l10n.downloads),
|
title: Text(widget.l10n.downloads),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(sheetContext);
|
Navigator.pop(sheetContext);
|
||||||
onDownload();
|
widget.onDownload();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (onPip != null)
|
if (widget.onPip != null)
|
||||||
ListTile(
|
ListTile(
|
||||||
key: const Key('player-more-pip'),
|
key: const Key('player-more-pip'),
|
||||||
leading: const Icon(
|
leading: const Icon(
|
||||||
Icons.picture_in_picture_alt_outlined,
|
Icons.picture_in_picture_alt_outlined,
|
||||||
color: AppColors.secondary,
|
color: AppColors.secondary,
|
||||||
),
|
),
|
||||||
title: Text(l10n.pictureInPicture),
|
title: Text(widget.l10n.pictureInPicture),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(sheetContext);
|
Navigator.pop(sheetContext);
|
||||||
onPip!();
|
widget.onPip!();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
key: const Key('player-more-like'),
|
key: const Key('player-more-like'),
|
||||||
leading: Icon(
|
leading: Icon(
|
||||||
liked ? Icons.favorite_rounded : Icons.favorite_border_rounded,
|
widget.liked
|
||||||
color: liked ? AppColors.blue : AppColors.muted,
|
? Icons.favorite_rounded
|
||||||
|
: Icons.favorite_border_rounded,
|
||||||
|
color: widget.liked ? AppColors.blue : AppColors.muted,
|
||||||
),
|
),
|
||||||
title: Text(l10n.like),
|
title: Text(widget.l10n.like),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(sheetContext);
|
Navigator.pop(sheetContext);
|
||||||
onLike();
|
widget.onLike();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
key: const Key('player-more-favorite'),
|
key: const Key('player-more-favorite'),
|
||||||
leading: Icon(
|
leading: Icon(
|
||||||
saved ? Icons.star_rounded : Icons.star_border_rounded,
|
widget.saved ? Icons.star_rounded : Icons.star_border_rounded,
|
||||||
color: saved ? AppColors.blue : AppColors.muted,
|
color: widget.saved ? AppColors.blue : AppColors.muted,
|
||||||
),
|
),
|
||||||
title: Text(l10n.favorite),
|
title: Text(widget.l10n.favorite),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(sheetContext);
|
Navigator.pop(sheetContext);
|
||||||
onSave();
|
widget.onSave();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
key: const Key('player-more-share'),
|
key: const Key('player-more-share'),
|
||||||
leading: const Icon(Icons.share_outlined, color: AppColors.muted),
|
leading: const Icon(Icons.share_outlined, color: AppColors.muted),
|
||||||
title: Text(l10n.share),
|
title: Text(widget.l10n.share),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(sheetContext);
|
Navigator.pop(sheetContext);
|
||||||
onShare();
|
widget.onShare();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
if (mounted) _reveal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class HomeFeedOverlay extends StatelessWidget {
|
|||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(0, 0, 10, 12),
|
padding: const EdgeInsets.fromLTRB(0, 0, 12, 16),
|
||||||
child: _rail(),
|
child: _rail(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
// Author: fruit.com.cn
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../../core/theme/app_colors.dart';
|
||||||
|
|
||||||
|
/// Shared play/pause control used by the home feed and full-screen player.
|
||||||
|
class PlayerCenterToggle extends StatelessWidget {
|
||||||
|
const PlayerCenterToggle({
|
||||||
|
super.key,
|
||||||
|
required this.playing,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
final bool playing;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Semantics(
|
||||||
|
button: true,
|
||||||
|
label: playing ? 'Pause' : 'Play',
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Container(
|
||||||
|
width: 52,
|
||||||
|
height: 52,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: Color(0x80000000),
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
playing ? Icons.pause_rounded : Icons.play_arrow_rounded,
|
||||||
|
color: AppColors.onPrimary,
|
||||||
|
size: 26,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Author: fruit.com.cn
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:fruitshorts/pages/player/widgets/player_center_toggle.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('should_use_one_shared_52px_style_for_home_and_player', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: Row(
|
||||||
|
children: [
|
||||||
|
PlayerCenterToggle(
|
||||||
|
key: const Key('home-feed-center-play'),
|
||||||
|
playing: false,
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
PlayerCenterToggle(
|
||||||
|
key: const Key('player-play-toggle'),
|
||||||
|
playing: true,
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (final key in const [
|
||||||
|
Key('home-feed-center-play'),
|
||||||
|
Key('player-play-toggle'),
|
||||||
|
]) {
|
||||||
|
expect(tester.getSize(find.byKey(key)), const Size(52, 52));
|
||||||
|
final container = tester.widget<Container>(
|
||||||
|
find.descendant(of: find.byKey(key), matching: find.byType(Container)),
|
||||||
|
);
|
||||||
|
final decoration = container.decoration! as BoxDecoration;
|
||||||
|
expect(decoration.color, const Color(0x80000000));
|
||||||
|
expect(decoration.shape, BoxShape.circle);
|
||||||
|
final icon = tester.widget<Icon>(
|
||||||
|
find.descendant(of: find.byKey(key), matching: find.byType(Icon)),
|
||||||
|
);
|
||||||
|
expect(icon.size, 26);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import 'package:fruitshorts/core/data/repositories/favorite_repository.dart';
|
|||||||
import 'package:fruitshorts/core/data/repositories/playback_repository.dart';
|
import 'package:fruitshorts/core/data/repositories/playback_repository.dart';
|
||||||
import 'package:fruitshorts/core/data/repositories/watch_history_repository.dart';
|
import 'package:fruitshorts/core/data/repositories/watch_history_repository.dart';
|
||||||
import 'package:fruitshorts/pages/player/player_view.dart';
|
import 'package:fruitshorts/pages/player/player_view.dart';
|
||||||
|
import 'package:fruitshorts/pages/player/widgets/player_center_toggle.dart';
|
||||||
|
|
||||||
import '../support/fake_catalog.dart';
|
import '../support/fake_catalog.dart';
|
||||||
import '../support/fake_category.dart';
|
import '../support/fake_category.dart';
|
||||||
@@ -90,6 +91,39 @@ void main() {
|
|||||||
expect(find.byIcon(Icons.play_arrow_rounded), findsOneWidget);
|
expect(find.byIcon(Icons.play_arrow_rounded), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('should_hide_controls_after_2_5s_and_reveal_on_video_tap', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
|
await _pumpApp(tester);
|
||||||
|
await tester.binding.handlePopRoute();
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.binding.handlePopRoute();
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.byKey(const Key('drawer-open-series')));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.byKey(const Key('detail-cta')));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.byKey(const Key('player-controls-top')), findsOneWidget);
|
||||||
|
expect(find.byKey(const Key('player-controls-bottom')), findsOneWidget);
|
||||||
|
expect(find.byType(PlayerCenterToggle), findsOneWidget);
|
||||||
|
|
||||||
|
await tester.pump(const Duration(milliseconds: 2600));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.byKey(const Key('player-controls-top')), findsNothing);
|
||||||
|
expect(find.byKey(const Key('player-controls-bottom')), findsNothing);
|
||||||
|
expect(find.byType(PlayerCenterToggle), findsNothing);
|
||||||
|
expect(find.byKey(const Key('player-video-tap')), findsOneWidget);
|
||||||
|
|
||||||
|
await tester.tap(find.byKey(const Key('player-video-tap')));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.byKey(const Key('player-controls-top')), findsOneWidget);
|
||||||
|
expect(find.byKey(const Key('player-controls-bottom')), findsOneWidget);
|
||||||
|
expect(find.byType(PlayerCenterToggle), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
test('should_only_show_feed_pause_indicator_in_home_mode', () {
|
test('should_only_show_feed_pause_indicator_in_home_mode', () {
|
||||||
expect(
|
expect(
|
||||||
shouldShowFeedPauseIndicator(
|
shouldShowFeedPauseIndicator(
|
||||||
|
|||||||
Reference in new issue
Block a user