diff --git a/lib/widgets/video_controls/helpers/track_selection_helper.dart b/lib/widgets/video_controls/helpers/track_selection_helper.dart index c3391c7b..f555ad57 100644 --- a/lib/widgets/video_controls/helpers/track_selection_helper.dart +++ b/lib/widgets/video_controls/helpers/track_selection_helper.dart @@ -21,7 +21,7 @@ class TrackSelectionHelper { /// Build a centered empty state widget static Widget buildEmptyState() { return Center( - child: Text(getEmptyMessage(), style: const TextStyle(color: Colors.white70)), + child: Text(getEmptyMessage()), ); } @@ -63,7 +63,7 @@ class TrackSelectionHelper { }) { return FocusableListTile( focusNode: focusNode, - title: Text(label, style: TextStyle(color: isSelected ? Colors.blue : Colors.white)), + title: Text(label, style: TextStyle(color: isSelected ? Colors.blue : null)), trailing: isSelected ? const AppIcon(Symbols.check_rounded, fill: 1, color: Colors.blue) : null, onTap: onTap, ); diff --git a/lib/widgets/video_controls/sheets/base_video_control_sheet.dart b/lib/widgets/video_controls/sheets/base_video_control_sheet.dart index 386de375..3941d7ae 100644 --- a/lib/widgets/video_controls/sheets/base_video_control_sheet.dart +++ b/lib/widgets/video_controls/sheets/base_video_control_sheet.dart @@ -26,7 +26,7 @@ class BaseVideoControlSheet extends StatelessWidget { Widget content = Column( children: [ VideoSheetHeader(title: title, icon: icon, iconColor: iconColor, onBack: onBack), - const Divider(color: Colors.white24, height: 1), + Divider(color: Theme.of(context).dividerColor, height: 1), Expanded(child: child), ], ); diff --git a/lib/widgets/video_controls/sheets/chapter_sheet.dart b/lib/widgets/video_controls/sheets/chapter_sheet.dart index 42a81ed9..8b6edbf9 100644 --- a/lib/widgets/video_controls/sheets/chapter_sheet.dart +++ b/lib/widgets/video_controls/sheets/chapter_sheet.dart @@ -7,6 +7,7 @@ import '../../../mpv/mpv.dart'; import '../../../services/plex_client.dart'; import '../../../services/download_storage_service.dart'; import '../../../models/plex_media_info.dart'; +import '../../../theme/mono_tokens.dart'; import '../../../utils/formatters.dart'; import '../../../utils/provider_extensions.dart'; import '../../../widgets/focusable_list_tile.dart'; @@ -76,7 +77,7 @@ class _ChapterSheetState extends State { content = const Center(child: CircularProgressIndicator()); } else if (widget.chapters.isEmpty) { content = Center( - child: Text(t.videoControls.noChaptersAvailable, style: const TextStyle(color: Colors.white70)), + child: Text(t.videoControls.noChaptersAvailable, style: TextStyle(color: tokens(context).textMuted)), ); } else { content = ListView.builder( @@ -92,8 +93,11 @@ class _ChapterSheetState extends State { return FocusableListTile( leading: chapter.thumb != null - ? Stack( - children: [ + ? SizedBox( + width: 60, + height: 34, + child: Stack( + children: [ ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(4)), child: PlexOptimizedImage.thumb( @@ -116,20 +120,21 @@ class _ChapterSheetState extends State { ), ), ), - ], + ], + ), ) : null, title: Text( chapter.label, style: TextStyle( - color: isCurrentChapter ? Colors.blue : Colors.white, + color: isCurrentChapter ? Colors.blue : null, fontWeight: isCurrentChapter ? FontWeight.bold : FontWeight.normal, ), ), subtitle: Text( formatDurationTimestamp(chapter.startTime), style: TextStyle( - color: isCurrentChapter ? Colors.blue.withValues(alpha: 0.7) : Colors.white70, + color: isCurrentChapter ? Colors.blue.withValues(alpha: 0.7) : tokens(context).textMuted, fontSize: 12, ), ), diff --git a/lib/widgets/video_controls/sheets/queue_sheet.dart b/lib/widgets/video_controls/sheets/queue_sheet.dart index 6b981190..4a3a2228 100644 --- a/lib/widgets/video_controls/sheets/queue_sheet.dart +++ b/lib/widgets/video_controls/sheets/queue_sheet.dart @@ -6,6 +6,7 @@ import 'package:provider/provider.dart'; import '../../../i18n/strings.g.dart'; import '../../../models/plex_metadata.dart'; import '../../../providers/playback_state_provider.dart'; +import '../../../theme/mono_tokens.dart'; import '../../../utils/provider_extensions.dart'; import '../../../widgets/focusable_list_tile.dart'; import '../../../widgets/overlay_sheet.dart'; @@ -28,7 +29,7 @@ class QueueSheet extends StatelessWidget { Widget content; if (items.isEmpty) { content = Center( - child: Text(t.videoControls.noQueueItems, style: const TextStyle(color: Colors.white70)), + child: Text(t.videoControls.noQueueItems, style: TextStyle(color: tokens(context).textMuted)), ); } else { // Find current index for initial scroll @@ -46,7 +47,7 @@ class QueueSheet extends StatelessWidget { title: Text( item.title, style: TextStyle( - color: isCurrent ? Colors.blue : Colors.white, + color: isCurrent ? Colors.blue : null, fontWeight: isCurrent ? FontWeight.bold : FontWeight.normal, ), maxLines: 1, @@ -55,7 +56,7 @@ class QueueSheet extends StatelessWidget { subtitle: Text( _buildSubtitle(item), style: TextStyle( - color: isCurrent ? Colors.blue.withValues(alpha: 0.7) : Colors.white70, + color: isCurrent ? Colors.blue.withValues(alpha: 0.7) : tokens(context).textMuted, fontSize: 12, ), maxLines: 1, @@ -88,30 +89,34 @@ class QueueSheet extends StatelessWidget { // Try to get client for thumbnails, may fail in offline mode final client = _tryGetClient(context, item); - return Stack( - children: [ - ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(4)), - child: PlexOptimizedImage.thumb( - client: client, - imagePath: item.thumb, - width: 60, - height: 34, - fit: BoxFit.cover, - errorWidget: (context, url, error) => - const AppIcon(Symbols.image_rounded, fill: 1, color: Colors.white54, size: 34), - ), - ), - if (isCurrent) - Positioned.fill( - child: Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(4)), - border: const Border.fromBorderSide(BorderSide(color: Colors.blue, width: 2)), - ), + return SizedBox( + width: 60, + height: 34, + child: Stack( + children: [ + ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(4)), + child: PlexOptimizedImage.thumb( + client: client, + imagePath: item.thumb, + width: 60, + height: 34, + fit: BoxFit.cover, + errorWidget: (context, url, error) => + const AppIcon(Symbols.image_rounded, fill: 1, color: Colors.white54, size: 34), ), ), - ], + if (isCurrent) + Positioned.fill( + child: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(4)), + border: const Border.fromBorderSide(BorderSide(color: Colors.blue, width: 2)), + ), + ), + ), + ], + ), ); } diff --git a/lib/widgets/video_controls/sheets/version_sheet.dart b/lib/widgets/video_controls/sheets/version_sheet.dart index 99f7d502..b2d104c1 100644 --- a/lib/widgets/video_controls/sheets/version_sheet.dart +++ b/lib/widgets/video_controls/sheets/version_sheet.dart @@ -36,7 +36,7 @@ class _VersionSheetState extends State { final isSelected = index == widget.selectedMediaIndex; return FocusableListTile( - title: Text(version.displayLabel, style: TextStyle(color: isSelected ? Colors.blue : Colors.white)), + title: Text(version.displayLabel, style: TextStyle(color: isSelected ? Colors.blue : null)), trailing: isSelected ? const AppIcon(Symbols.check_rounded, fill: 1, color: Colors.blue) : null, onTap: () { OverlaySheetController.of(context).close(); diff --git a/lib/widgets/video_controls/sheets/video_settings_sheet.dart b/lib/widgets/video_controls/sheets/video_settings_sheet.dart index ab194c64..9b854ece 100644 --- a/lib/widgets/video_controls/sheets/video_settings_sheet.dart +++ b/lib/widgets/video_controls/sheets/video_settings_sheet.dart @@ -14,6 +14,7 @@ import '../../../services/shader_service.dart'; import '../../../services/sleep_timer_service.dart'; import '../../../utils/formatters.dart'; import '../../../utils/platform_detector.dart'; +import '../../../theme/mono_tokens.dart'; import '../../../widgets/focusable_list_tile.dart'; import '../../../widgets/overlay_sheet.dart'; import '../widgets/sync_offset_control.dart'; @@ -43,21 +44,22 @@ class _SettingsMenuItem extends StatelessWidget { @override Widget build(BuildContext context) { + final t = tokens(context); final valueWidget = Text( valueText, - style: TextStyle(color: isHighlighted ? Colors.amber : Colors.white70, fontSize: 14), + style: TextStyle(color: isHighlighted ? Colors.amber : t.textMuted, fontSize: 14), overflow: allowValueOverflow ? TextOverflow.ellipsis : null, ); return FocusableListTile( - leading: AppIcon(icon, fill: 1, color: isHighlighted ? Colors.amber : Colors.white70), - title: Text(title, style: const TextStyle(color: Colors.white)), + leading: AppIcon(icon, fill: 1, color: isHighlighted ? Colors.amber : t.textMuted), + title: Text(title), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ if (allowValueOverflow) Flexible(child: valueWidget) else valueWidget, const SizedBox(width: 8), - const AppIcon(Symbols.chevron_right_rounded, fill: 1, color: Colors.white70), + AppIcon(Symbols.chevron_right_rounded, fill: 1, color: t.textMuted), ], ), onTap: onTap, @@ -286,8 +288,8 @@ class _VideoSettingsSheetState extends State { // HDR Toggle (iOS, macOS, and Windows) if (Platform.isIOS || Platform.isMacOS || Platform.isWindows) ListTile( - leading: AppIcon(Symbols.hdr_strong_rounded, fill: 1, color: _enableHDR ? Colors.amber : Colors.white70), - title: Text(t.videoSettings.hdr, style: const TextStyle(color: Colors.white)), + leading: AppIcon(Symbols.hdr_strong_rounded, fill: 1, color: _enableHDR ? Colors.amber : tokens(context).textMuted), + title: Text(t.videoSettings.hdr), trailing: Switch(value: _enableHDR, onChanged: (_) => _toggleHDR(), activeThumbColor: Colors.amber), onTap: _toggleHDR, ), @@ -297,9 +299,9 @@ class _VideoSettingsSheetState extends State { leading: AppIcon( Symbols.skip_next_rounded, fill: 1, - color: _autoPlayNextEpisode ? Colors.amber : Colors.white70, + color: _autoPlayNextEpisode ? Colors.amber : tokens(context).textMuted, ), - title: Text(t.videoControls.autoPlayNext, style: const TextStyle(color: Colors.white)), + title: Text(t.videoControls.autoPlayNext), trailing: Switch( value: _autoPlayNextEpisode, onChanged: (_) => _toggleAutoPlayNextEpisode(), @@ -345,9 +347,9 @@ class _VideoSettingsSheetState extends State { leading: AppIcon( Symbols.blur_on, fill: 1, - color: widget.isAmbientLightingEnabled ? Colors.amber : Colors.white70, + color: widget.isAmbientLightingEnabled ? Colors.amber : tokens(context).textMuted, ), - title: Text(t.videoControls.ambientLighting, style: const TextStyle(color: Colors.white)), + title: Text(t.videoControls.ambientLighting), trailing: Switch( value: widget.isAmbientLightingEnabled, onChanged: (_) { @@ -367,9 +369,9 @@ class _VideoSettingsSheetState extends State { leading: AppIcon( Symbols.analytics_rounded, fill: 1, - color: _showPerformanceOverlay ? Colors.amber : Colors.white70, + color: _showPerformanceOverlay ? Colors.amber : tokens(context).textMuted, ), - title: Text(t.videoSettings.performanceOverlay, style: const TextStyle(color: Colors.white)), + title: Text(t.videoSettings.performanceOverlay), trailing: Switch( value: _showPerformanceOverlay, onChanged: (_) => _togglePerformanceOverlay(), @@ -397,7 +399,7 @@ class _VideoSettingsSheetState extends State { final label = speed == 1.0 ? 'Normal' : '${speed.toStringAsFixed(2)}x'; return ListTile( - title: Text(label, style: TextStyle(color: isSelected ? Colors.blue : Colors.white)), + title: Text(label, style: TextStyle(color: isSelected ? Colors.blue : null)), trailing: isSelected ? const AppIcon(Symbols.check_rounded, fill: 1, color: Colors.blue) : null, onTap: () async { widget.player.setRate(speed); @@ -521,7 +523,7 @@ class _VideoSettingsSheetState extends State { padding: const EdgeInsets.fromLTRB(16, 12, 16, 4), child: Text( _formatBackend(entry.key), - style: TextStyle(color: Colors.white.withValues(alpha: 0.5), fontSize: 12, fontWeight: FontWeight.w600), + style: TextStyle(color: tokens(context).textMuted, fontSize: 12, fontWeight: FontWeight.w600), ), ), for (final d in entry.value) _buildDeviceTile(d, currentDevice), @@ -546,7 +548,7 @@ class _VideoSettingsSheetState extends State { final label = device.description.isEmpty ? device.name : device.description; return ListTile( - title: Text(label, style: TextStyle(color: isSelected ? Colors.blue : Colors.white)), + title: Text(label, style: TextStyle(color: isSelected ? Colors.blue : null)), trailing: isSelected ? const AppIcon(Symbols.check_rounded, fill: 1, color: Colors.blue) : null, onTap: () { widget.player.setAudioDevice(device); @@ -570,9 +572,9 @@ class _VideoSettingsSheetState extends State { final isSelected = preset.id == currentPreset.id; return FocusableListTile( - title: Text(preset.name, style: TextStyle(color: isSelected ? Colors.amber : Colors.white)), + title: Text(preset.name, style: TextStyle(color: isSelected ? Colors.amber : null)), subtitle: _getShaderSubtitle(preset) != null - ? Text(_getShaderSubtitle(preset)!, style: const TextStyle(color: Colors.white54, fontSize: 12)) + ? Text(_getShaderSubtitle(preset)!, style: TextStyle(color: tokens(context).textMuted, fontSize: 12)) : null, trailing: isSelected ? const AppIcon(Symbols.check_rounded, fill: 1, color: Colors.amber) : null, onTap: () async { @@ -624,7 +626,7 @@ class _VideoSettingsSheetState extends State { iconColor: () { if (isIconActive) return Colors.amber; if (_currentView == _SettingsView.shader && isShaderActive) return Colors.amber; - return Colors.white; + return null; }(), onBack: _currentView != _SettingsView.menu ? _navigateBack : null, child: () { diff --git a/lib/widgets/video_controls/sheets/video_sheet_header.dart b/lib/widgets/video_controls/sheets/video_sheet_header.dart index 6f756c29..7d6b8d6e 100644 --- a/lib/widgets/video_controls/sheets/video_sheet_header.dart +++ b/lib/widgets/video_controls/sheets/video_sheet_header.dart @@ -21,10 +21,10 @@ class VideoSheetHeader extends StatelessWidget { return BottomSheetHeader( title: title, icon: icon, - iconColor: iconColor ?? Colors.white, + iconColor: iconColor, onBack: onBack, onClose: onClose, - titleStyle: const TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold), + titleStyle: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), showBorder: false, ); } diff --git a/lib/widgets/video_controls/widgets/sleep_timer_active_status.dart b/lib/widgets/video_controls/widgets/sleep_timer_active_status.dart index c1424445..3cc1c9dc 100644 --- a/lib/widgets/video_controls/widgets/sleep_timer_active_status.dart +++ b/lib/widgets/video_controls/widgets/sleep_timer_active_status.dart @@ -27,7 +27,7 @@ class SleepTimerActiveStatus extends StatelessWidget { const SizedBox(height: 8), Text( t.videoControls.playbackWillPauseIn(duration: formatDurationWithSeconds(remainingTime)), - style: const TextStyle(color: Colors.white70, fontSize: 14), + style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 14), ), const SizedBox(height: 16), Row( @@ -37,8 +37,8 @@ class SleepTimerActiveStatus extends StatelessWidget { icon: const AppIcon(Symbols.add_rounded, fill: 1), label: Text(t.videoControls.addTime(amount: "15", unit: " min")), style: OutlinedButton.styleFrom( - foregroundColor: Colors.white, - side: const BorderSide(color: Colors.white54), + foregroundColor: Theme.of(context).colorScheme.onSurface, + side: BorderSide(color: Theme.of(context).colorScheme.outline), ), onPressed: () { sleepTimer.extendTimer(const Duration(minutes: 15)); diff --git a/lib/widgets/video_controls/widgets/sleep_timer_content.dart b/lib/widgets/video_controls/widgets/sleep_timer_content.dart index db6591af..65141925 100644 --- a/lib/widgets/video_controls/widgets/sleep_timer_content.dart +++ b/lib/widgets/video_controls/widgets/sleep_timer_content.dart @@ -31,7 +31,7 @@ class SleepTimerContent extends StatelessWidget { children: [ if (sleepTimer.isActive && remainingTime != null) ...[ SleepTimerActiveStatus(sleepTimer: sleepTimer, remainingTime: remainingTime, onCancel: onCancel), - const Divider(color: Colors.white24, height: 1), + Divider(color: Theme.of(context).dividerColor, height: 1), ], Expanded( child: SleepTimerDurationList(player: player, sleepTimer: sleepTimer, defaultDuration: defaultDuration), diff --git a/lib/widgets/video_controls/widgets/sleep_timer_duration_list.dart b/lib/widgets/video_controls/widgets/sleep_timer_duration_list.dart index a53b7813..19b92b59 100644 --- a/lib/widgets/video_controls/widgets/sleep_timer_duration_list.dart +++ b/lib/widgets/video_controls/widgets/sleep_timer_duration_list.dart @@ -36,11 +36,8 @@ class SleepTimerDurationList extends StatelessWidget { ); return ListTile( - leading: const AppIcon(Symbols.timer_rounded, fill: 1, color: Colors.white70), - title: Text( - label, - style: const TextStyle(color: Colors.white, fontWeight: FontWeight.normal), - ), + leading: AppIcon(Symbols.timer_rounded, fill: 1, color: Theme.of(context).colorScheme.onSurfaceVariant), + title: Text(label), onTap: () { sleepTimer.startTimer(Duration(minutes: minutes), () { // Pause playback when timer completes diff --git a/lib/widgets/video_controls/widgets/sync_offset_control.dart b/lib/widgets/video_controls/widgets/sync_offset_control.dart index c2d4d103..f18ab5fa 100644 --- a/lib/widgets/video_controls/widgets/sync_offset_control.dart +++ b/lib/widgets/video_controls/widgets/sync_offset_control.dart @@ -6,6 +6,7 @@ import 'package:material_symbols_icons/symbols.dart'; import '../../../mpv/mpv.dart'; import '../../../i18n/strings.g.dart'; +import '../../../theme/mono_tokens.dart'; import '../../../utils/formatters.dart'; /// Reusable widget for adjusting sync offsets (audio or subtitle) @@ -146,8 +147,8 @@ class _SyncOffsetControlState extends State { child: Container( width: 48, height: 48, - decoration: BoxDecoration(color: Colors.grey[800], borderRadius: const BorderRadius.all(Radius.circular(8))), - child: Icon(icon, color: Colors.white, size: 28), + decoration: BoxDecoration(color: Theme.of(context).colorScheme.surfaceContainerHighest, borderRadius: const BorderRadius.all(Radius.circular(8))), + child: Icon(icon, color: tokens(context).text, size: 28), ), ); } @@ -165,10 +166,10 @@ class _SyncOffsetControlState extends State { // Current offset display Text( formatSyncOffset(_currentOffset), - style: const TextStyle(color: Colors.white, fontSize: 48, fontWeight: FontWeight.bold), + style: const TextStyle(fontSize: 48, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), - Text(_getDescriptionText(), style: const TextStyle(color: Colors.white70, fontSize: 16)), + Text(_getDescriptionText(), style: TextStyle(color: tokens(context).textMuted, fontSize: 16)), const SizedBox(height: 48), // Slider with +/- buttons Row( @@ -183,7 +184,7 @@ class _SyncOffsetControlState extends State { // Slider section Text( t.videoControls.minusTime(amount: "5", unit: "s"), - style: const TextStyle(color: Colors.white70), + style: TextStyle(color: tokens(context).textMuted), ), Expanded( child: Slider( @@ -192,7 +193,7 @@ class _SyncOffsetControlState extends State { max: _sliderMax, divisions: _sliderDivisions, activeColor: Colors.blue, - inactiveColor: Colors.white24, + inactiveColor: Theme.of(context).colorScheme.outlineVariant, onChanged: (value) { setState(() { _currentOffset = value; @@ -205,7 +206,7 @@ class _SyncOffsetControlState extends State { ), Text( t.videoControls.addTime(amount: "5", unit: "s"), - style: const TextStyle(color: Colors.white70), + style: TextStyle(color: tokens(context).textMuted), ), const SizedBox(width: 12), // Increment button @@ -223,10 +224,6 @@ class _SyncOffsetControlState extends State { icon: const AppIcon(Symbols.restart_alt_rounded, fill: 1), label: Text(t.videoControls.resetToZero), style: ElevatedButton.styleFrom( - backgroundColor: Colors.grey[800], - foregroundColor: Colors.white, - disabledBackgroundColor: Colors.grey[850], - disabledForegroundColor: Colors.white38, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), ), ),