feat: oled theme

This commit is contained in:
edde746
2026-01-28 23:06:50 +01:00
parent 18a0c8833d
commit ef325cde29
21 changed files with 123 additions and 26 deletions
+2
View File
@@ -78,6 +78,8 @@
"systemThemeDescription": "Systemeinstellungen folgen",
"lightTheme": "Hell",
"darkTheme": "Dunkel",
"oledTheme": "OLED",
"oledThemeDescription": "Reines Schwarz für OLED-Bildschirme",
"libraryDensity": "Mediathekdichte",
"compact": "Kompakt",
"compactDescription": "Kleinere Karten, mehr Elemente sichtbar",
+2
View File
@@ -77,6 +77,8 @@
"systemThemeDescription": "Follow system settings",
"lightTheme": "Light",
"darkTheme": "Dark",
"oledTheme": "OLED",
"oledThemeDescription": "Pure black for OLED screens",
"libraryDensity": "Library Density",
"compact": "Compact",
"compactDescription": "Smaller cards, more items visible",
+2
View File
@@ -77,6 +77,8 @@
"systemThemeDescription": "Suivre les paramètres système",
"lightTheme": "Light",
"darkTheme": "Dark",
"oledTheme": "OLED",
"oledThemeDescription": "Noir pur pour les écrans OLED",
"libraryDensity": "Densité des bibliothèques",
"compact": "Compact",
"compactDescription": "Cartes plus petites, plus d'éléments visibles",
+2
View File
@@ -78,6 +78,8 @@
"systemThemeDescription": "Segui le impostazioni di sistema",
"lightTheme": "Chiaro",
"darkTheme": "Scuro",
"oledTheme": "OLED",
"oledThemeDescription": "Nero puro per schermi OLED",
"libraryDensity": "Densità libreria",
"compact": "Compatta",
"compactDescription": "Schede più piccole, più elementi visibili",
+2
View File
@@ -78,6 +78,8 @@
"systemThemeDescription": "시스템 설정에 따름",
"lightTheme": "라이트 모드",
"darkTheme": "다크 모드",
"oledTheme": "OLED",
"oledThemeDescription": "OLED 화면을 위한 순수 검정",
"libraryDensity": "라이브러리 표시 밀도",
"compact": "좁게",
"compactDescription": "카드를 작게 표시하여 더 많은 항목을 보여줍니다.",
+2
View File
@@ -78,6 +78,8 @@
"systemThemeDescription": "Volg systeeminstellingen",
"lightTheme": "Licht",
"darkTheme": "Donker",
"oledTheme": "OLED",
"oledThemeDescription": "Puur zwart voor OLED-schermen",
"libraryDensity": "Bibliotheek dichtheid",
"compact": "Compact",
"compactDescription": "Kleinere kaarten, meer items zichtbaar",
+2 -2
View File
@@ -4,9 +4,9 @@
/// To regenerate, run: `dart run slang`
///
/// Locales: 8
/// Strings: 4444 (555 per locale)
/// Strings: 4460 (557 per locale)
///
/// Built on 2026-01-26 at 02:10 UTC
/// Built on 2026-01-28 at 22:06 UTC
// coverage:ignore-file
// ignore_for_file: type=lint, unused_import
+6 -2
View File
@@ -191,6 +191,8 @@ class _TranslationsSettingsDe implements TranslationsSettingsEn {
@override String get systemThemeDescription => 'Systemeinstellungen folgen';
@override String get lightTheme => 'Hell';
@override String get darkTheme => 'Dunkel';
@override String get oledTheme => 'OLED';
@override String get oledThemeDescription => 'Reines Schwarz für OLED-Bildschirme';
@override String get libraryDensity => 'Mediathekdichte';
@override String get compact => 'Kompakt';
@override String get compactDescription => 'Kleinere Karten, mehr Elemente sichtbar';
@@ -1031,6 +1033,8 @@ extension on TranslationsDe {
'settings.systemThemeDescription' => 'Systemeinstellungen folgen',
'settings.lightTheme' => 'Hell',
'settings.darkTheme' => 'Dunkel',
'settings.oledTheme' => 'OLED',
'settings.oledThemeDescription' => 'Reines Schwarz für OLED-Bildschirme',
'settings.libraryDensity' => 'Mediathekdichte',
'settings.compact' => 'Kompakt',
'settings.compactDescription' => 'Kleinere Karten, mehr Elemente sichtbar',
@@ -1473,10 +1477,10 @@ extension on TranslationsDe {
'collections.created' => 'Sammlung erstellt',
'collections.removeFromCollection' => 'Aus Sammlung entfernen',
'collections.removeFromCollectionConfirm' => ({required Object title}) => '"${title}" aus dieser Sammlung entfernen?',
'collections.removedFromCollection' => 'Aus Sammlung entfernt',
'collections.removeFromCollectionFailed' => 'Entfernen aus Sammlung fehlgeschlagen',
_ => null,
} ?? switch (path) {
'collections.removedFromCollection' => 'Aus Sammlung entfernt',
'collections.removeFromCollectionFailed' => 'Entfernen aus Sammlung fehlgeschlagen',
'collections.removeFromCollectionError' => ({required Object error}) => 'Fehler beim Entfernen aus der Sammlung: ${error}',
'watchTogether.title' => 'Gemeinsam Schauen',
'watchTogether.description' => 'Inhalte synchron mit Freunden und Familie schauen',
+10 -2
View File
@@ -328,6 +328,12 @@ class TranslationsSettingsEn {
/// en: 'Dark'
String get darkTheme => 'Dark';
/// en: 'OLED'
String get oledTheme => 'OLED';
/// en: 'Pure black for OLED screens'
String get oledThemeDescription => 'Pure black for OLED screens';
/// en: 'Library Density'
String get libraryDensity => 'Library Density';
@@ -2142,6 +2148,8 @@ extension on Translations {
'settings.systemThemeDescription' => 'Follow system settings',
'settings.lightTheme' => 'Light',
'settings.darkTheme' => 'Dark',
'settings.oledTheme' => 'OLED',
'settings.oledThemeDescription' => 'Pure black for OLED screens',
'settings.libraryDensity' => 'Library Density',
'settings.compact' => 'Compact',
'settings.compactDescription' => 'Smaller cards, more items visible',
@@ -2585,10 +2593,10 @@ extension on Translations {
'watchTogether.hostingSession' => 'Hosting Session',
'watchTogether.inSession' => 'In Session',
'watchTogether.sessionCode' => 'Session Code',
'watchTogether.hostControlsPlayback' => 'Host controls playback',
'watchTogether.anyoneCanControl' => 'Anyone can control playback',
_ => null,
} ?? switch (path) {
'watchTogether.hostControlsPlayback' => 'Host controls playback',
'watchTogether.anyoneCanControl' => 'Anyone can control playback',
'watchTogether.hostControls' => 'Host controls',
'watchTogether.anyoneControls' => 'Anyone controls',
'watchTogether.participants' => 'Participants',
+6 -2
View File
@@ -190,6 +190,8 @@ class _TranslationsSettingsFr implements TranslationsSettingsEn {
@override String get systemThemeDescription => 'Suivre les paramètres système';
@override String get lightTheme => 'Light';
@override String get darkTheme => 'Dark';
@override String get oledTheme => 'OLED';
@override String get oledThemeDescription => 'Noir pur pour les écrans OLED';
@override String get libraryDensity => 'Densité des bibliothèques';
@override String get compact => 'Compact';
@override String get compactDescription => 'Cartes plus petites, plus d\'éléments visibles';
@@ -1028,6 +1030,8 @@ extension on TranslationsFr {
'settings.systemThemeDescription' => 'Suivre les paramètres système',
'settings.lightTheme' => 'Light',
'settings.darkTheme' => 'Dark',
'settings.oledTheme' => 'OLED',
'settings.oledThemeDescription' => 'Noir pur pour les écrans OLED',
'settings.libraryDensity' => 'Densité des bibliothèques',
'settings.compact' => 'Compact',
'settings.compactDescription' => 'Cartes plus petites, plus d\'éléments visibles',
@@ -1471,10 +1475,10 @@ extension on TranslationsFr {
'watchTogether.hostingSession' => 'Session d\'hébergement',
'watchTogether.inSession' => 'En session',
'watchTogether.sessionCode' => 'Code de session',
'watchTogether.hostControlsPlayback' => 'L\'hôte contrôle la lecture',
'watchTogether.anyoneCanControl' => 'Tout le monde peut contrôler la lecture',
_ => null,
} ?? switch (path) {
'watchTogether.hostControlsPlayback' => 'L\'hôte contrôle la lecture',
'watchTogether.anyoneCanControl' => 'Tout le monde peut contrôler la lecture',
'watchTogether.hostControls' => 'Commandes de l\'hôte',
'watchTogether.anyoneControls' => 'Tout le monde contrôle',
'watchTogether.participants' => 'Participants',
+6 -2
View File
@@ -191,6 +191,8 @@ class _TranslationsSettingsIt implements TranslationsSettingsEn {
@override String get systemThemeDescription => 'Segui le impostazioni di sistema';
@override String get lightTheme => 'Chiaro';
@override String get darkTheme => 'Scuro';
@override String get oledTheme => 'OLED';
@override String get oledThemeDescription => 'Nero puro per schermi OLED';
@override String get libraryDensity => 'Densità libreria';
@override String get compact => 'Compatta';
@override String get compactDescription => 'Schede più piccole, più elementi visibili';
@@ -1031,6 +1033,8 @@ extension on TranslationsIt {
'settings.systemThemeDescription' => 'Segui le impostazioni di sistema',
'settings.lightTheme' => 'Chiaro',
'settings.darkTheme' => 'Scuro',
'settings.oledTheme' => 'OLED',
'settings.oledThemeDescription' => 'Nero puro per schermi OLED',
'settings.libraryDensity' => 'Densità libreria',
'settings.compact' => 'Compatta',
'settings.compactDescription' => 'Schede più piccole, più elementi visibili',
@@ -1473,10 +1477,10 @@ extension on TranslationsIt {
'collections.created' => 'Raccolta creata',
'collections.removeFromCollection' => 'Rimuovi dalla raccolta',
'collections.removeFromCollectionConfirm' => ({required Object title}) => 'Rimuovere "${title}" da questa raccolta?',
'collections.removedFromCollection' => 'Rimosso dalla raccolta',
'collections.removeFromCollectionFailed' => 'Impossibile rimuovere dalla raccolta',
_ => null,
} ?? switch (path) {
'collections.removedFromCollection' => 'Rimosso dalla raccolta',
'collections.removeFromCollectionFailed' => 'Impossibile rimuovere dalla raccolta',
'collections.removeFromCollectionError' => ({required Object error}) => 'Errore durante la rimozione dalla raccolta: ${error}',
'watchTogether.title' => 'Guarda Insieme',
'watchTogether.description' => 'Guarda contenuti in sincronia con amici e familiari',
+6 -2
View File
@@ -191,6 +191,8 @@ class _TranslationsSettingsKo implements TranslationsSettingsEn {
@override String get systemThemeDescription => '시스템 설정에 따름';
@override String get lightTheme => '라이트 모드';
@override String get darkTheme => '다크 모드';
@override String get oledTheme => 'OLED';
@override String get oledThemeDescription => 'OLED 화면을 위한 순수 검정';
@override String get libraryDensity => '라이브러리 표시 밀도';
@override String get compact => '좁게';
@override String get compactDescription => '카드를 작게 표시하여 더 많은 항목을 보여줍니다.';
@@ -1031,6 +1033,8 @@ extension on TranslationsKo {
'settings.systemThemeDescription' => '시스템 설정에 따름',
'settings.lightTheme' => '라이트 모드',
'settings.darkTheme' => '다크 모드',
'settings.oledTheme' => 'OLED',
'settings.oledThemeDescription' => 'OLED 화면을 위한 순수 검정',
'settings.libraryDensity' => '라이브러리 표시 밀도',
'settings.compact' => '좁게',
'settings.compactDescription' => '카드를 작게 표시하여 더 많은 항목을 보여줍니다.',
@@ -1473,10 +1477,10 @@ extension on TranslationsKo {
'watchTogether.hostOnly' => '호스트만',
'watchTogether.anyone' => '누구나',
'watchTogether.hostingSession' => '세션 호스팅',
'watchTogether.inSession' => '세션 중',
'watchTogether.sessionCode' => '세션 코드',
_ => null,
} ?? switch (path) {
'watchTogether.inSession' => '세션 중',
'watchTogether.sessionCode' => '세션 코드',
'watchTogether.hostControlsPlayback' => '호스트 재생 제어',
'watchTogether.anyoneCanControl' => '누구나 재생 제어 가능',
'watchTogether.hostControls' => '호스트 제어',
+6 -2
View File
@@ -191,6 +191,8 @@ class _TranslationsSettingsNl implements TranslationsSettingsEn {
@override String get systemThemeDescription => 'Volg systeeminstellingen';
@override String get lightTheme => 'Licht';
@override String get darkTheme => 'Donker';
@override String get oledTheme => 'OLED';
@override String get oledThemeDescription => 'Puur zwart voor OLED-schermen';
@override String get libraryDensity => 'Bibliotheek dichtheid';
@override String get compact => 'Compact';
@override String get compactDescription => 'Kleinere kaarten, meer items zichtbaar';
@@ -1031,6 +1033,8 @@ extension on TranslationsNl {
'settings.systemThemeDescription' => 'Volg systeeminstellingen',
'settings.lightTheme' => 'Licht',
'settings.darkTheme' => 'Donker',
'settings.oledTheme' => 'OLED',
'settings.oledThemeDescription' => 'Puur zwart voor OLED-schermen',
'settings.libraryDensity' => 'Bibliotheek dichtheid',
'settings.compact' => 'Compact',
'settings.compactDescription' => 'Kleinere kaarten, meer items zichtbaar',
@@ -1473,10 +1477,10 @@ extension on TranslationsNl {
'collections.created' => 'Collectie gemaakt',
'collections.removeFromCollection' => 'Verwijderen uit collectie',
'collections.removeFromCollectionConfirm' => ({required Object title}) => '"${title}" uit deze collectie verwijderen?',
'collections.removedFromCollection' => 'Uit collectie verwijderd',
'collections.removeFromCollectionFailed' => 'Verwijderen uit collectie mislukt',
_ => null,
} ?? switch (path) {
'collections.removedFromCollection' => 'Uit collectie verwijderd',
'collections.removeFromCollectionFailed' => 'Verwijderen uit collectie mislukt',
'collections.removeFromCollectionError' => ({required Object error}) => 'Fout bij verwijderen uit collectie: ${error}',
'watchTogether.title' => 'Samen Kijken',
'watchTogether.description' => 'Kijk synchroon met vrienden en familie',
+6 -2
View File
@@ -191,6 +191,8 @@ class _TranslationsSettingsSv implements TranslationsSettingsEn {
@override String get systemThemeDescription => 'Följ systeminställningar';
@override String get lightTheme => 'Ljust';
@override String get darkTheme => 'Mörkt';
@override String get oledTheme => 'OLED';
@override String get oledThemeDescription => 'Rent svart för OLED-skärmar';
@override String get libraryDensity => 'Biblioteksdensitet';
@override String get compact => 'Kompakt';
@override String get compactDescription => 'Mindre kort, fler objekt synliga';
@@ -1031,6 +1033,8 @@ extension on TranslationsSv {
'settings.systemThemeDescription' => 'Följ systeminställningar',
'settings.lightTheme' => 'Ljust',
'settings.darkTheme' => 'Mörkt',
'settings.oledTheme' => 'OLED',
'settings.oledThemeDescription' => 'Rent svart för OLED-skärmar',
'settings.libraryDensity' => 'Biblioteksdensitet',
'settings.compact' => 'Kompakt',
'settings.compactDescription' => 'Mindre kort, fler objekt synliga',
@@ -1473,10 +1477,10 @@ extension on TranslationsSv {
'collections.created' => 'Samling skapad',
'collections.removeFromCollection' => 'Ta bort från samling',
'collections.removeFromCollectionConfirm' => ({required Object title}) => 'Ta bort "${title}" från denna samling?',
'collections.removedFromCollection' => 'Borttagen från samling',
'collections.removeFromCollectionFailed' => 'Misslyckades med att ta bort från samling',
_ => null,
} ?? switch (path) {
'collections.removedFromCollection' => 'Borttagen från samling',
'collections.removeFromCollectionFailed' => 'Misslyckades med att ta bort från samling',
'collections.removeFromCollectionError' => ({required Object error}) => 'Fel vid borttagning från samling: ${error}',
'watchTogether.title' => 'Titta Tillsammans',
'watchTogether.description' => 'Titta på innehåll synkroniserat med vänner och familj',
+6 -2
View File
@@ -191,6 +191,8 @@ class _TranslationsSettingsZh implements TranslationsSettingsEn {
@override String get systemThemeDescription => '跟随系统设置';
@override String get lightTheme => '浅色';
@override String get darkTheme => '深色';
@override String get oledTheme => 'OLED';
@override String get oledThemeDescription => '纯黑色适用于 OLED 屏幕';
@override String get libraryDensity => '媒体库密度';
@override String get compact => '紧凑';
@override String get compactDescription => '卡片更小,显示更多项目';
@@ -1031,6 +1033,8 @@ extension on TranslationsZh {
'settings.systemThemeDescription' => '跟随系统设置',
'settings.lightTheme' => '浅色',
'settings.darkTheme' => '深色',
'settings.oledTheme' => 'OLED',
'settings.oledThemeDescription' => '纯黑色适用于 OLED 屏幕',
'settings.libraryDensity' => '媒体库密度',
'settings.compact' => '紧凑',
'settings.compactDescription' => '卡片更小,显示更多项目',
@@ -1473,10 +1477,10 @@ extension on TranslationsZh {
'collections.created' => '已创建合集',
'collections.removeFromCollection' => '从合集移除',
'collections.removeFromCollectionConfirm' => ({required Object title}) => '将“${title}”从此合集移除?',
'collections.removedFromCollection' => '已从合集移除',
'collections.removeFromCollectionFailed' => '从合集移除失败',
_ => null,
} ?? switch (path) {
'collections.removedFromCollection' => '已从合集移除',
'collections.removeFromCollectionFailed' => '从合集移除失败',
'collections.removeFromCollectionError' => ({required Object error}) => '从合集移除时出错:${error}',
'watchTogether.title' => '一起看',
'watchTogether.description' => '与朋友和家人同步观看内容',
+2
View File
@@ -78,6 +78,8 @@
"systemThemeDescription": "Följ systeminställningar",
"lightTheme": "Ljust",
"darkTheme": "Mörkt",
"oledTheme": "OLED",
"oledThemeDescription": "Rent svart för OLED-skärmar",
"libraryDensity": "Biblioteksdensitet",
"compact": "Kompakt",
"compactDescription": "Mindre kort, fler objekt synliga",
+2
View File
@@ -78,6 +78,8 @@
"systemThemeDescription": "跟随系统设置",
"lightTheme": "浅色",
"darkTheme": "深色",
"oledTheme": "OLED",
"oledThemeDescription": "纯黑色适用于 OLED 屏幕",
"libraryDensity": "媒体库密度",
"compact": "紧凑",
"compactDescription": "卡片更小,显示更多项目",
+17 -1
View File
@@ -30,7 +30,12 @@ class ThemeProvider extends ChangeNotifier {
settings.ThemeMode get themeMode => _themeMode;
ThemeData get lightTheme => monoTheme(dark: false);
ThemeData get darkTheme => monoTheme(dark: true);
ThemeData get darkTheme {
if (_themeMode == settings.ThemeMode.oled) {
return monoTheme(dark: true, oled: true);
}
return monoTheme(dark: true);
}
ThemeMode get materialThemeMode {
switch (_themeMode) {
@@ -38,6 +43,8 @@ class ThemeProvider extends ChangeNotifier {
return ThemeMode.light;
case settings.ThemeMode.dark:
return ThemeMode.dark;
case settings.ThemeMode.oled:
return ThemeMode.dark;
case settings.ThemeMode.system:
return ThemeMode.system;
}
@@ -49,6 +56,8 @@ class ThemeProvider extends ChangeNotifier {
return false;
case settings.ThemeMode.dark:
return true;
case settings.ThemeMode.oled:
return true;
case settings.ThemeMode.system:
return _systemBrightness == Brightness.dark;
}
@@ -68,6 +77,8 @@ class ThemeProvider extends ChangeNotifier {
return 'Light';
case settings.ThemeMode.dark:
return 'Dark';
case settings.ThemeMode.oled:
return 'OLED';
case settings.ThemeMode.system:
return 'System';
}
@@ -79,6 +90,8 @@ class ThemeProvider extends ChangeNotifier {
return Symbols.light_mode_rounded;
case settings.ThemeMode.dark:
return Symbols.dark_mode_rounded;
case settings.ThemeMode.oled:
return Symbols.contrast_rounded;
case settings.ThemeMode.system:
return Symbols.brightness_auto_rounded;
}
@@ -93,6 +106,9 @@ class ThemeProvider extends ChangeNotifier {
setThemeMode(settings.ThemeMode.dark);
break;
case settings.ThemeMode.dark:
setThemeMode(settings.ThemeMode.oled);
break;
case settings.ThemeMode.oled:
setThemeMode(settings.ThemeMode.system);
break;
}
+14
View File
@@ -902,6 +902,20 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab {
Navigator.pop(context);
},
),
ListTile(
leading: AppIcon(
themeProvider.themeMode == settings.ThemeMode.oled
? Symbols.radio_button_checked_rounded
: Symbols.radio_button_unchecked_rounded,
fill: 1,
),
title: Text(t.settings.oledTheme),
subtitle: Text(t.settings.oledThemeDescription),
onTap: () {
themeProvider.setThemeMode(settings.ThemeMode.oled);
Navigator.pop(context);
},
),
],
),
actions: [TextButton(onPressed: () => Navigator.pop(context), child: Text(t.common.cancel))],
+6 -1
View File
@@ -7,7 +7,7 @@ import '../models/mpv_config_models.dart';
import 'base_shared_preferences_service.dart';
import '../utils/platform_detector.dart';
enum ThemeMode { system, light, dark }
enum ThemeMode { system, light, dark, oled }
enum LibraryDensity { compact, normal, comfortable }
@@ -86,6 +86,11 @@ class SettingsService extends BaseSharedPreferencesService {
}
ThemeMode getThemeMode() {
final stored = prefs.getString(_keyThemeMode);
if (stored == null) {
// Default to OLED on Android TV, system elsewhere
return TvDetectionService.isTVSync() ? ThemeMode.oled : ThemeMode.system;
}
return _getEnumValue(_keyThemeMode, ThemeMode.values, ThemeMode.system);
}
+16 -6
View File
@@ -1,9 +1,17 @@
import 'package:flutter/material.dart';
import 'mono_tokens.dart';
ThemeData monoTheme({required bool dark}) {
ThemeData monoTheme({required bool dark, bool oled = false}) {
// neutral greys tuned for crisp contrast
final c = dark
final c = oled
? (
bg: const Color(0xFF000000), // Pure black for OLED
surface: const Color(0xFF0A0A0A), // Very dark gray
outline: const Color(0x1FFFFFFF),
text: const Color(0xFFEDEDED),
textMuted: const Color(0x99EDEDED),
)
: dark
? (
bg: const Color(0xFF0E0F12),
surface: const Color(0xFF15171C),
@@ -23,17 +31,19 @@ ThemeData monoTheme({required bool dark}) {
padding: const WidgetStatePropertyAll(EdgeInsets.symmetric(horizontal: 18, vertical: 14)),
elevation: const WidgetStatePropertyAll(0),
backgroundColor: WidgetStatePropertyAll(c.text),
foregroundColor: WidgetStatePropertyAll(dark ? c.bg : Colors.white),
foregroundColor: WidgetStatePropertyAll(isDark ? c.bg : Colors.white),
shape: const WidgetStatePropertyAll(StadiumBorder()),
);
final isDark = dark || oled;
final base = ThemeData(
useMaterial3: true,
brightness: dark ? Brightness.dark : Brightness.light,
brightness: isDark ? Brightness.dark : Brightness.light,
colorScheme: ColorScheme(
brightness: dark ? Brightness.dark : Brightness.light,
brightness: isDark ? Brightness.dark : Brightness.light,
primary: c.text,
onPrimary: dark ? const Color(0xFF0E0F12) : Colors.white,
onPrimary: isDark ? c.bg : Colors.white,
secondary: c.text,
onSecondary: c.bg,
surface: c.surface,