mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-02-24 18:38:48 +01:00
Add alt color schemes that can be picked in settings
This commit is contained in:
parent
86cdc1f9c8
commit
597961c821
7 changed files with 237 additions and 80 deletions
|
@ -1,69 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
const lightColorScheme = ColorScheme(
|
||||
brightness: Brightness.light,
|
||||
primary: Color(0xFF0061A4),
|
||||
onPrimary: Color(0xFFFFFFFF),
|
||||
primaryContainer: Color(0xFFD1E4FF),
|
||||
onPrimaryContainer: Color(0xFF001D36),
|
||||
secondary: Color(0xFF535F70),
|
||||
onSecondary: Color(0xFFFFFFFF),
|
||||
secondaryContainer: Color(0xFFD7E3F7),
|
||||
onSecondaryContainer: Color(0xFF101C2B),
|
||||
tertiary: Color(0xFF6B5778),
|
||||
onTertiary: Color(0xFFFFFFFF),
|
||||
tertiaryContainer: Color(0xFFF2DAFF),
|
||||
onTertiaryContainer: Color(0xFF251431),
|
||||
error: Color(0xFFBA1A1A),
|
||||
errorContainer: Color(0xFFFFDAD6),
|
||||
onError: Color(0xFFFFFFFF),
|
||||
onErrorContainer: Color(0xFF410002),
|
||||
background: Color(0xFFFDFCFF),
|
||||
onBackground: Color(0xFF1A1C1E),
|
||||
surface: Color(0xFFFDFCFF),
|
||||
onSurface: Color(0xFF1A1C1E),
|
||||
surfaceVariant: Color(0xFFDFE2EB),
|
||||
onSurfaceVariant: Color(0xFF43474E),
|
||||
outline: Color(0xFF73777F),
|
||||
onInverseSurface: Color(0xFFF1F0F4),
|
||||
inverseSurface: Color(0xFF2F3033),
|
||||
inversePrimary: Color(0xFF9ECAFF),
|
||||
shadow: Color(0xFF000000),
|
||||
surfaceTint: Color(0xFF0061A4),
|
||||
// outlineVariant: Color(0xFFC3C7CF),
|
||||
// scrim: Color(0xFF000000),
|
||||
);
|
||||
|
||||
const darkColorScheme = ColorScheme(
|
||||
brightness: Brightness.dark,
|
||||
primary: Color(0xFF9ECAFF),
|
||||
onPrimary: Color(0xFF003258),
|
||||
primaryContainer: Color(0xFF00497D),
|
||||
onPrimaryContainer: Color(0xFFD1E4FF),
|
||||
secondary: Color(0xFFBBC7DB),
|
||||
onSecondary: Color(0xFF253140),
|
||||
secondaryContainer: Color(0xFF3B4858),
|
||||
onSecondaryContainer: Color(0xFFD7E3F7),
|
||||
tertiary: Color(0xFFD6BEE4),
|
||||
onTertiary: Color(0xFF3B2948),
|
||||
tertiaryContainer: Color(0xFF523F5F),
|
||||
onTertiaryContainer: Color(0xFFF2DAFF),
|
||||
error: Color(0xFFFFB4AB),
|
||||
errorContainer: Color(0xFF93000A),
|
||||
onError: Color(0xFF690005),
|
||||
onErrorContainer: Color(0xFFFFDAD6),
|
||||
background: Color(0xFF1A1C1E),
|
||||
onBackground: Color(0xFFE2E2E6),
|
||||
surface: Color(0xFF1A1C1E),
|
||||
onSurface: Color(0xFFE2E2E6),
|
||||
surfaceVariant: Color(0xFF43474E),
|
||||
onSurfaceVariant: Color(0xFFC3C7CF),
|
||||
outline: Color(0xFF8D9199),
|
||||
onInverseSurface: Color(0xFF1A1C1E),
|
||||
inverseSurface: Color(0xFFE2E2E6),
|
||||
inversePrimary: Color(0xFF0061A4),
|
||||
shadow: Color(0xFF000000),
|
||||
surfaceTint: Color(0xFF9ECAFF),
|
||||
// outlineVariant: Color(0xFF43474E),
|
||||
// scrim: Color(0xFF000000),
|
||||
);
|
|
@ -406,6 +406,14 @@
|
|||
"@settingsFollowSystemThemeTitle": {
|
||||
"description": "Respect the system dark mode settings introduced on Android 10"
|
||||
},
|
||||
"settingsSeedColorTitle": "Theme color",
|
||||
"@settingsSeedColorTitle": {
|
||||
"description": "Customize the colors used in app"
|
||||
},
|
||||
"settingsSeedColorPickerTitle": "Pick a color",
|
||||
"@settingsSeedColorPickerTitle": {
|
||||
"description": "Dialog to customize the colors used in app"
|
||||
},
|
||||
"settingsUseBlackInDarkThemeTitle": "Darker theme",
|
||||
"@settingsUseBlackInDarkThemeTitle": {
|
||||
"description": "Make the dark theme darker"
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
"settingsImageEditSaveResultsToServerTitle",
|
||||
"settingsImageEditSaveResultsToServerTrueDescription",
|
||||
"settingsImageEditSaveResultsToServerFalseDescription",
|
||||
"settingsSeedColorTitle",
|
||||
"settingsSeedColorPickerTitle",
|
||||
"settingsMiscellaneousTitle",
|
||||
"settingsDoubleTapExitTitle",
|
||||
"settingsPhotosTabSortByNameTitle",
|
||||
|
@ -207,6 +209,8 @@
|
|||
"settingsImageEditSaveResultsToServerTitle",
|
||||
"settingsImageEditSaveResultsToServerTrueDescription",
|
||||
"settingsImageEditSaveResultsToServerFalseDescription",
|
||||
"settingsSeedColorTitle",
|
||||
"settingsSeedColorPickerTitle",
|
||||
"settingsMiscellaneousTitle",
|
||||
"settingsDoubleTapExitTitle",
|
||||
"settingsPhotosTabSortByNameTitle",
|
||||
|
@ -382,6 +386,8 @@
|
|||
"settingsImageEditSaveResultsToServerTitle",
|
||||
"settingsImageEditSaveResultsToServerTrueDescription",
|
||||
"settingsImageEditSaveResultsToServerFalseDescription",
|
||||
"settingsSeedColorTitle",
|
||||
"settingsSeedColorPickerTitle",
|
||||
"settingsDoubleTapExitTitle",
|
||||
"slideshowSetupDialogReverseTitle",
|
||||
"shareMethodPreviewTitle",
|
||||
|
@ -449,10 +455,17 @@
|
|||
|
||||
"es": [
|
||||
"settingsEnhanceMaxResolutionTitle2",
|
||||
"settingsSeedColorTitle",
|
||||
"settingsSeedColorPickerTitle",
|
||||
"rootPickerSkipConfirmationDialogContent2",
|
||||
"slideshowSetupDialogReverseTitle"
|
||||
],
|
||||
|
||||
"fi": [
|
||||
"settingsSeedColorTitle",
|
||||
"settingsSeedColorPickerTitle"
|
||||
],
|
||||
|
||||
"fr": [
|
||||
"collectionsTooltip",
|
||||
"settingsLanguageOptionSystemDefaultLabel",
|
||||
|
@ -470,6 +483,8 @@
|
|||
"settingsImageEditSaveResultsToServerTitle",
|
||||
"settingsImageEditSaveResultsToServerTrueDescription",
|
||||
"settingsImageEditSaveResultsToServerFalseDescription",
|
||||
"settingsSeedColorTitle",
|
||||
"settingsSeedColorPickerTitle",
|
||||
"settingsMiscellaneousTitle",
|
||||
"settingsDoubleTapExitTitle",
|
||||
"settingsPhotosTabSortByNameTitle",
|
||||
|
@ -569,6 +584,8 @@
|
|||
"settingsImageEditSaveResultsToServerTitle",
|
||||
"settingsImageEditSaveResultsToServerTrueDescription",
|
||||
"settingsImageEditSaveResultsToServerFalseDescription",
|
||||
"settingsSeedColorTitle",
|
||||
"settingsSeedColorPickerTitle",
|
||||
"settingsMiscellaneousTitle",
|
||||
"settingsDoubleTapExitTitle",
|
||||
"settingsPhotosTabSortByNameTitle",
|
||||
|
@ -686,6 +703,8 @@
|
|||
"settingsImageEditSaveResultsToServerTitle",
|
||||
"settingsImageEditSaveResultsToServerTrueDescription",
|
||||
"settingsImageEditSaveResultsToServerFalseDescription",
|
||||
"settingsSeedColorTitle",
|
||||
"settingsSeedColorPickerTitle",
|
||||
"settingsMiscellaneousTitle",
|
||||
"settingsDoubleTapExitTitle",
|
||||
"settingsPhotosTabSortByNameTitle",
|
||||
|
@ -782,6 +801,8 @@
|
|||
"settingsImageEditSaveResultsToServerTitle",
|
||||
"settingsImageEditSaveResultsToServerTrueDescription",
|
||||
"settingsImageEditSaveResultsToServerFalseDescription",
|
||||
"settingsSeedColorTitle",
|
||||
"settingsSeedColorPickerTitle",
|
||||
"settingsMiscellaneousTitle",
|
||||
"settingsDoubleTapExitTitle",
|
||||
"settingsPhotosTabSortByNameTitle",
|
||||
|
@ -878,6 +899,8 @@
|
|||
"settingsImageEditSaveResultsToServerTitle",
|
||||
"settingsImageEditSaveResultsToServerTrueDescription",
|
||||
"settingsImageEditSaveResultsToServerFalseDescription",
|
||||
"settingsSeedColorTitle",
|
||||
"settingsSeedColorPickerTitle",
|
||||
"settingsMiscellaneousTitle",
|
||||
"settingsDoubleTapExitTitle",
|
||||
"settingsPhotosTabSortByNameTitle",
|
||||
|
@ -974,6 +997,8 @@
|
|||
"settingsImageEditSaveResultsToServerTitle",
|
||||
"settingsImageEditSaveResultsToServerTrueDescription",
|
||||
"settingsImageEditSaveResultsToServerFalseDescription",
|
||||
"settingsSeedColorTitle",
|
||||
"settingsSeedColorPickerTitle",
|
||||
"settingsMiscellaneousTitle",
|
||||
"settingsDoubleTapExitTitle",
|
||||
"settingsPhotosTabSortByNameTitle",
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
|
||||
class M3 extends ThemeExtension<M3> {
|
||||
const M3({
|
||||
required this.seed,
|
||||
required this.checkbox,
|
||||
required this.filterChip,
|
||||
required this.listTile,
|
||||
|
@ -11,11 +12,13 @@ class M3 extends ThemeExtension<M3> {
|
|||
|
||||
@override
|
||||
M3 copyWith({
|
||||
Color? seed,
|
||||
M3Checkbox? checkbox,
|
||||
M3FilterChip? filterChip,
|
||||
M3ListTile? listTile,
|
||||
}) =>
|
||||
M3(
|
||||
seed: seed ?? this.seed,
|
||||
checkbox: checkbox ?? this.checkbox,
|
||||
filterChip: filterChip ?? this.filterChip,
|
||||
listTile: listTile ?? this.listTile,
|
||||
|
@ -27,12 +30,14 @@ class M3 extends ThemeExtension<M3> {
|
|||
return this;
|
||||
}
|
||||
return M3(
|
||||
seed: Color.lerp(seed, other.seed, t)!,
|
||||
checkbox: checkbox.lerp(other.checkbox, t),
|
||||
filterChip: filterChip.lerp(other.filterChip, t),
|
||||
listTile: listTile.lerp(other.listTile, t),
|
||||
);
|
||||
}
|
||||
|
||||
final Color seed;
|
||||
final M3Checkbox checkbox;
|
||||
final M3FilterChip filterChip;
|
||||
final M3ListTile listTile;
|
||||
|
|
|
@ -274,6 +274,11 @@ class Pref {
|
|||
value,
|
||||
(key, value) => provider.setBool(key, value));
|
||||
|
||||
int? getSeedColor() => provider.getInt(PrefKey.seedColor);
|
||||
int getSeedColorOr(int def) => getSeedColor() ?? def;
|
||||
Future<bool> setSeedColor(int value) => _set<int>(
|
||||
PrefKey.seedColor, value, (key, value) => provider.setInt(key, value));
|
||||
|
||||
Future<bool> _set<T>(PrefKey key, T value,
|
||||
Future<bool> Function(PrefKey key, T value) setFn) async {
|
||||
if (await setFn(key, value)) {
|
||||
|
@ -589,6 +594,7 @@ enum PrefKey {
|
|||
saveEditResultToServer,
|
||||
hasShownSaveEditResultDialog,
|
||||
isSlideshowReverse,
|
||||
seedColor,
|
||||
|
||||
// account pref
|
||||
isEnableFaceRecognitionApp,
|
||||
|
@ -666,6 +672,8 @@ extension on PrefKey {
|
|||
return "hasShownSaveEditResultDialog";
|
||||
case PrefKey.isSlideshowReverse:
|
||||
return "isSlideshowReverse";
|
||||
case PrefKey.seedColor:
|
||||
return "seedColor";
|
||||
|
||||
// account pref
|
||||
case PrefKey.isEnableFaceRecognitionApp:
|
||||
|
|
|
@ -1,8 +1,32 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:nc_photos/color_schemes.g.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:nc_photos/material3.dart';
|
||||
import 'package:nc_photos/pref.dart';
|
||||
|
||||
enum SeedColor {
|
||||
// the order must NOT change
|
||||
amber,
|
||||
blue,
|
||||
green,
|
||||
purple,
|
||||
red;
|
||||
|
||||
Color toColor() {
|
||||
switch (this) {
|
||||
case amber:
|
||||
return const Color(0xFFFFC107);
|
||||
case blue:
|
||||
return const Color(0xFF2196F3);
|
||||
case green:
|
||||
return const Color(0xFF4CAF50);
|
||||
case purple:
|
||||
return const Color(0xFF9C27B0);
|
||||
case red:
|
||||
return const Color(0xFFF44336);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ThemeExtension on ThemeData {
|
||||
double get widthLimitedContentMaxWidth => 550.0;
|
||||
|
||||
|
@ -56,21 +80,32 @@ ThemeData buildTheme(Brightness brightness) {
|
|||
}
|
||||
|
||||
ThemeData buildLightTheme() {
|
||||
final theme = _applyColorScheme(lightColorScheme);
|
||||
return theme;
|
||||
final seedColor = getSeedColor();
|
||||
final color = seedColor.toColor();
|
||||
final colorScheme = ColorScheme.fromSeed(
|
||||
seedColor: color,
|
||||
);
|
||||
return _applyColorScheme(colorScheme, color);
|
||||
}
|
||||
|
||||
ThemeData buildDarkTheme() {
|
||||
final ThemeData theme;
|
||||
final seedColor = getSeedColor();
|
||||
final color = seedColor.toColor();
|
||||
final colorScheme = ColorScheme.fromSeed(
|
||||
seedColor: color,
|
||||
brightness: Brightness.dark,
|
||||
);
|
||||
if (Pref().isUseBlackInDarkThemeOr(false)) {
|
||||
theme = _applyColorScheme(darkColorScheme.copyWith(
|
||||
background: Colors.black,
|
||||
surface: Colors.grey[900],
|
||||
));
|
||||
return _applyColorScheme(
|
||||
colorScheme.copyWith(
|
||||
background: Colors.black,
|
||||
surface: Colors.grey[900],
|
||||
),
|
||||
color,
|
||||
);
|
||||
} else {
|
||||
theme = _applyColorScheme(darkColorScheme);
|
||||
return _applyColorScheme(colorScheme, color);
|
||||
}
|
||||
return theme;
|
||||
}
|
||||
|
||||
ThemeData buildDarkModeSwitchTheme(BuildContext context) {
|
||||
|
@ -83,7 +118,23 @@ ThemeData buildDarkModeSwitchTheme(BuildContext context) {
|
|||
);
|
||||
}
|
||||
|
||||
ThemeData _applyColorScheme(ColorScheme colorScheme) {
|
||||
SeedColor getSeedColor() {
|
||||
final index = Pref().getSeedColor();
|
||||
if (index == null) {
|
||||
return SeedColor.blue;
|
||||
} else {
|
||||
SeedColor seedColor;
|
||||
try {
|
||||
seedColor = SeedColor.values[index];
|
||||
} catch (e, stackTrace) {
|
||||
_log.severe("[getSeedColor] Uncaught exception", e, stackTrace);
|
||||
seedColor = SeedColor.blue;
|
||||
}
|
||||
return seedColor;
|
||||
}
|
||||
}
|
||||
|
||||
ThemeData _applyColorScheme(ColorScheme colorScheme, Color seedColor) {
|
||||
return ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: colorScheme.brightness,
|
||||
|
@ -173,6 +224,7 @@ ThemeData _applyColorScheme(ColorScheme colorScheme) {
|
|||
),
|
||||
extensions: [
|
||||
M3(
|
||||
seed: seedColor,
|
||||
checkbox: M3Checkbox(
|
||||
disabled: M3CheckboxDisabled(
|
||||
container: colorScheme.onSurface.withOpacity(.38),
|
||||
|
@ -194,3 +246,5 @@ ThemeData _applyColorScheme(ColorScheme colorScheme) {
|
|||
],
|
||||
);
|
||||
}
|
||||
|
||||
final _log = Logger("theme");
|
||||
|
|
|
@ -22,6 +22,7 @@ import 'package:nc_photos/platform/notification.dart';
|
|||
import 'package:nc_photos/pref.dart';
|
||||
import 'package:nc_photos/service.dart';
|
||||
import 'package:nc_photos/snack_bar_manager.dart';
|
||||
import 'package:nc_photos/theme.dart';
|
||||
import 'package:nc_photos/url_launcher_util.dart';
|
||||
import 'package:nc_photos/widget/fancy_option_picker.dart';
|
||||
import 'package:nc_photos/widget/gps_map.dart';
|
||||
|
@ -1473,6 +1474,7 @@ class _ThemeSettingsState extends State<_ThemeSettings> {
|
|||
super.initState();
|
||||
_isFollowSystemTheme = Pref().isFollowSystemThemeOr(false);
|
||||
_isUseBlackInDarkTheme = Pref().isUseBlackInDarkThemeOr(false);
|
||||
_seedColor = getSeedColor();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -1494,6 +1496,15 @@ class _ThemeSettingsState extends State<_ThemeSettings> {
|
|||
SliverList(
|
||||
delegate: SliverChildListDelegate(
|
||||
[
|
||||
ListTile(
|
||||
title: Text(L10n.global().settingsSeedColorTitle),
|
||||
trailing: Icon(
|
||||
Icons.circle,
|
||||
size: 32,
|
||||
color: _seedColor.toColor(),
|
||||
),
|
||||
onTap: () => _onSeedColorPressed(context),
|
||||
),
|
||||
if (platform_k.isAndroid &&
|
||||
AndroidInfo().sdkInt >= AndroidVersion.Q)
|
||||
SwitchListTile(
|
||||
|
@ -1559,12 +1570,127 @@ class _ThemeSettingsState extends State<_ThemeSettings> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _onSeedColorPressed(BuildContext context) async {
|
||||
final result = await showDialog<SeedColor>(
|
||||
context: context,
|
||||
builder: (context) => _SeedColorPicker(
|
||||
initialColor: _seedColor,
|
||||
),
|
||||
);
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final oldValue = _seedColor;
|
||||
setState(() {
|
||||
_seedColor = result;
|
||||
});
|
||||
if (await Pref().setSeedColor(result.index)) {
|
||||
KiwiContainer().resolve<EventBus>().fire(ThemeChangedEvent());
|
||||
} else {
|
||||
_log.severe("[_onSeedColorPressed] Failed writing pref");
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global().writePreferenceFailureNotification),
|
||||
duration: k.snackBarDurationNormal,
|
||||
));
|
||||
setState(() {
|
||||
_seedColor = oldValue;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
late bool _isFollowSystemTheme;
|
||||
late bool _isUseBlackInDarkTheme;
|
||||
late SeedColor _seedColor;
|
||||
|
||||
static final _log = Logger("widget.settings._ThemeSettingsState");
|
||||
}
|
||||
|
||||
class _SeedColorPicker extends StatefulWidget {
|
||||
const _SeedColorPicker({
|
||||
required this.initialColor,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _SeedColorPickerState();
|
||||
|
||||
final SeedColor initialColor;
|
||||
}
|
||||
|
||||
class _SeedColorPickerState extends State<_SeedColorPicker> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(L10n.global().settingsSeedColorPickerTitle),
|
||||
content: Wrap(
|
||||
children: const [
|
||||
SeedColor.red,
|
||||
SeedColor.purple,
|
||||
SeedColor.blue,
|
||||
SeedColor.green,
|
||||
SeedColor.amber,
|
||||
]
|
||||
.map((c) => _SeedColorPickerItem(
|
||||
seedColor: c,
|
||||
isSelected: c == widget.initialColor,
|
||||
onSelected: () => _onItemSelected(context, c),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onItemSelected(BuildContext context, SeedColor seedColor) {
|
||||
Navigator.of(context).pop(seedColor);
|
||||
}
|
||||
}
|
||||
|
||||
class _SeedColorPickerItem extends StatelessWidget {
|
||||
const _SeedColorPickerItem({
|
||||
required this.seedColor,
|
||||
required this.isSelected,
|
||||
this.onSelected,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final content = SizedBox.square(
|
||||
dimension: _size,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
if (isSelected)
|
||||
Icon(
|
||||
Icons.circle,
|
||||
size: _size,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
Icon(
|
||||
Icons.circle,
|
||||
size: _size - _size * .2,
|
||||
color: seedColor.toColor(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (onSelected != null) {
|
||||
return InkWell(
|
||||
customBorder: const CircleBorder(),
|
||||
onTap: onSelected,
|
||||
child: content,
|
||||
);
|
||||
} else {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
final SeedColor seedColor;
|
||||
final bool isSelected;
|
||||
final VoidCallback? onSelected;
|
||||
|
||||
static const _size = 56.0;
|
||||
}
|
||||
|
||||
class _MiscSettings extends StatefulWidget {
|
||||
const _MiscSettings({Key? key}) : super(key: key);
|
||||
|
||||
|
|
Loading…
Reference in a new issue