refactor: deduplicate track controls

also fixes double click to fullscreen issues
This commit is contained in:
edde746
2025-10-28 07:59:17 +01:00
parent abd5900290
commit cebe90d39b
+82 -29
View File
@@ -180,6 +180,52 @@ class _PlexVideoControlsState extends State<PlexVideoControls>
return platform == TargetPlatform.iOS || platform == TargetPlatform.android; return platform == TargetPlatform.iOS || platform == TargetPlatform.android;
} }
bool _hasMultipleAudioTracks(Tracks? tracks) {
if (tracks == null) return false;
final audioTracks = tracks.audio
.where((track) => track.id != 'auto' && track.id != 'no')
.toList();
return audioTracks.length > 1;
}
bool _hasSubtitles(Tracks? tracks) {
if (tracks == null) return false;
final subtitles = tracks.subtitle
.where((track) => track.id != 'auto' && track.id != 'no')
.toList();
return subtitles.isNotEmpty;
}
Widget _buildTrackAndChapterControls() {
return StreamBuilder<Tracks>(
stream: widget.player.stream.tracks,
initialData: widget.player.state.tracks,
builder: (context, snapshot) {
final tracks = snapshot.data;
return Row(
mainAxisSize: MainAxisSize.min,
children: [
if (_hasMultipleAudioTracks(tracks))
IconButton(
icon: const Icon(Icons.audiotrack, color: Colors.white),
onPressed: _showAudioBottomSheet,
),
if (_hasSubtitles(tracks))
IconButton(
icon: const Icon(Icons.subtitles, color: Colors.white),
onPressed: _showSubtitleBottomSheet,
),
if (_chapters.isNotEmpty)
IconButton(
icon: const Icon(Icons.video_library, color: Colors.white),
onPressed: _showChapterBottomSheet,
),
],
);
},
);
}
void _seekToPreviousChapter() { void _seekToPreviousChapter() {
if (_chapters.isEmpty) { if (_chapters.isEmpty) {
// No chapters - seek backward 10 seconds // No chapters - seek backward 10 seconds
@@ -300,7 +346,7 @@ class _PlexVideoControlsState extends State<PlexVideoControls>
Positioned.fill( Positioned.fill(
child: GestureDetector( child: GestureDetector(
onTap: _toggleControls, onTap: _toggleControls,
onDoubleTap: _toggleFullscreen, onDoubleTap: isMobile ? null : _toggleFullscreen,
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
child: Container(color: Colors.transparent), child: Container(color: Colors.transparent),
), ),
@@ -314,7 +360,6 @@ class _PlexVideoControlsState extends State<PlexVideoControls>
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
child: GestureDetector( child: GestureDetector(
onTap: _toggleControls, onTap: _toggleControls,
onDoubleTap: _toggleFullscreen,
behavior: HitTestBehavior.deferToChild, behavior: HitTestBehavior.deferToChild,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
@@ -338,6 +383,38 @@ class _PlexVideoControlsState extends State<PlexVideoControls>
), ),
), ),
), ),
// Middle area double-tap detector for fullscreen (desktop only)
// Only covers the clear video area (20% to 80% vertically)
if (!isMobile)
Positioned(
top: 0,
left: 0,
right: 0,
bottom: 0,
child: LayoutBuilder(
builder: (context, constraints) {
final height = constraints.maxHeight;
final topExclude = height * 0.20; // Top 20%
final bottomExclude = height * 0.20; // Bottom 20%
return Stack(
children: [
Positioned(
top: topExclude,
left: 0,
right: 0,
bottom: bottomExclude,
child: GestureDetector(
onDoubleTap: _toggleFullscreen,
behavior: HitTestBehavior.translucent,
child: Container(color: Colors.transparent),
),
),
],
);
},
),
),
], ],
), ),
), ),
@@ -408,18 +485,7 @@ class _PlexVideoControlsState extends State<PlexVideoControls>
), ),
), ),
// Track and chapter controls in top right // Track and chapter controls in top right
IconButton( _buildTrackAndChapterControls(),
icon: const Icon(Icons.audiotrack, color: Colors.white),
onPressed: _showAudioBottomSheet,
),
IconButton(
icon: const Icon(Icons.subtitles, color: Colors.white),
onPressed: _showSubtitleBottomSheet,
),
IconButton(
icon: const Icon(Icons.video_library, color: Colors.white),
onPressed: _showChapterBottomSheet,
),
], ],
), ),
); );
@@ -784,21 +850,8 @@ class _PlexVideoControlsState extends State<PlexVideoControls>
// Volume control // Volume control
_buildVolumeControl(), _buildVolumeControl(),
const SizedBox(width: 16), const SizedBox(width: 16),
// Audio track // Audio track, subtitle, and chapter controls
IconButton( _buildTrackAndChapterControls(),
icon: const Icon(Icons.audiotrack, color: Colors.white),
onPressed: _showAudioBottomSheet,
),
// Subtitle track
IconButton(
icon: const Icon(Icons.subtitles, color: Colors.white),
onPressed: _showSubtitleBottomSheet,
),
// Chapters
IconButton(
icon: const Icon(Icons.video_library, color: Colors.white),
onPressed: _showChapterBottomSheet,
),
// Fullscreen // Fullscreen
IconButton( IconButton(
icon: Icon( icon: Icon(