Support selecting secondary theme color in settings

This commit is contained in:
Ming Ming 2024-05-18 22:00:25 +08:00
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

View file

@ -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));
}

View file

@ -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;
}

View file

@ -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";
}
}
}

View file

@ -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,

View file

@ -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"

View file

@ -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",

View file

@ -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,
);
}

View file

@ -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) {

View file

@ -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}";
}
}

View file

@ -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),
),
]);
}

View file

@ -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 {}

View file

@ -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;

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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}";
}
}