212 lines
5.6 KiB
Dart
212 lines
5.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../l10n/gen/app_localizations.dart';
|
|
import 'preferences.dart';
|
|
import 'responsive.dart';
|
|
|
|
typedef ThemeBuilderBuilder =
|
|
Widget Function(
|
|
ThemeMode themeMode,
|
|
ThemeData themeLight,
|
|
ThemeData themeDark,
|
|
);
|
|
|
|
class ThemeBuilderData {
|
|
ThemeBuilderData? _current;
|
|
ThemeBuilderData? get current => _current;
|
|
|
|
final ColorScheme? dynamicLight;
|
|
final ColorScheme? dynamicDark;
|
|
|
|
ThemeBuilderData({required this.dynamicLight, required this.dynamicDark}) {
|
|
_current = this;
|
|
}
|
|
|
|
ThemeData themeModifier(BuildContext context, ThemeData theme) {
|
|
var isMobile = Display.from(context).isMobile;
|
|
return theme.copyWith(
|
|
pageTransitionsTheme: const PageTransitionsTheme(
|
|
builders: <TargetPlatform, PageTransitionsBuilder>{
|
|
TargetPlatform.android: PredictiveBackPageTransitionsBuilder(),
|
|
},
|
|
),
|
|
sliderTheme: theme.sliderTheme.copyWith(year2023: false),
|
|
progressIndicatorTheme: theme.progressIndicatorTheme.copyWith(
|
|
year2023: false,
|
|
),
|
|
snackBarTheme: theme.snackBarTheme.copyWith(
|
|
behavior: isMobile ? null : SnackBarBehavior.floating,
|
|
width: isMobile ? null : 288,
|
|
),
|
|
listTileTheme: theme.listTileTheme.copyWith(
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
|
),
|
|
);
|
|
}
|
|
|
|
ThemeData themeLight() {
|
|
if (Preferences.instance.themeSystem && dynamicLight != null) {
|
|
return ThemeData.from(colorScheme: dynamicLight!);
|
|
} else {
|
|
return ThemeData.from(
|
|
colorScheme: ColorScheme.fromSeed(
|
|
seedColor: Colors.black,
|
|
dynamicSchemeVariant: DynamicSchemeVariant.content,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
ThemeData themeDark() {
|
|
if (Preferences.instance.themeSystem && dynamicDark != null) {
|
|
return ThemeData.from(colorScheme: dynamicDark!);
|
|
} else {
|
|
return ThemeData.from(
|
|
colorScheme: ColorScheme.fromSeed(
|
|
seedColor: Colors.white,
|
|
dynamicSchemeVariant: DynamicSchemeVariant.content,
|
|
brightness: Brightness.dark,
|
|
).copyWith(surface: Colors.black),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
class ThemeBuilder extends StatefulWidget {
|
|
final ThemeBuilderData data;
|
|
final ThemeBuilderBuilder builder;
|
|
|
|
const ThemeBuilder({super.key, required this.data, required this.builder});
|
|
|
|
@override
|
|
State<ThemeBuilder> createState() => _ThemeBuilderState();
|
|
}
|
|
|
|
class _ThemeBuilderState extends State<ThemeBuilder> {
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
Preferences.instance.addListener(onChange);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
Preferences.instance.removeListener(onChange);
|
|
super.dispose();
|
|
}
|
|
|
|
void onChange() {
|
|
if (mounted) setState(() {});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return widget.builder.call(
|
|
Preferences.instance.themeMode,
|
|
widget.data.themeModifier(context, widget.data.themeLight()),
|
|
widget.data.themeModifier(context, widget.data.themeDark()),
|
|
);
|
|
}
|
|
}
|
|
|
|
class ThemeModeSwitch extends StatefulWidget {
|
|
const ThemeModeSwitch({super.key});
|
|
|
|
@override
|
|
State<ThemeModeSwitch> createState() => _ThemeModeSwitchState();
|
|
}
|
|
|
|
class _ThemeModeSwitchState extends State<ThemeModeSwitch> {
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
Preferences.instance.addListener(onChange);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
Preferences.instance.removeListener(onChange);
|
|
super.dispose();
|
|
}
|
|
|
|
void onChange() {
|
|
if (mounted) setState(() {});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return SegmentedButton(
|
|
segments: [
|
|
ButtonSegment(
|
|
value: "dark",
|
|
icon: const Icon(Icons.dark_mode),
|
|
label: Text(AppLocalizations.of(context).settingsBrightnessDark),
|
|
),
|
|
ButtonSegment(
|
|
value: "system",
|
|
icon: const Icon(Icons.brightness_auto),
|
|
label: Text(AppLocalizations.of(context).settingsBrightnessSystem),
|
|
),
|
|
ButtonSegment(
|
|
value: "light",
|
|
icon: const Icon(Icons.light_mode),
|
|
label: Text(AppLocalizations.of(context).settingsBrightnessLight),
|
|
),
|
|
],
|
|
selected: switch (Preferences.instance.themeMode) {
|
|
ThemeMode.light => {"light"},
|
|
ThemeMode.dark => {"dark"},
|
|
_ => {"system"},
|
|
},
|
|
onSelectionChanged: (selection) => Preferences.instance.themeMode =
|
|
ThemeMode.values.byName(selection.first),
|
|
);
|
|
}
|
|
}
|
|
|
|
class ThemeSwitch extends StatefulWidget {
|
|
const ThemeSwitch({super.key});
|
|
|
|
@override
|
|
State<ThemeSwitch> createState() => _ThemeSwitchState();
|
|
}
|
|
|
|
class _ThemeSwitchState extends State<ThemeSwitch> {
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
Preferences.instance.addListener(onChange);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
Preferences.instance.removeListener(onChange);
|
|
super.dispose();
|
|
}
|
|
|
|
void onChange() {
|
|
if (mounted) setState(() {});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return SegmentedButton(
|
|
segments: [
|
|
ButtonSegment(
|
|
value: "system",
|
|
icon: const Icon(Icons.app_shortcut),
|
|
label: Text(AppLocalizations.of(context).settingsThemeDevice),
|
|
),
|
|
ButtonSegment(
|
|
value: "ollama",
|
|
icon: const ImageIcon(AssetImage("assets/logo512.png")),
|
|
label: Text(AppLocalizations.of(context).settingsThemeOllama),
|
|
),
|
|
],
|
|
selected: Preferences.instance.themeSystem ? {"system"} : {"ollama"},
|
|
onSelectionChanged: (selection) =>
|
|
Preferences.instance.themeSystem = selection.first == "system",
|
|
);
|
|
}
|
|
}
|