refactor: wrap buttons in semantics

This commit is contained in:
edde746
2025-11-18 14:44:00 +01:00
parent 6417a37366
commit 9af87d2e02
4 changed files with 101 additions and 43 deletions
+1 -5
View File
@@ -152,11 +152,7 @@ class _AppBarBackButtonState extends State<AppBarBackButton>
color: currentColor,
shape: BoxShape.circle,
),
child: Icon(
Icons.arrow_back,
color: effectiveColor,
size: 20,
),
child: Icon(Icons.arrow_back, color: effectiveColor, size: 20),
);
},
),
+5 -2
View File
@@ -80,9 +80,12 @@ class _MediaCardState extends State<MediaCard> {
// Add watched status
if (item.isWatched) {
baseLabel = '$baseLabel, ${t.accessibility.mediaCardWatched}';
} else if (item.viewOffset != null && item.duration != null && item.viewOffset! > 0) {
} else if (item.viewOffset != null &&
item.duration != null &&
item.viewOffset! > 0) {
final percent = ((item.viewOffset! / item.duration!) * 100).round();
baseLabel = '$baseLabel, ${t.accessibility.mediaCardPartiallyWatched(percent: percent)}';
baseLabel =
'$baseLabel, ${t.accessibility.mediaCardPartiallyWatched(percent: percent)}';
} else {
baseLabel = '$baseLabel, ${t.accessibility.mediaCardUnwatched}';
}
@@ -41,11 +41,20 @@ class VideoControlButton extends StatelessWidget {
// Determine the effective color: explicit color > active amber > default white
final effectiveColor = color ?? (isActive ? Colors.amber : Colors.white);
return IconButton(
icon: Icon(icon, color: effectiveColor, semanticLabel: semanticLabel),
final button = IconButton(
icon: Icon(icon, color: effectiveColor),
onPressed: onPressed,
tooltip: tooltip,
constraints: const BoxConstraints(minWidth: 40, minHeight: 40),
);
return semanticLabel != null
? Semantics(
label: semanticLabel,
button: true,
excludeSemantics: true,
child: button,
)
: button;
}
}
+84 -34
View File
@@ -1105,18 +1105,24 @@ class _PlexVideoControlsState extends State<PlexVideoControls>
color: Colors.black.withValues(alpha: 0.5),
shape: BoxShape.circle,
),
child: IconButton(
child: Semantics(
label: t.videoControls.seekBackwardButton(
seconds: _seekTimeSmall,
),
button: true,
excludeSemantics: true,
child: IconButton(
icon: Icon(
_getReplayIcon(_seekTimeSmall),
color: Colors.white,
size: 48,
semanticLabel: t.videoControls.seekBackwardButton(seconds: _seekTimeSmall),
),
iconSize: 48,
onPressed: () {
_seekWithClamping(Duration(seconds: -_seekTimeSmall));
},
),
),
),
const SizedBox(width: 48),
Container(
@@ -1124,12 +1130,17 @@ class _PlexVideoControlsState extends State<PlexVideoControls>
color: Colors.black.withValues(alpha: 0.5),
shape: BoxShape.circle,
),
child: IconButton(
child: Semantics(
label: isPlaying
? t.videoControls.pauseButton
: t.videoControls.playButton,
button: true,
excludeSemantics: true,
child: IconButton(
icon: Icon(
isPlaying ? Icons.pause : Icons.play_arrow,
color: Colors.white,
size: 72,
semanticLabel: isPlaying ? t.videoControls.pauseButton : t.videoControls.playButton,
),
iconSize: 72,
onPressed: () {
@@ -1142,6 +1153,7 @@ class _PlexVideoControlsState extends State<PlexVideoControls>
}
},
),
),
),
const SizedBox(width: 48),
Container(
@@ -1149,18 +1161,24 @@ class _PlexVideoControlsState extends State<PlexVideoControls>
color: Colors.black.withValues(alpha: 0.5),
shape: BoxShape.circle,
),
child: IconButton(
child: Semantics(
label: t.videoControls.seekForwardButton(
seconds: _seekTimeSmall,
),
button: true,
excludeSemantics: true,
child: IconButton(
icon: Icon(
_getForwardIcon(_seekTimeSmall),
color: Colors.white,
size: 48,
semanticLabel: t.videoControls.seekForwardButton(seconds: _seekTimeSmall),
),
iconSize: 48,
onPressed: () {
_seekWithClamping(Duration(seconds: _seekTimeSmall));
},
),
),
),
],
);
@@ -1375,41 +1393,56 @@ class _PlexVideoControlsState extends State<PlexVideoControls>
Row(
children: [
// Previous item
IconButton(
Semantics(
label: t.videoControls.previousButton,
button: true,
excludeSemantics: true,
child: IconButton(
icon: Icon(
Icons.skip_previous,
color: widget.onPrevious != null
? Colors.white
: Colors.white54,
semanticLabel: t.videoControls.previousButton,
),
onPressed: widget.onPrevious,
),
),
// Previous chapter (or skip backward if no chapters)
IconButton(
Semantics(
label: _chapters.isEmpty
? t.videoControls.seekBackwardButton(
seconds: _seekTimeSmall,
)
: t.videoControls.previousChapterButton,
button: true,
excludeSemantics: true,
child: IconButton(
icon: Icon(
_chapters.isEmpty
? _getReplayIcon(_seekTimeSmall)
: Icons.fast_rewind,
color: Colors.white,
semanticLabel: _chapters.isEmpty
? t.videoControls.seekBackwardButton(seconds: _seekTimeSmall)
: t.videoControls.previousChapterButton,
),
onPressed: _seekToPreviousChapter,
),
),
// Play/Pause
StreamBuilder<bool>(
stream: widget.player.stream.playing,
initialData: widget.player.state.playing,
builder: (context, snapshot) {
final isPlaying = snapshot.data ?? false;
return IconButton(
return Semantics(
label: isPlaying
? t.videoControls.pauseButton
: t.videoControls.playButton,
button: true,
excludeSemantics: true,
child: IconButton(
icon: Icon(
isPlaying ? Icons.pause : Icons.play_arrow,
color: Colors.white,
size: 32,
semanticLabel: isPlaying ? t.videoControls.pauseButton : t.videoControls.playButton,
),
iconSize: 32,
onPressed: () {
@@ -1421,30 +1454,41 @@ class _PlexVideoControlsState extends State<PlexVideoControls>
_startHideTimer(); // Start auto-hide when playing
}
},
),
);
},
),
// Next chapter (or skip forward if no chapters)
IconButton(
Semantics(
label: _chapters.isEmpty
? t.videoControls.seekForwardButton(seconds: _seekTimeSmall)
: t.videoControls.nextChapterButton,
button: true,
excludeSemantics: true,
child: IconButton(
icon: Icon(
_chapters.isEmpty
? _getForwardIcon(_seekTimeSmall)
: Icons.fast_forward,
color: Colors.white,
semanticLabel: _chapters.isEmpty
? t.videoControls.seekForwardButton(seconds: _seekTimeSmall)
: t.videoControls.nextChapterButton,
),
onPressed: _seekToNextChapter,
),
),
// Next item
IconButton(
Semantics(
label: t.videoControls.nextButton,
button: true,
excludeSemantics: true,
child: IconButton(
icon: Icon(
Icons.skip_next,
semanticLabel: t.videoControls.nextButton,
color: widget.onNext != null ? Colors.white : Colors.white54,
color: widget.onNext != null
? Colors.white
: Colors.white54,
),
onPressed: widget.onNext,
),
),
const Spacer(),
// Volume control
@@ -1554,20 +1598,26 @@ class _PlexVideoControlsState extends State<PlexVideoControls>
return Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(
isMuted ? Icons.volume_off : Icons.volume_up,
color: Colors.white,
semanticLabel: isMuted ? t.videoControls.unmuteButton : t.videoControls.muteButton,
Semantics(
label: isMuted
? t.videoControls.unmuteButton
: t.videoControls.muteButton,
button: true,
excludeSemantics: true,
child: IconButton(
icon: Icon(
isMuted ? Icons.volume_off : Icons.volume_up,
color: Colors.white,
),
onPressed: () async {
final newVolume = isMuted ? 100.0 : 0.0;
widget.player.setVolume(newVolume);
final settings = await SettingsService.getInstance();
await settings.setVolume(newVolume);
},
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
),
onPressed: () async {
final newVolume = isMuted ? 100.0 : 0.0;
widget.player.setVolume(newVolume);
final settings = await SettingsService.getInstance();
await settings.setVolume(newVolume);
},
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
),
const SizedBox(width: 8),
SizedBox(