Dark/bright: Auto (Android's dark theme), Dark or Bright, kept across restarts
Settings has a segmented Auto / Dark / Bright. Auto follows platformBrightness, so Android's schedule too. The Dash's quick button holds the other variant; a long press is auto again. The mode is kept with shared_preferences. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+60
-12
@@ -20,6 +20,7 @@ import 'dart:math' show max, min;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// The HUD face (smart-serow's DIN 1451). Opt-in: a widget names it in its own
|
||||
/// TextStyle; everything else keeps the basic font, which covers CJK where DIN does not.
|
||||
@@ -116,29 +117,70 @@ class Palette {
|
||||
);
|
||||
}
|
||||
|
||||
/// A theme: both variants, and which one is showing. Listen to it to follow a switch.
|
||||
class Themes extends ChangeNotifier {
|
||||
Themes(this.name, this.dark, this.bright);
|
||||
/// Which variant shows: Android's own dark theme (auto), or one held fixed.
|
||||
enum VariantMode { auto, dark, bright }
|
||||
|
||||
/// A theme: both variants, and which one is showing. Listen to it to follow a switch,
|
||||
/// and, in auto, Android's dark theme turning on or off (by hand or on its schedule).
|
||||
/// The mode is kept across restarts.
|
||||
class Themes extends ChangeNotifier with WidgetsBindingObserver {
|
||||
Themes(this.name, this.dark, this.bright, [this._prefs]) {
|
||||
_mode =
|
||||
VariantMode.values.asNameMap()[_prefs?.getString(_key)] ??
|
||||
VariantMode.auto;
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
}
|
||||
|
||||
static const _key = 'variant_mode';
|
||||
|
||||
final String name;
|
||||
final Palette dark;
|
||||
final Palette bright;
|
||||
bool _isDark = true;
|
||||
final SharedPreferences? _prefs;
|
||||
late VariantMode _mode;
|
||||
|
||||
bool get isDark => _isDark;
|
||||
Palette get current => _isDark ? dark : bright;
|
||||
VariantMode get mode => _mode;
|
||||
|
||||
set isDark(bool v) {
|
||||
if (v == _isDark) return;
|
||||
_isDark = v;
|
||||
set mode(VariantMode v) {
|
||||
if (v == _mode) return;
|
||||
_mode = v;
|
||||
_prefs?.setString(_key, v.name);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void toggle() => isDark = !_isDark;
|
||||
bool get isDark => switch (_mode) {
|
||||
VariantMode.auto =>
|
||||
WidgetsBinding.instance.platformDispatcher.platformBrightness ==
|
||||
Brightness.dark,
|
||||
VariantMode.dark => true,
|
||||
VariantMode.bright => false,
|
||||
};
|
||||
|
||||
Palette get current => isDark ? dark : bright;
|
||||
|
||||
/// The quick switch: hold the other variant fixed.
|
||||
void toggle() => mode = isDark ? VariantMode.bright : VariantMode.dark;
|
||||
|
||||
@override
|
||||
void didChangePlatformBrightness() {
|
||||
if (_mode == VariantMode.auto) notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/// [themeName], falling back to `default`, then to the hard-coded palette
|
||||
/// (smart-serow's chain).
|
||||
static Future<Themes> load([String name = themeName]) async {
|
||||
SharedPreferences? prefs;
|
||||
try {
|
||||
prefs = await SharedPreferences.getInstance();
|
||||
} catch (_) {
|
||||
// No storage: the mode starts at auto each time.
|
||||
}
|
||||
for (final n in {name, 'default'}) {
|
||||
try {
|
||||
final j = jsonDecode(
|
||||
@@ -148,19 +190,25 @@ class Themes extends ChangeNotifier {
|
||||
n,
|
||||
Palette._fromJson(j['dark'] as Map<String, dynamic>),
|
||||
Palette._fromJson(j['bright'] as Map<String, dynamic>),
|
||||
prefs,
|
||||
);
|
||||
} catch (_) {
|
||||
// Missing or malformed: try the next one.
|
||||
}
|
||||
}
|
||||
return Themes('fallback', Palette.fallbackDark, Palette.fallbackBright);
|
||||
return Themes(
|
||||
'fallback',
|
||||
Palette.fallbackDark,
|
||||
Palette.fallbackBright,
|
||||
prefs,
|
||||
);
|
||||
}
|
||||
|
||||
/// Material's theme for the rest of the app, from the same roles: buttons and
|
||||
/// selection in accent, errors in alarm, raised surfaces in surface.
|
||||
ThemeData material() {
|
||||
final p = current;
|
||||
final brightness = _isDark ? Brightness.dark : Brightness.light;
|
||||
final brightness = isDark ? Brightness.dark : Brightness.light;
|
||||
return ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: brightness,
|
||||
|
||||
Reference in New Issue
Block a user