mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-02-24 02:18:50 +01:00
Support selecting secondary theme color in settings
This commit is contained in:
parent
71652c7e9a
commit
df3f3a65d3
18 changed files with 424 additions and 113 deletions
Binary file not shown.
Before Width: | Height: | Size: 9.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 18 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.6 KiB |
|
@ -133,6 +133,14 @@ class PrefController {
|
|||
value: value,
|
||||
);
|
||||
|
||||
Future<void> setSecondarySeedColor(Color? value) => _setOrRemove<Color>(
|
||||
controller: _secondarySeedColorController,
|
||||
setter: (pref, value) =>
|
||||
pref.setSecondarySeedColor(value.withAlpha(0xFF).value),
|
||||
remover: (pref) => pref.setSecondarySeedColor(null),
|
||||
value: value,
|
||||
);
|
||||
|
||||
Future<void> _set<T>({
|
||||
required BehaviorSubject<T> controller,
|
||||
required Future<bool> Function(Pref pref, T value) setter,
|
||||
|
@ -242,4 +250,7 @@ class PrefController {
|
|||
@NpSubjectAccessor(type: "Color?")
|
||||
late final _seedColorController =
|
||||
BehaviorSubject<Color?>.seeded(_c.pref.getSeedColor()?.run(Color.new));
|
||||
@NpSubjectAccessor(type: "Color?")
|
||||
late final _secondarySeedColorController = BehaviorSubject<Color?>.seeded(
|
||||
_c.pref.getSecondarySeedColor()?.run(Color.new));
|
||||
}
|
||||
|
|
|
@ -134,4 +134,11 @@ extension $PrefControllerNpSubjectAccessor on PrefController {
|
|||
Stream<Color?> get seedColorNew => seedColor.skip(1);
|
||||
Stream<Color?> get seedColorChange => seedColor.distinct().skip(1);
|
||||
Color? get seedColorValue => _seedColorController.value;
|
||||
// _secondarySeedColorController
|
||||
ValueStream<Color?> get secondarySeedColor =>
|
||||
_secondarySeedColorController.stream;
|
||||
Stream<Color?> get secondarySeedColorNew => secondarySeedColor.skip(1);
|
||||
Stream<Color?> get secondarySeedColorChange =>
|
||||
secondarySeedColor.distinct().skip(1);
|
||||
Color? get secondarySeedColorValue => _secondarySeedColorController.value;
|
||||
}
|
||||
|
|
|
@ -110,7 +110,9 @@ enum PrefKey implements PrefKeyInterface {
|
|||
isSlideshowReverse,
|
||||
seedColor,
|
||||
isVideoPlayerMute,
|
||||
isVideoPlayerLoop;
|
||||
isVideoPlayerLoop,
|
||||
secondarySeedColor,
|
||||
;
|
||||
|
||||
@override
|
||||
String toStringKey() {
|
||||
|
@ -186,6 +188,8 @@ enum PrefKey implements PrefKeyInterface {
|
|||
return "isVideoPlayerMute";
|
||||
case PrefKey.isVideoPlayerLoop:
|
||||
return "isVideoPlayerLoop";
|
||||
case PrefKey.secondarySeedColor:
|
||||
return "secondarySeedColor";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -276,6 +276,20 @@ extension PrefExtension on Pref {
|
|||
PrefKey.isVideoPlayerLoop,
|
||||
value,
|
||||
(key, value) => provider.setBool(key, value));
|
||||
|
||||
@Deprecated("Use PrefController")
|
||||
int? getSecondarySeedColor() => provider.getInt(PrefKey.secondarySeedColor);
|
||||
@Deprecated("Use PrefController")
|
||||
int getSecondarySeedColorOr(int def) => getSecondarySeedColor() ?? def;
|
||||
@Deprecated("Use PrefController")
|
||||
Future<bool> setSecondarySeedColor(int? value) {
|
||||
if (value == null) {
|
||||
return _remove(PrefKey.secondarySeedColor);
|
||||
} else {
|
||||
return _set<int>(PrefKey.secondarySeedColor, value,
|
||||
(key, value) => provider.setInt(key, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AccountPrefExtension on AccountPref {
|
||||
|
@ -296,8 +310,7 @@ extension AccountPrefExtension on AccountPref {
|
|||
|
||||
bool? isEnableMemoryAlbum() =>
|
||||
provider.getBool(AccountPrefKey.isEnableMemoryAlbum);
|
||||
bool isEnableMemoryAlbumOr([bool def = true]) =>
|
||||
isEnableMemoryAlbum() ?? def;
|
||||
bool isEnableMemoryAlbumOr([bool def = true]) => isEnableMemoryAlbum() ?? def;
|
||||
Future<bool> setEnableMemoryAlbum(bool value) => _set<bool>(
|
||||
AccountPrefKey.isEnableMemoryAlbum,
|
||||
value,
|
||||
|
|
|
@ -393,6 +393,9 @@
|
|||
"@settingsSeedColorPickerTitle": {
|
||||
"description": "Dialog to customize the colors used in app"
|
||||
},
|
||||
"settingsThemePrimaryColor": "Primary",
|
||||
"settingsThemeSecondaryColor": "Secondary",
|
||||
"settingsThemePresets": "Presets",
|
||||
"settingsSeedColorPickerSystemColorButtonLabel": "USE SYSTEM COLOR",
|
||||
"@settingsSeedColorPickerSystemColorButtonLabel": {
|
||||
"description": "Use color provided by the OS, i.e., Material You"
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
"settingsImageEditSaveResultsToServerTitle",
|
||||
"settingsImageEditSaveResultsToServerTrueDescription",
|
||||
"settingsSeedColorSystemColorDescription",
|
||||
"settingsThemePrimaryColor",
|
||||
"settingsThemeSecondaryColor",
|
||||
"settingsThemePresets",
|
||||
"settingsSeedColorPickerSystemColorButtonLabel",
|
||||
"settingsAboutSectionTitle",
|
||||
"settingsVersionTitle",
|
||||
|
@ -234,11 +237,20 @@
|
|||
"errorNoStoragePermission"
|
||||
],
|
||||
|
||||
"cs": [
|
||||
"settingsThemePrimaryColor",
|
||||
"settingsThemeSecondaryColor",
|
||||
"settingsThemePresets"
|
||||
],
|
||||
|
||||
"de": [
|
||||
"settingsShareFolderDialogTitle",
|
||||
"settingsShareFolderDialogDescription",
|
||||
"settingsShareFolderPickerDescription",
|
||||
"settingsPersonProviderTitle",
|
||||
"settingsThemePrimaryColor",
|
||||
"settingsThemeSecondaryColor",
|
||||
"settingsThemePresets",
|
||||
"settingsSeedColorPickerSystemColorButtonLabel",
|
||||
"fileLastSharedByOthersDescription",
|
||||
"multipleFilesLinkShareDialogContent",
|
||||
|
@ -288,6 +300,9 @@
|
|||
"settingsSeedColorDescription",
|
||||
"settingsSeedColorSystemColorDescription",
|
||||
"settingsSeedColorPickerTitle",
|
||||
"settingsThemePrimaryColor",
|
||||
"settingsThemeSecondaryColor",
|
||||
"settingsThemePresets",
|
||||
"settingsSeedColorPickerSystemColorButtonLabel",
|
||||
"settingsDoubleTapExitTitle",
|
||||
"settingsExpertTitle",
|
||||
|
@ -375,9 +390,30 @@
|
|||
"deleteAccountConfirmDialogText"
|
||||
],
|
||||
|
||||
"es": [
|
||||
"settingsThemePrimaryColor",
|
||||
"settingsThemeSecondaryColor",
|
||||
"settingsThemePresets"
|
||||
],
|
||||
|
||||
"fi": [
|
||||
"settingsThemePrimaryColor",
|
||||
"settingsThemeSecondaryColor",
|
||||
"settingsThemePresets"
|
||||
],
|
||||
|
||||
"fr": [
|
||||
"settingsThemePrimaryColor",
|
||||
"settingsThemeSecondaryColor",
|
||||
"settingsThemePresets"
|
||||
],
|
||||
|
||||
"it": [
|
||||
"settingsPersonProviderTitle",
|
||||
"settingsImageEditTitle",
|
||||
"settingsThemePrimaryColor",
|
||||
"settingsThemeSecondaryColor",
|
||||
"settingsThemePresets",
|
||||
"unmuteTooltip",
|
||||
"slideshowTooltip",
|
||||
"enhanceColorPopTitle"
|
||||
|
@ -453,6 +489,9 @@
|
|||
"settingsSeedColorDescription",
|
||||
"settingsSeedColorSystemColorDescription",
|
||||
"settingsSeedColorPickerTitle",
|
||||
"settingsThemePrimaryColor",
|
||||
"settingsThemeSecondaryColor",
|
||||
"settingsThemePresets",
|
||||
"settingsSeedColorPickerSystemColorButtonLabel",
|
||||
"settingsUseBlackInDarkThemeTitle",
|
||||
"settingsUseBlackInDarkThemeTrueDescription",
|
||||
|
@ -737,6 +776,9 @@
|
|||
|
||||
"pl": [
|
||||
"settingsMemoriesRangeValueText",
|
||||
"settingsThemePrimaryColor",
|
||||
"settingsThemeSecondaryColor",
|
||||
"settingsThemePresets",
|
||||
"enhanceColorPopTitle",
|
||||
"imageEditTransformOrientationClockwise",
|
||||
"imageEditTransformOrientationCounterclockwise"
|
||||
|
@ -747,6 +789,9 @@
|
|||
"settingsMetadataTitle",
|
||||
"settingsPersonProviderTitle",
|
||||
"settingsSeedColorSystemColorDescription",
|
||||
"settingsThemePrimaryColor",
|
||||
"settingsThemeSecondaryColor",
|
||||
"settingsThemePresets",
|
||||
"settingsSeedColorPickerSystemColorButtonLabel",
|
||||
"settingsServerVersionTitle",
|
||||
"searchLandingPeopleListEmptyText2",
|
||||
|
@ -765,6 +810,18 @@
|
|||
"deleteAccountConfirmDialogText"
|
||||
],
|
||||
|
||||
"ru": [
|
||||
"settingsThemePrimaryColor",
|
||||
"settingsThemeSecondaryColor",
|
||||
"settingsThemePresets"
|
||||
],
|
||||
|
||||
"tr": [
|
||||
"settingsThemePrimaryColor",
|
||||
"settingsThemeSecondaryColor",
|
||||
"settingsThemePresets"
|
||||
],
|
||||
|
||||
"zh": [
|
||||
"connectingToServer2",
|
||||
"connectingToServerInstruction",
|
||||
|
@ -791,6 +848,9 @@
|
|||
"settingsSeedColorDescription",
|
||||
"settingsSeedColorSystemColorDescription",
|
||||
"settingsSeedColorPickerTitle",
|
||||
"settingsThemePrimaryColor",
|
||||
"settingsThemeSecondaryColor",
|
||||
"settingsThemePresets",
|
||||
"settingsSeedColorPickerSystemColorButtonLabel",
|
||||
"settingsMiscellaneousTitle",
|
||||
"settingsDoubleTapExitTitle",
|
||||
|
@ -919,6 +979,9 @@
|
|||
"settingsSeedColorDescription",
|
||||
"settingsSeedColorSystemColorDescription",
|
||||
"settingsSeedColorPickerTitle",
|
||||
"settingsThemePrimaryColor",
|
||||
"settingsThemeSecondaryColor",
|
||||
"settingsThemePresets",
|
||||
"settingsSeedColorPickerSystemColorButtonLabel",
|
||||
"settingsMiscellaneousTitle",
|
||||
"settingsDoubleTapExitTitle",
|
||||
|
|
|
@ -132,24 +132,24 @@ ThemeData buildDarkTheme(BuildContext context, [ColorScheme? dynamicScheme]) {
|
|||
}
|
||||
}
|
||||
|
||||
Color? getSeedColor(BuildContext context) {
|
||||
return context.read<PrefController>().seedColorValue;
|
||||
}
|
||||
|
||||
ColorScheme _getColorScheme(
|
||||
BuildContext context, ColorScheme? dynamicScheme, Brightness brightness) {
|
||||
var seedColor = getSeedColor(context);
|
||||
if (seedColor == null) {
|
||||
var primary = context.read<PrefController>().seedColorValue;
|
||||
Color? secondary;
|
||||
if (primary == null) {
|
||||
if (dynamicScheme != null) {
|
||||
return dynamicScheme;
|
||||
} else {
|
||||
seedColor = defaultSeedColor;
|
||||
primary = defaultSeedColor;
|
||||
}
|
||||
} else {
|
||||
secondary = context.read<PrefController>().secondarySeedColorValue;
|
||||
}
|
||||
return SeedColorScheme.fromSeeds(
|
||||
brightness: brightness,
|
||||
tones: FlexTones.oneHue(brightness),
|
||||
primaryKey: seedColor,
|
||||
primaryKey: primary,
|
||||
secondaryKey: secondary,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -125,7 +125,8 @@ class _WrappedAppState extends State<_WrappedApp>
|
|||
previous.isDarkTheme != current.isDarkTheme ||
|
||||
previous.isFollowSystemTheme != current.isFollowSystemTheme ||
|
||||
previous.isUseBlackInDarkTheme != current.isUseBlackInDarkTheme ||
|
||||
previous.seedColor != current.seedColor,
|
||||
previous.seedColor != current.seedColor ||
|
||||
previous.secondarySeedColor != current.secondarySeedColor,
|
||||
builder: (context, state) => DynamicColorBuilder(
|
||||
builder: (lightDynamic, darkDynamic) {
|
||||
if (lightDynamic != null) {
|
||||
|
|
|
@ -18,7 +18,8 @@ abstract class $_StateCopyWithWorker {
|
|||
bool? isDarkTheme,
|
||||
bool? isFollowSystemTheme,
|
||||
bool? isUseBlackInDarkTheme,
|
||||
int? seedColor});
|
||||
int? seedColor,
|
||||
int? secondarySeedColor});
|
||||
}
|
||||
|
||||
class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
|
||||
|
@ -30,7 +31,8 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
|
|||
dynamic isDarkTheme,
|
||||
dynamic isFollowSystemTheme,
|
||||
dynamic isUseBlackInDarkTheme,
|
||||
dynamic seedColor = copyWithNull}) {
|
||||
dynamic seedColor = copyWithNull,
|
||||
dynamic secondarySeedColor = copyWithNull}) {
|
||||
return _State(
|
||||
language: language as language_util.AppLanguage? ?? that.language,
|
||||
isDarkTheme: isDarkTheme as bool? ?? that.isDarkTheme,
|
||||
|
@ -39,7 +41,10 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
|
|||
isUseBlackInDarkTheme:
|
||||
isUseBlackInDarkTheme as bool? ?? that.isUseBlackInDarkTheme,
|
||||
seedColor:
|
||||
seedColor == copyWithNull ? that.seedColor : seedColor as int?);
|
||||
seedColor == copyWithNull ? that.seedColor : seedColor as int?,
|
||||
secondarySeedColor: secondarySeedColor == copyWithNull
|
||||
? that.secondarySeedColor
|
||||
: secondarySeedColor as int?);
|
||||
}
|
||||
|
||||
final _State that;
|
||||
|
@ -75,7 +80,7 @@ extension _$_BlocNpLog on _Bloc {
|
|||
extension _$_StateToString on _State {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_State {language: $language, isDarkTheme: $isDarkTheme, isFollowSystemTheme: $isFollowSystemTheme, isUseBlackInDarkTheme: $isUseBlackInDarkTheme, seedColor: $seedColor}";
|
||||
return "_State {language: $language, isDarkTheme: $isDarkTheme, isFollowSystemTheme: $isFollowSystemTheme, isUseBlackInDarkTheme: $isUseBlackInDarkTheme, seedColor: $seedColor, secondarySeedColor: $secondarySeedColor}";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
|||
isFollowSystemTheme: prefController.isFollowSystemThemeValue,
|
||||
isUseBlackInDarkTheme: prefController.isUseBlackInDarkThemeValue,
|
||||
seedColor: prefController.seedColorValue?.value,
|
||||
secondarySeedColor: prefController.secondarySeedColorValue?.value,
|
||||
)) {
|
||||
on<_Init>(_onInit);
|
||||
}
|
||||
|
@ -40,6 +41,10 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
|||
prefController.seedColorChange,
|
||||
onData: (data) => state.copyWith(seedColor: data?.value),
|
||||
),
|
||||
emit.forEachIgnoreError<Color?>(
|
||||
prefController.secondarySeedColorChange,
|
||||
onData: (data) => state.copyWith(secondarySeedColor: data?.value),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ class _State {
|
|||
required this.isFollowSystemTheme,
|
||||
required this.isUseBlackInDarkTheme,
|
||||
required this.seedColor,
|
||||
required this.secondarySeedColor,
|
||||
});
|
||||
|
||||
@override
|
||||
|
@ -19,6 +20,7 @@ class _State {
|
|||
final bool isFollowSystemTheme;
|
||||
final bool isUseBlackInDarkTheme;
|
||||
final int? seedColor;
|
||||
final int? secondarySeedColor;
|
||||
}
|
||||
|
||||
abstract class _Event {}
|
||||
|
|
|
@ -8,11 +8,12 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
|||
isFollowSystemTheme: prefController.isFollowSystemThemeValue,
|
||||
isUseBlackInDarkTheme: prefController.isUseBlackInDarkThemeValue,
|
||||
seedColor: prefController.seedColorValue?.value,
|
||||
secondarySeedColor: prefController.secondarySeedColorValue?.value,
|
||||
)) {
|
||||
on<_Init>(_onInit);
|
||||
on<_SetFollowSystemTheme>(_onSetFollowSystemTheme);
|
||||
on<_SetUseBlackInDarkTheme>(_onSetUseBlackInDarkTheme);
|
||||
on<_SetSeedColor>(_onSetSeedColor);
|
||||
on<_SetThemeColor>(_onSetThemeColor);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -45,6 +46,14 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
|||
return state.copyWith(error: ExceptionEvent(e, stackTrace));
|
||||
},
|
||||
),
|
||||
emit.forEach<Color?>(
|
||||
prefController.secondarySeedColorChange,
|
||||
onData: (data) => state.copyWith(secondarySeedColor: data?.value),
|
||||
onError: (e, stackTrace) {
|
||||
_log.severe("[_onInit] Uncaught exception", e, stackTrace);
|
||||
return state.copyWith(error: ExceptionEvent(e, stackTrace));
|
||||
},
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -59,9 +68,11 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
|||
prefController.setUseBlackInDarkTheme(ev.value);
|
||||
}
|
||||
|
||||
void _onSetSeedColor(_SetSeedColor ev, Emitter<_State> emit) {
|
||||
void _onSetThemeColor(_SetThemeColor ev, Emitter<_State> emit) {
|
||||
_log.info(ev);
|
||||
prefController.setSeedColor(ev.value);
|
||||
prefController
|
||||
..setSeedColor(ev.primary)
|
||||
..setSecondarySeedColor(ev.secondary);
|
||||
}
|
||||
|
||||
final PrefController prefController;
|
||||
|
|
|
@ -7,6 +7,7 @@ class _State {
|
|||
required this.isFollowSystemTheme,
|
||||
required this.isUseBlackInDarkTheme,
|
||||
required this.seedColor,
|
||||
required this.secondarySeedColor,
|
||||
this.error,
|
||||
});
|
||||
|
||||
|
@ -17,6 +18,7 @@ class _State {
|
|||
final bool isUseBlackInDarkTheme;
|
||||
// workaround analyzer bug where Color type can't be recognized
|
||||
final int? seedColor;
|
||||
final int? secondarySeedColor;
|
||||
|
||||
final ExceptionEvent? error;
|
||||
}
|
||||
|
@ -55,11 +57,12 @@ class _SetUseBlackInDarkTheme extends _Event {
|
|||
}
|
||||
|
||||
@toString
|
||||
class _SetSeedColor extends _Event {
|
||||
const _SetSeedColor(this.value);
|
||||
class _SetThemeColor extends _Event {
|
||||
const _SetThemeColor(this.primary, this.secondary);
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
final Color? value;
|
||||
final Color? primary;
|
||||
final Color? secondary;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import 'package:nc_photos/snack_bar_manager.dart';
|
|||
import 'package:nc_photos/theme.dart';
|
||||
import 'package:nc_photos/widget/page_visibility_mixin.dart';
|
||||
import 'package:np_codegen/np_codegen.dart';
|
||||
import 'package:np_common/object_util.dart';
|
||||
import 'package:np_platform_util/np_platform_util.dart';
|
||||
import 'package:to_string/to_string.dart';
|
||||
|
||||
|
@ -25,10 +26,6 @@ part 'theme/bloc.dart';
|
|||
part 'theme/state_event.dart';
|
||||
part 'theme_settings.g.dart';
|
||||
|
||||
// typedef _BlocBuilder = BlocBuilder<_Bloc, _State>;
|
||||
typedef _BlocListener = BlocListener<_Bloc, _State>;
|
||||
typedef _BlocSelector<T> = BlocSelector<_Bloc, _State, T>;
|
||||
|
||||
class ThemeSettings extends StatelessWidget {
|
||||
const ThemeSettings({super.key});
|
||||
|
||||
|
@ -138,53 +135,49 @@ class _SeedColorOption extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _BlocSelector<int?>(
|
||||
selector: (state) => state.seedColor,
|
||||
builder: (context, seedColor) {
|
||||
if (SessionStorage().isSupportDynamicColor) {
|
||||
return ListTile(
|
||||
title: Text(L10n.global().settingsSeedColorTitle),
|
||||
subtitle: Text(seedColor == null
|
||||
? L10n.global().settingsSeedColorSystemColorDescription
|
||||
: L10n.global().settingsSeedColorDescription),
|
||||
trailing: seedColor == null
|
||||
? null
|
||||
: Icon(
|
||||
Icons.circle,
|
||||
size: 32,
|
||||
color: Color(seedColor),
|
||||
),
|
||||
onTap: () => _onSeedColorPressed(context),
|
||||
);
|
||||
} else {
|
||||
return ListTile(
|
||||
title: Text(L10n.global().settingsSeedColorTitle),
|
||||
subtitle: Text(L10n.global().settingsSeedColorDescription),
|
||||
trailing: Icon(
|
||||
Icons.circle,
|
||||
size: 32,
|
||||
color: seedColor?.run(Color.new) ?? defaultSeedColor,
|
||||
),
|
||||
onTap: () => _onSeedColorPressed(context),
|
||||
);
|
||||
}
|
||||
return _BlocBuilder(
|
||||
buildWhen: (previous, current) =>
|
||||
previous.seedColor != current.seedColor ||
|
||||
previous.secondarySeedColor != current.secondarySeedColor,
|
||||
builder: (context, state) {
|
||||
return ListTile(
|
||||
title: Text(L10n.global().settingsSeedColorTitle),
|
||||
subtitle: Text(
|
||||
state.seedColor == null || SessionStorage().isSupportDynamicColor
|
||||
? L10n.global().settingsSeedColorSystemColorDescription
|
||||
: L10n.global().settingsSeedColorDescription),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (state.seedColor != null)
|
||||
Icon(
|
||||
Icons.circle,
|
||||
size: 32,
|
||||
color: Color(state.seedColor!),
|
||||
),
|
||||
if (state.secondarySeedColor != null)
|
||||
Icon(
|
||||
Icons.circle,
|
||||
size: 32,
|
||||
color: Color(state.secondarySeedColor!),
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: () => _onSeedColorPressed(context),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onSeedColorPressed(BuildContext context) async {
|
||||
final result = await showDialog<int>(
|
||||
final parentContext = context;
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) => const _SeedColorPicker(),
|
||||
builder: (context) => BlocProvider.value(
|
||||
value: parentContext.read<_Bloc>(),
|
||||
child: const _SeedColorPicker(),
|
||||
),
|
||||
);
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
if (context.mounted) {
|
||||
context
|
||||
.read<_Bloc>()
|
||||
.add(_SetSeedColor(result == -1 ? null : Color(result)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,26 +194,94 @@ class _SeedColorPickerState extends State<_SeedColorPicker> {
|
|||
return Visibility(
|
||||
visible: _isVisible,
|
||||
child: AlertDialog(
|
||||
title: Text(L10n.global().settingsSeedColorPickerTitle),
|
||||
content: Wrap(
|
||||
children: const [
|
||||
Color(0xFFF44336),
|
||||
Color(0xFF9C27B0),
|
||||
Color(0xFF2196F3),
|
||||
Color(0xFF4CAF50),
|
||||
Color(0xFFFFC107),
|
||||
null,
|
||||
]
|
||||
.map((c) => _SeedColorPickerItem(
|
||||
seedColor: c,
|
||||
onSelected: () => _onItemSelected(context, c?.value),
|
||||
))
|
||||
.toList(),
|
||||
title: Text(L10n.global().settingsSeedColorTitle),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(L10n.global().settingsThemePrimaryColor),
|
||||
leading: const SizedBox(width: 48),
|
||||
trailing: _BlocSelector<int?>(
|
||||
selector: (state) => state.seedColor,
|
||||
builder: (context, seedColor) => _SeedColorPickerItem(
|
||||
seedColor: seedColor?.run(Color.new),
|
||||
onSelected: () => _onPrimaryTap(this.context),
|
||||
),
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
onTap: () => _onPrimaryTap(context),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(L10n.global().settingsThemeSecondaryColor),
|
||||
leading: _BlocSelector<bool>(
|
||||
selector: (state) => state.secondarySeedColor != null,
|
||||
builder: (context, isSecondaryEnabled) => Checkbox(
|
||||
value: isSecondaryEnabled,
|
||||
onChanged: (value) {
|
||||
if (value == true) {
|
||||
_onSecondaryTap(this.context);
|
||||
} else {
|
||||
context.addEvent(_SetThemeColor(
|
||||
context.state.seedColor?.let(Color.new), null));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
trailing: _BlocSelector<int?>(
|
||||
selector: (state) => state.secondarySeedColor,
|
||||
builder: (context, secondarySeedColor) => _SeedColorPickerItem(
|
||||
seedColor: secondarySeedColor?.run(Color.new),
|
||||
onSelected: () => _onSecondaryTap(this.context),
|
||||
),
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
onTap: () => _onSecondaryTap(context),
|
||||
),
|
||||
const Divider(thickness: 1),
|
||||
Text(
|
||||
L10n.global().settingsThemePresets,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
Wrap(
|
||||
children: const [
|
||||
_PresetItem(primary: Color(0xFFF44336)),
|
||||
_PresetItem(primary: Color(0xFF9C27B0)),
|
||||
_PresetItem(primary: Color(0xFF2196F3)),
|
||||
_PresetItem(primary: Color(0xFF4CAF50)),
|
||||
_PresetItem(primary: Color(0xFFFFC107)),
|
||||
_PresetItem(
|
||||
emoji: "\u{1f349}",
|
||||
primary: Color(0xFF009736),
|
||||
secondary: Color(0xFFEE2A35),
|
||||
),
|
||||
_PresetItem(
|
||||
emoji: "\u{1f33d}",
|
||||
primary: Color(0xFFFFC107),
|
||||
secondary: Color(0xFF4CAF50),
|
||||
),
|
||||
_PresetItem(
|
||||
emoji: "\u{1f38f}",
|
||||
primary: Color(0xFF2196F3),
|
||||
secondary: Color(0xFFF44336),
|
||||
),
|
||||
]
|
||||
.map((e) => _PresetItemView(
|
||||
item: e,
|
||||
onSelected: () => _onPresetSelected(
|
||||
context,
|
||||
primary: e.primary,
|
||||
secondary: e.secondary,
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: SessionStorage().isSupportDynamicColor
|
||||
? [
|
||||
TextButton(
|
||||
onPressed: () => _onItemSelected(context, -1),
|
||||
onPressed: () => _onSystemColorSelected(context),
|
||||
child: Text(L10n.global()
|
||||
.settingsSeedColorPickerSystemColorButtonLabel),
|
||||
),
|
||||
|
@ -230,30 +291,87 @@ class _SeedColorPickerState extends State<_SeedColorPicker> {
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> _onItemSelected(BuildContext context, int? seedColor) async {
|
||||
if (seedColor != null) {
|
||||
Navigator.of(context).pop(seedColor);
|
||||
return;
|
||||
}
|
||||
Future<void> _onPrimaryTap(BuildContext context) async {
|
||||
setState(() {
|
||||
_isVisible = false;
|
||||
});
|
||||
final color = await showDialog<Color>(
|
||||
context: context,
|
||||
builder: (_) => const _SeedColorCustomPicker(),
|
||||
barrierColor: Colors.transparent,
|
||||
);
|
||||
Navigator.of(context).pop(color?.value);
|
||||
try {
|
||||
final color = await showDialog<Color>(
|
||||
context: context,
|
||||
builder: (_) => _SeedColorCustomPicker(
|
||||
initialColor:
|
||||
context.bloc.prefController.seedColorValue ?? defaultSeedColor,
|
||||
),
|
||||
barrierColor: Colors.transparent,
|
||||
);
|
||||
if (color == null) {
|
||||
return;
|
||||
}
|
||||
context.addEvent(_SetThemeColor(
|
||||
color, context.state.secondarySeedColor?.let(Color.new)));
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isVisible = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onSecondaryTap(BuildContext context) async {
|
||||
setState(() {
|
||||
_isVisible = false;
|
||||
});
|
||||
try {
|
||||
final color = await showDialog<Color>(
|
||||
context: context,
|
||||
builder: (_) => _SeedColorCustomPicker(
|
||||
initialColor: context.bloc.prefController.secondarySeedColorValue ??
|
||||
defaultSeedColor,
|
||||
),
|
||||
barrierColor: Colors.transparent,
|
||||
);
|
||||
if (color == null) {
|
||||
return;
|
||||
}
|
||||
// enabling secondary automatically enable primary color
|
||||
context.addEvent(_SetThemeColor(
|
||||
context.state.seedColor?.let(Color.new) ?? defaultSeedColor, color));
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isVisible = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onPresetSelected(
|
||||
BuildContext context, {
|
||||
required Color? primary,
|
||||
Color? secondary,
|
||||
}) async {
|
||||
context.addEvent(_SetThemeColor(primary, secondary));
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
Future<void> _onSystemColorSelected(BuildContext context) async {
|
||||
context.addEvent(const _SetThemeColor(null, null));
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
var _isVisible = true;
|
||||
}
|
||||
|
||||
class _SeedColorCustomPicker extends StatefulWidget {
|
||||
const _SeedColorCustomPicker();
|
||||
const _SeedColorCustomPicker({
|
||||
required this.initialColor,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _SeedColorCustomPickerState();
|
||||
|
||||
final Color initialColor;
|
||||
}
|
||||
|
||||
class _SeedColorCustomPickerState extends State<_SeedColorCustomPicker> {
|
||||
|
@ -282,7 +400,7 @@ class _SeedColorCustomPickerState extends State<_SeedColorCustomPicker> {
|
|||
);
|
||||
}
|
||||
|
||||
late var _customColor = getSeedColor(context) ?? defaultSeedColor;
|
||||
late var _customColor = widget.initialColor;
|
||||
}
|
||||
|
||||
class _SeedColorPickerItem extends StatelessWidget {
|
||||
|
@ -302,20 +420,7 @@ class _SeedColorPickerItem extends StatelessWidget {
|
|||
size: _size * .9,
|
||||
color: seedColor,
|
||||
)
|
||||
: Transform.scale(
|
||||
scale: .9,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Image.asset("assets/ic_custom_color_56dp.png"),
|
||||
const Icon(
|
||||
Icons.colorize_outlined,
|
||||
size: _size * .5,
|
||||
color: Colors.black87,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
: const Icon(Icons.edit_outlined),
|
||||
),
|
||||
);
|
||||
if (onSelected != null) {
|
||||
|
@ -332,7 +437,69 @@ class _SeedColorPickerItem extends StatelessWidget {
|
|||
final Color? seedColor;
|
||||
final VoidCallback? onSelected;
|
||||
|
||||
static const _size = 56.0;
|
||||
static const _size = 48.0;
|
||||
}
|
||||
|
||||
class _PresetItemView extends StatelessWidget {
|
||||
const _PresetItemView({
|
||||
required this.item,
|
||||
this.onSelected,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final content = SizedBox.square(
|
||||
dimension: _size,
|
||||
child: Center(
|
||||
child: item.emoji != null
|
||||
? Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.circle,
|
||||
size: _size * .9,
|
||||
color: Colors.white,
|
||||
),
|
||||
Text(
|
||||
item.emoji!,
|
||||
style: const TextStyle(fontSize: _size * .35),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Icon(
|
||||
Icons.circle,
|
||||
size: _size * .9,
|
||||
color: item.primary,
|
||||
),
|
||||
),
|
||||
);
|
||||
if (onSelected != null) {
|
||||
return InkWell(
|
||||
customBorder: const CircleBorder(),
|
||||
onTap: onSelected,
|
||||
child: content,
|
||||
);
|
||||
} else {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
final _PresetItem item;
|
||||
final VoidCallback? onSelected;
|
||||
|
||||
static const _size = 72.0;
|
||||
}
|
||||
|
||||
class _PresetItem {
|
||||
const _PresetItem({
|
||||
this.emoji,
|
||||
required this.primary,
|
||||
this.secondary,
|
||||
});
|
||||
|
||||
final String? emoji;
|
||||
final Color primary;
|
||||
final Color? secondary;
|
||||
}
|
||||
|
||||
/// Based on the original HueRingPicker
|
||||
|
@ -416,3 +583,14 @@ class _HueRingPickerState extends State<_HueRingPicker> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
typedef _BlocBuilder = BlocBuilder<_Bloc, _State>;
|
||||
typedef _BlocListener = BlocListener<_Bloc, _State>;
|
||||
// typedef _BlocListenerT<T> = BlocListenerT<_Bloc, _State, T>;
|
||||
typedef _BlocSelector<T> = BlocSelector<_Bloc, _State, T>;
|
||||
|
||||
extension on BuildContext {
|
||||
_Bloc get bloc => read<_Bloc>();
|
||||
_State get state => bloc.state;
|
||||
void addEvent(_Event event) => bloc.add(event);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ abstract class $_StateCopyWithWorker {
|
|||
{bool? isFollowSystemTheme,
|
||||
bool? isUseBlackInDarkTheme,
|
||||
int? seedColor,
|
||||
int? secondarySeedColor,
|
||||
ExceptionEvent? error});
|
||||
}
|
||||
|
||||
|
@ -28,6 +29,7 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
|
|||
{dynamic isFollowSystemTheme,
|
||||
dynamic isUseBlackInDarkTheme,
|
||||
dynamic seedColor = copyWithNull,
|
||||
dynamic secondarySeedColor = copyWithNull,
|
||||
dynamic error = copyWithNull}) {
|
||||
return _State(
|
||||
isFollowSystemTheme:
|
||||
|
@ -36,6 +38,9 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
|
|||
isUseBlackInDarkTheme as bool? ?? that.isUseBlackInDarkTheme,
|
||||
seedColor:
|
||||
seedColor == copyWithNull ? that.seedColor : seedColor as int?,
|
||||
secondarySeedColor: secondarySeedColor == copyWithNull
|
||||
? that.secondarySeedColor
|
||||
: secondarySeedColor as int?,
|
||||
error: error == copyWithNull ? that.error : error as ExceptionEvent?);
|
||||
}
|
||||
|
||||
|
@ -73,7 +78,7 @@ extension _$_BlocNpLog on _Bloc {
|
|||
extension _$_StateToString on _State {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_State {isFollowSystemTheme: $isFollowSystemTheme, isUseBlackInDarkTheme: $isUseBlackInDarkTheme, seedColor: $seedColor, error: $error}";
|
||||
return "_State {isFollowSystemTheme: $isFollowSystemTheme, isUseBlackInDarkTheme: $isUseBlackInDarkTheme, seedColor: $seedColor, secondarySeedColor: $secondarySeedColor, error: $error}";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,9 +103,9 @@ extension _$_SetUseBlackInDarkThemeToString on _SetUseBlackInDarkTheme {
|
|||
}
|
||||
}
|
||||
|
||||
extension _$_SetSeedColorToString on _SetSeedColor {
|
||||
extension _$_SetThemeColorToString on _SetThemeColor {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_SetSeedColor {value: $value}";
|
||||
return "_SetThemeColor {primary: $primary, secondary: $secondary}";
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue