From 24a2747c20636450898b5b09b4ef52143556ab9f Mon Sep 17 00:00:00 2001 From: Vincent Velociter Date: Sat, 7 Dec 2024 12:31:26 +0100 Subject: [PATCH 01/17] WIP on custom theme --- lib/src/app.dart | 29 +++- lib/src/init.dart | 19 +- .../model/settings/general_preferences.dart | 43 ++--- lib/src/utils/color_palette.dart | 6 + lib/src/utils/json.dart | 58 +++++++ lib/src/view/puzzle/puzzle_tab_screen.dart | 4 - .../settings/account_preferences_screen.dart | 1 - .../settings/app_background_mode_screen.dart | 2 +- lib/src/view/settings/board_theme_screen.dart | 20 +-- .../view/settings/settings_tab_screen.dart | 12 +- lib/src/view/settings/theme_screen.dart | 162 ++++++++++++++++-- lib/src/widgets/settings.dart | 3 + pubspec.lock | 8 + pubspec.yaml | 1 + 14 files changed, 286 insertions(+), 82 deletions(-) diff --git a/lib/src/app.dart b/lib/src/app.dart index 158855a519..40cf12ec4c 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -17,6 +17,7 @@ import 'package:lichess_mobile/src/navigation.dart'; import 'package:lichess_mobile/src/network/connectivity.dart'; import 'package:lichess_mobile/src/network/http.dart'; import 'package:lichess_mobile/src/network/socket.dart'; +import 'package:lichess_mobile/src/styles/lichess_colors.dart'; import 'package:lichess_mobile/src/styles/styles.dart'; import 'package:lichess_mobile/src/utils/screen.dart'; @@ -139,13 +140,27 @@ class _AppState extends ConsumerState { final dynamicColorScheme = brightness == Brightness.light ? fixedLightScheme : fixedDarkScheme; - final colorScheme = - generalPrefs.systemColors && dynamicColorScheme != null - ? dynamicColorScheme - : ColorScheme.fromSeed( - seedColor: boardTheme.colors.darkSquare, - brightness: brightness, - ); + ColorScheme colorScheme; + if (generalPrefs.customThemeEnabled) { + if (generalPrefs.customThemeSeed != null) { + colorScheme = ColorScheme.fromSeed( + seedColor: generalPrefs.customThemeSeed!, + brightness: brightness, + ); + } else if (dynamicColorScheme != null) { + colorScheme = dynamicColorScheme; + } else { + colorScheme = ColorScheme.fromSeed( + seedColor: LichessColors.primary[500]!, + brightness: brightness, + ); + } + } else { + colorScheme = ColorScheme.fromSeed( + seedColor: boardTheme.colors.darkSquare, + brightness: brightness, + ); + } final cupertinoThemeData = CupertinoThemeData( primaryColor: colorScheme.primary, diff --git a/lib/src/init.dart b/lib/src/init.dart index 145117567f..d4ac98c44f 100644 --- a/lib/src/init.dart +++ b/lib/src/init.dart @@ -12,6 +12,7 @@ import 'package:lichess_mobile/src/db/secure_storage.dart'; import 'package:lichess_mobile/src/model/notifications/notification_service.dart'; import 'package:lichess_mobile/src/model/notifications/notifications.dart'; import 'package:lichess_mobile/src/model/settings/board_preferences.dart'; +import 'package:lichess_mobile/src/model/settings/general_preferences.dart'; import 'package:lichess_mobile/src/model/settings/preferences_storage.dart'; import 'package:lichess_mobile/src/utils/chessboard.dart'; import 'package:lichess_mobile/src/utils/color_palette.dart'; @@ -92,11 +93,19 @@ Future androidDisplayInitialization(WidgetsBinding widgetsBinding) async { await DynamicColorPlugin.getCorePalette().then((value) { setCorePalette(value); - if (getCorePalette() != null && prefs.getString(PrefCategory.board.storageKey) == null) { - prefs.setString( - PrefCategory.board.storageKey, - jsonEncode(BoardPrefs.defaults.copyWith(boardTheme: BoardTheme.system)), - ); + if (getCorePalette() != null) { + if (prefs.getString(PrefCategory.general.storageKey) == null) { + prefs.setString( + PrefCategory.general.storageKey, + jsonEncode(GeneralPrefs.defaults.copyWith(customThemeEnabled: true)), + ); + } + if (prefs.getString(PrefCategory.board.storageKey) == null) { + prefs.setString( + PrefCategory.board.storageKey, + jsonEncode(BoardPrefs.defaults.copyWith(boardTheme: BoardTheme.system)), + ); + } } }); } catch (e) { diff --git a/lib/src/model/settings/general_preferences.dart b/lib/src/model/settings/general_preferences.dart index 70f18c3e69..b61dfc3b77 100644 --- a/lib/src/model/settings/general_preferences.dart +++ b/lib/src/model/settings/general_preferences.dart @@ -1,8 +1,9 @@ -import 'dart:ui' show Locale; +import 'dart:ui' show Color, Locale; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:lichess_mobile/src/model/settings/board_preferences.dart'; import 'package:lichess_mobile/src/model/settings/preferences_storage.dart'; +import 'package:lichess_mobile/src/utils/json.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'general_preferences.freezed.dart'; @@ -26,7 +27,7 @@ class GeneralPreferences extends _$GeneralPreferences with PreferencesStorage setThemeMode(BackgroundThemeMode themeMode) { + Future setBackgroundThemeMode(BackgroundThemeMode themeMode) { return save(state.copyWith(themeMode: themeMode)); } @@ -46,9 +47,9 @@ class GeneralPreferences extends _$GeneralPreferences with PreferencesStorage toggleSystemColors() async { - await save(state.copyWith(systemColors: !state.systemColors)); - if (state.systemColors == false) { + Future toggleCustomTheme() async { + await save(state.copyWith(customThemeEnabled: !state.customThemeEnabled)); + if (state.customThemeEnabled == false) { final boardTheme = ref.read(boardPreferencesProvider).boardTheme; if (boardTheme == BoardTheme.system) { await ref.read(boardPreferencesProvider.notifier).setBoardTheme(BoardTheme.brown); @@ -57,27 +58,10 @@ class GeneralPreferences extends _$GeneralPreferences with PreferencesStorage? _localeToJson(Locale? locale) { - return locale != null - ? { - 'languageCode': locale.languageCode, - 'countryCode': locale.countryCode, - 'scriptCode': locale.scriptCode, - } - : null; -} -Locale? _localeFromJson(Map? json) { - if (json == null) { - return null; + Future setCustomThemeSeed(Color? color) { + return save(state.copyWith(customThemeSeed: color)); } - return Locale.fromSubtags( - languageCode: json['languageCode'] as String, - countryCode: json['countryCode'] as String?, - scriptCode: json['scriptCode'] as String?, - ); } @Freezed(fromJson: true, toJson: true) @@ -89,11 +73,14 @@ class GeneralPrefs with _$GeneralPrefs implements Serializable { @JsonKey(unknownEnumValue: SoundTheme.standard) required SoundTheme soundTheme, @JsonKey(defaultValue: 0.8) required double masterVolume, - /// Should enable system color palette (android 12+ only) - @JsonKey(defaultValue: true) required bool systemColors, + /// Should enable custom theme + @JsonKey(defaultValue: false) required bool customThemeEnabled, + + /// Custom theme seed color + @ColorConverter() Color? customThemeSeed, /// Locale to use in the app, use system locale if null - @JsonKey(toJson: _localeToJson, fromJson: _localeFromJson) Locale? locale, + @LocaleConverter() Locale? locale, }) = _GeneralPrefs; static const defaults = GeneralPrefs( @@ -101,7 +88,7 @@ class GeneralPrefs with _$GeneralPrefs implements Serializable { isSoundEnabled: true, soundTheme: SoundTheme.standard, masterVolume: 0.8, - systemColors: true, + customThemeEnabled: true, ); factory GeneralPrefs.fromJson(Map json) { diff --git a/lib/src/utils/color_palette.dart b/lib/src/utils/color_palette.dart index 6ecdd18d82..70c8d262b3 100644 --- a/lib/src/utils/color_palette.dart +++ b/lib/src/utils/color_palette.dart @@ -45,6 +45,12 @@ void setCorePalette(CorePalette? palette) { } } +Color? getCorePalettePrimary() { + return _corePalette?.primary != null + ? Color(_corePalette!.primary.get(50)) + : null; +} + /// Get the core palette if available (android 12+ only). CorePalette? getCorePalette() { return _corePalette; diff --git a/lib/src/utils/json.dart b/lib/src/utils/json.dart index 2bc6852ebe..4a19699502 100644 --- a/lib/src/utils/json.dart +++ b/lib/src/utils/json.dart @@ -1,6 +1,64 @@ +import 'dart:ui' show Color, Locale; + import 'package:deep_pick/deep_pick.dart'; +import 'package:json_annotation/json_annotation.dart'; import 'package:lichess_mobile/src/model/common/uci.dart'; +class LocaleConverter implements JsonConverter?> { + const LocaleConverter(); + + @override + Locale? fromJson(Map? json) { + if (json == null) { + return null; + } + return Locale.fromSubtags( + languageCode: json['languageCode'] as String, + countryCode: json['countryCode'] as String?, + scriptCode: json['scriptCode'] as String?, + ); + } + + @override + Map? toJson(Locale? locale) { + return locale != null + ? { + 'languageCode': locale.languageCode, + 'countryCode': locale.countryCode, + 'scriptCode': locale.scriptCode, + } + : null; + } +} + +class ColorConverter implements JsonConverter?> { + const ColorConverter(); + + @override + Color? fromJson(Map? json) { + return json != null + ? Color.from( + alpha: json['a'] as double, + red: json['r'] as double, + green: json['g'] as double, + blue: json['b'] as double, + ) + : null; + } + + @override + Map? toJson(Color? color) { + return color != null + ? { + 'a': color.a, + 'r': color.r, + 'g': color.g, + 'b': color.b, + } + : null; + } +} + extension UciExtension on Pick { /// Matches a UciCharPair from a string. UciCharPair asUciCharPairOrThrow() { diff --git a/lib/src/view/puzzle/puzzle_tab_screen.dart b/lib/src/view/puzzle/puzzle_tab_screen.dart index a870b218b9..189752b604 100644 --- a/lib/src/view/puzzle/puzzle_tab_screen.dart +++ b/lib/src/view/puzzle/puzzle_tab_screen.dart @@ -372,10 +372,6 @@ class _PuzzleMenuListTile extends StatelessWidget { leading: Icon( icon, size: Styles.mainListTileIconSize, - color: - Theme.of(context).platform == TargetPlatform.iOS - ? CupertinoTheme.of(context).primaryColor - : Theme.of(context).colorScheme.primary, ), title: Text(title, style: Styles.mainListTileTitle), subtitle: Text(subtitle, maxLines: 3), diff --git a/lib/src/view/settings/account_preferences_screen.dart b/lib/src/view/settings/account_preferences_screen.dart index 83c65bfa7a..bb77c2a493 100644 --- a/lib/src/view/settings/account_preferences_screen.dart +++ b/lib/src/view/settings/account_preferences_screen.dart @@ -116,7 +116,6 @@ class _AccountPreferencesScreenState extends ConsumerState ref .read(generalPreferencesProvider.notifier) - .setThemeMode(value ?? BackgroundThemeMode.system); + .setBackgroundThemeMode(value ?? BackgroundThemeMode.system); return SafeArea( child: ListView( diff --git a/lib/src/view/settings/board_theme_screen.dart b/lib/src/view/settings/board_theme_screen.dart index 6e3e128a1f..6654f3548d 100644 --- a/lib/src/view/settings/board_theme_screen.dart +++ b/lib/src/view/settings/board_theme_screen.dart @@ -2,9 +2,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:lichess_mobile/src/model/settings/board_preferences.dart'; -import 'package:lichess_mobile/src/model/settings/general_preferences.dart'; +import 'package:lichess_mobile/src/utils/color_palette.dart'; import 'package:lichess_mobile/src/utils/l10n_context.dart'; -import 'package:lichess_mobile/src/utils/system.dart'; import 'package:lichess_mobile/src/widgets/list.dart'; import 'package:lichess_mobile/src/widgets/platform.dart'; @@ -30,18 +29,13 @@ class _Body extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final boardTheme = ref.watch(boardPreferencesProvider.select((p) => p.boardTheme)); - final hasSystemColors = ref.watch(generalPreferencesProvider.select((p) => p.systemColors)); + final hasSystemColors = getCorePalette() != null; - final androidVersion = ref.watch(androidVersionProvider).whenOrNull(data: (v) => v); - - final choices = - BoardTheme.values - .where( - (t) => - t != BoardTheme.system || - (hasSystemColors && androidVersion != null && androidVersion.sdkInt >= 31), - ) - .toList(); + final choices = BoardTheme.values + .where( + (t) => t != BoardTheme.system || hasSystemColors, + ) + .toList(); void onChanged(BoardTheme? value) => ref.read(boardPreferencesProvider.notifier).setBoardTheme(value ?? BoardTheme.brown); diff --git a/lib/src/view/settings/settings_tab_screen.dart b/lib/src/view/settings/settings_tab_screen.dart index f1dbdc0cfb..091fecab8b 100644 --- a/lib/src/view/settings/settings_tab_screen.dart +++ b/lib/src/view/settings/settings_tab_screen.dart @@ -207,11 +207,13 @@ class _Body extends ConsumerWidget { context, choices: BackgroundThemeMode.values, selectedItem: generalPrefs.themeMode, - labelBuilder: (t) => Text(AppBackgroundModeScreen.themeTitle(context, t)), - onSelectedItemChanged: - (BackgroundThemeMode? value) => ref - .read(generalPreferencesProvider.notifier) - .setThemeMode(value ?? BackgroundThemeMode.system), + labelBuilder: (t) => + Text(AppBackgroundModeScreen.themeTitle(context, t)), + onSelectedItemChanged: (BackgroundThemeMode? value) => ref + .read(generalPreferencesProvider.notifier) + .setBackgroundThemeMode( + value ?? BackgroundThemeMode.system, + ), ); } else { pushPlatformRoute( diff --git a/lib/src/view/settings/theme_screen.dart b/lib/src/view/settings/theme_screen.dart index 2e1307a8e0..90c8d500f4 100644 --- a/lib/src/view/settings/theme_screen.dart +++ b/lib/src/view/settings/theme_screen.dart @@ -3,18 +3,22 @@ import 'package:chessground/chessground.dart'; import 'package:dartchess/dartchess.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_colorpicker/flutter_colorpicker.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:lichess_mobile/src/constants.dart'; import 'package:lichess_mobile/src/model/settings/board_preferences.dart'; import 'package:lichess_mobile/src/model/settings/general_preferences.dart'; +import 'package:lichess_mobile/src/styles/lichess_colors.dart'; import 'package:lichess_mobile/src/styles/lichess_icons.dart'; +import 'package:lichess_mobile/src/utils/color_palette.dart'; import 'package:lichess_mobile/src/utils/l10n_context.dart'; import 'package:lichess_mobile/src/utils/navigation.dart'; -import 'package:lichess_mobile/src/utils/system.dart'; import 'package:lichess_mobile/src/view/settings/board_theme_screen.dart'; import 'package:lichess_mobile/src/view/settings/piece_set_screen.dart'; import 'package:lichess_mobile/src/widgets/adaptive_choice_picker.dart'; +import 'package:lichess_mobile/src/widgets/buttons.dart'; import 'package:lichess_mobile/src/widgets/list.dart'; +import 'package:lichess_mobile/src/widgets/platform_alert_dialog.dart'; import 'package:lichess_mobile/src/widgets/platform_scaffold.dart'; import 'package:lichess_mobile/src/widgets/settings.dart'; @@ -41,7 +45,6 @@ class _Body extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final generalPrefs = ref.watch(generalPreferencesProvider); final boardPrefs = ref.watch(boardPreferencesProvider); - final androidVersionAsync = ref.watch(androidVersionProvider); const horizontalPadding = 16.0; @@ -50,7 +53,7 @@ class _Body extends ConsumerWidget { LayoutBuilder( builder: (context, constraints) { final double boardSize = math.min( - 400, + 250, constraints.biggest.shortestSide - horizontalPadding * 2, ); return Padding( @@ -82,22 +85,145 @@ class _Body extends ConsumerWidget { ListSection( hasLeading: true, children: [ - if (Theme.of(context).platform == TargetPlatform.android) - androidVersionAsync.maybeWhen( - data: - (version) => - version != null && version.sdkInt >= 31 - ? SwitchSettingTile( - leading: const Icon(Icons.colorize_outlined), - title: Text(context.l10n.mobileSystemColors), - value: generalPrefs.systemColors, - onChanged: (value) { - ref.read(generalPreferencesProvider.notifier).toggleSystemColors(); - }, - ) - : const SizedBox.shrink(), - orElse: () => const SizedBox.shrink(), + SwitchSettingTile( + leading: const Icon(Icons.colorize_outlined), + padding: Theme.of(context).platform == TargetPlatform.iOS + ? const EdgeInsets.symmetric(horizontal: 14, vertical: 8) + : null, + title: const Text('Custom theme'), + // TODO translate + subtitle: const Text( + 'Configure your own app theme using a seed color. Disable to use the chessboard theme.', + maxLines: 3, + ), + value: generalPrefs.customThemeEnabled, + onChanged: (value) { + ref + .read(generalPreferencesProvider.notifier) + .toggleCustomTheme(); + }, + ), + AnimatedCrossFade( + duration: const Duration(milliseconds: 300), + crossFadeState: generalPrefs.customThemeEnabled + ? CrossFadeState.showSecond + : CrossFadeState.showFirst, + firstChild: const SizedBox.shrink(), + secondChild: ListSection( + margin: EdgeInsets.zero, + cupertinoBorderRadius: BorderRadius.zero, + cupertinoClipBehavior: Clip.none, + children: [ + PlatformListTile( + leading: const Icon(Icons.color_lens), + title: const Text('Seed color'), + trailing: generalPrefs.customThemeSeed != null + ? Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: generalPrefs.customThemeSeed, + shape: BoxShape.circle, + ), + ) + : getCorePalette() != null + ? Text(context.l10n.mobileSystemColors) + : Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: LichessColors.primary[500], + shape: BoxShape.circle, + ), + ), + onTap: () { + showAdaptiveDialog( + context: context, + barrierDismissible: false, + builder: (context) { + final defaultColor = getCorePalettePrimary() ?? + LichessColors.primary[500]!; + bool useDefault = + generalPrefs.customThemeSeed == null; + Color color = + generalPrefs.customThemeSeed ?? defaultColor; + return StatefulBuilder( + builder: (context, setState) { + return PlatformAlertDialog( + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ColorPicker( + enableAlpha: false, + pickerColor: color, + onColorChanged: (c) { + setState(() { + useDefault = false; + color = c; + }); + }, + ), + SecondaryButton( + semanticsLabel: getCorePalette() != null + ? context.l10n.mobileSystemColors + : 'Default color', + onPressed: !useDefault + ? () { + setState(() { + useDefault = true; + color = defaultColor; + }); + } + : null, + child: Text( + getCorePalette() != null + ? context.l10n.mobileSystemColors + : 'Default color', + ), + ), + SecondaryButton( + semanticsLabel: context.l10n.cancel, + onPressed: () { + Navigator.of(context).pop(false); + }, + child: Text(context.l10n.cancel), + ), + SecondaryButton( + semanticsLabel: context.l10n.ok, + onPressed: () { + if (useDefault) { + Navigator.of(context).pop(null); + } else { + Navigator.of(context).pop(color); + } + }, + child: Text(context.l10n.ok), + ), + ], + ), + ), + ); + }, + ); + }, + ).then((color) { + if (color != false) { + ref + .read(generalPreferencesProvider.notifier) + .setCustomThemeSeed(color as Color?); + } + }); + }, + ), + ], ), + ), + ], + ), + ListSection( + hasLeading: true, + children: [ SettingsListTile( icon: const Icon(LichessIcons.chess_board), settingsLabel: Text(context.l10n.board), diff --git a/lib/src/widgets/settings.dart b/lib/src/widgets/settings.dart index b9daf76648..b553c49b03 100644 --- a/lib/src/widgets/settings.dart +++ b/lib/src/widgets/settings.dart @@ -71,6 +71,7 @@ class SwitchSettingTile extends StatelessWidget { required this.value, this.onChanged, this.leading, + this.padding, super.key, }); @@ -79,10 +80,12 @@ class SwitchSettingTile extends StatelessWidget { final bool value; final void Function(bool value)? onChanged; final Widget? leading; + final EdgeInsetsGeometry? padding; @override Widget build(BuildContext context) { return PlatformListTile( + padding: padding, leading: leading, title: _SettingsTitle(title: title), subtitle: subtitle, diff --git a/pubspec.lock b/pubspec.lock index 2319ddc62f..accc6a41dd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -555,6 +555,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.4.1" + flutter_colorpicker: + dependency: "direct main" + description: + name: flutter_colorpicker + sha256: "969de5f6f9e2a570ac660fb7b501551451ea2a1ab9e2097e89475f60e07816ea" + url: "https://pub.dev" + source: hosted + version: "1.1.0" flutter_displaymode: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 72f8e3d380..bdcc0193d9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,6 +34,7 @@ dependencies: flutter: sdk: flutter flutter_appauth: ^8.0.0+1 + flutter_colorpicker: ^1.1.0 flutter_displaymode: ^0.6.0 flutter_layout_grid: ^2.0.1 flutter_linkify: ^6.0.0 From ed235150190893a3bc62bd49054182163dd979b6 Mon Sep 17 00:00:00 2001 From: Vincent Velociter Date: Mon, 16 Dec 2024 10:24:30 +0100 Subject: [PATCH 02/17] Reformat files --- lib/src/utils/color_palette.dart | 4 +- lib/src/utils/json.dart | 27 +++--- lib/src/view/puzzle/puzzle_tab_screen.dart | 5 +- .../settings/account_preferences_screen.dart | 5 +- lib/src/view/settings/board_theme_screen.dart | 7 +- .../view/settings/settings_tab_screen.dart | 12 ++- lib/src/view/settings/theme_screen.dart | 85 ++++++++++--------- 7 files changed, 63 insertions(+), 82 deletions(-) diff --git a/lib/src/utils/color_palette.dart b/lib/src/utils/color_palette.dart index 70c8d262b3..2419eb73b5 100644 --- a/lib/src/utils/color_palette.dart +++ b/lib/src/utils/color_palette.dart @@ -46,9 +46,7 @@ void setCorePalette(CorePalette? palette) { } Color? getCorePalettePrimary() { - return _corePalette?.primary != null - ? Color(_corePalette!.primary.get(50)) - : null; + return _corePalette?.primary != null ? Color(_corePalette!.primary.get(50)) : null; } /// Get the core palette if available (android 12+ only). diff --git a/lib/src/utils/json.dart b/lib/src/utils/json.dart index 4a19699502..cea4664d8c 100644 --- a/lib/src/utils/json.dart +++ b/lib/src/utils/json.dart @@ -23,10 +23,10 @@ class LocaleConverter implements JsonConverter?> { Map? toJson(Locale? locale) { return locale != null ? { - 'languageCode': locale.languageCode, - 'countryCode': locale.countryCode, - 'scriptCode': locale.scriptCode, - } + 'languageCode': locale.languageCode, + 'countryCode': locale.countryCode, + 'scriptCode': locale.scriptCode, + } : null; } } @@ -38,24 +38,17 @@ class ColorConverter implements JsonConverter?> { Color? fromJson(Map? json) { return json != null ? Color.from( - alpha: json['a'] as double, - red: json['r'] as double, - green: json['g'] as double, - blue: json['b'] as double, - ) + alpha: json['a'] as double, + red: json['r'] as double, + green: json['g'] as double, + blue: json['b'] as double, + ) : null; } @override Map? toJson(Color? color) { - return color != null - ? { - 'a': color.a, - 'r': color.r, - 'g': color.g, - 'b': color.b, - } - : null; + return color != null ? {'a': color.a, 'r': color.r, 'g': color.g, 'b': color.b} : null; } } diff --git a/lib/src/view/puzzle/puzzle_tab_screen.dart b/lib/src/view/puzzle/puzzle_tab_screen.dart index 189752b604..ed6b65764c 100644 --- a/lib/src/view/puzzle/puzzle_tab_screen.dart +++ b/lib/src/view/puzzle/puzzle_tab_screen.dart @@ -369,10 +369,7 @@ class _PuzzleMenuListTile extends StatelessWidget { Theme.of(context).platform == TargetPlatform.iOS ? const EdgeInsets.symmetric(vertical: 10.0, horizontal: 14.0) : null, - leading: Icon( - icon, - size: Styles.mainListTileIconSize, - ), + leading: Icon(icon, size: Styles.mainListTileIconSize), title: Text(title, style: Styles.mainListTileTitle), subtitle: Text(subtitle, maxLines: 3), trailing: diff --git a/lib/src/view/settings/account_preferences_screen.dart b/lib/src/view/settings/account_preferences_screen.dart index bb77c2a493..10031c8be6 100644 --- a/lib/src/view/settings/account_preferences_screen.dart +++ b/lib/src/view/settings/account_preferences_screen.dart @@ -113,10 +113,7 @@ class _AccountPreferencesScreenState extends ConsumerState t != BoardTheme.system || hasSystemColors, - ) - .toList(); + final choices = + BoardTheme.values.where((t) => t != BoardTheme.system || hasSystemColors).toList(); void onChanged(BoardTheme? value) => ref.read(boardPreferencesProvider.notifier).setBoardTheme(value ?? BoardTheme.brown); diff --git a/lib/src/view/settings/settings_tab_screen.dart b/lib/src/view/settings/settings_tab_screen.dart index 091fecab8b..c279dae0ca 100644 --- a/lib/src/view/settings/settings_tab_screen.dart +++ b/lib/src/view/settings/settings_tab_screen.dart @@ -207,13 +207,11 @@ class _Body extends ConsumerWidget { context, choices: BackgroundThemeMode.values, selectedItem: generalPrefs.themeMode, - labelBuilder: (t) => - Text(AppBackgroundModeScreen.themeTitle(context, t)), - onSelectedItemChanged: (BackgroundThemeMode? value) => ref - .read(generalPreferencesProvider.notifier) - .setBackgroundThemeMode( - value ?? BackgroundThemeMode.system, - ), + labelBuilder: (t) => Text(AppBackgroundModeScreen.themeTitle(context, t)), + onSelectedItemChanged: + (BackgroundThemeMode? value) => ref + .read(generalPreferencesProvider.notifier) + .setBackgroundThemeMode(value ?? BackgroundThemeMode.system), ); } else { pushPlatformRoute( diff --git a/lib/src/view/settings/theme_screen.dart b/lib/src/view/settings/theme_screen.dart index 90c8d500f4..03021beae7 100644 --- a/lib/src/view/settings/theme_screen.dart +++ b/lib/src/view/settings/theme_screen.dart @@ -87,9 +87,10 @@ class _Body extends ConsumerWidget { children: [ SwitchSettingTile( leading: const Icon(Icons.colorize_outlined), - padding: Theme.of(context).platform == TargetPlatform.iOS - ? const EdgeInsets.symmetric(horizontal: 14, vertical: 8) - : null, + padding: + Theme.of(context).platform == TargetPlatform.iOS + ? const EdgeInsets.symmetric(horizontal: 14, vertical: 8) + : null, title: const Text('Custom theme'), // TODO translate subtitle: const Text( @@ -98,16 +99,15 @@ class _Body extends ConsumerWidget { ), value: generalPrefs.customThemeEnabled, onChanged: (value) { - ref - .read(generalPreferencesProvider.notifier) - .toggleCustomTheme(); + ref.read(generalPreferencesProvider.notifier).toggleCustomTheme(); }, ), AnimatedCrossFade( duration: const Duration(milliseconds: 300), - crossFadeState: generalPrefs.customThemeEnabled - ? CrossFadeState.showSecond - : CrossFadeState.showFirst, + crossFadeState: + generalPrefs.customThemeEnabled + ? CrossFadeState.showSecond + : CrossFadeState.showFirst, firstChild: const SizedBox.shrink(), secondChild: ListSection( margin: EdgeInsets.zero, @@ -117,36 +117,35 @@ class _Body extends ConsumerWidget { PlatformListTile( leading: const Icon(Icons.color_lens), title: const Text('Seed color'), - trailing: generalPrefs.customThemeSeed != null - ? Container( - width: 20, - height: 20, - decoration: BoxDecoration( - color: generalPrefs.customThemeSeed, - shape: BoxShape.circle, - ), - ) - : getCorePalette() != null + trailing: + generalPrefs.customThemeSeed != null + ? Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: generalPrefs.customThemeSeed, + shape: BoxShape.circle, + ), + ) + : getCorePalette() != null ? Text(context.l10n.mobileSystemColors) : Container( - width: 20, - height: 20, - decoration: BoxDecoration( - color: LichessColors.primary[500], - shape: BoxShape.circle, - ), + width: 20, + height: 20, + decoration: BoxDecoration( + color: LichessColors.primary[500], + shape: BoxShape.circle, ), + ), onTap: () { showAdaptiveDialog( context: context, barrierDismissible: false, builder: (context) { - final defaultColor = getCorePalettePrimary() ?? - LichessColors.primary[500]!; - bool useDefault = - generalPrefs.customThemeSeed == null; - Color color = - generalPrefs.customThemeSeed ?? defaultColor; + final defaultColor = + getCorePalettePrimary() ?? LichessColors.primary[500]!; + bool useDefault = generalPrefs.customThemeSeed == null; + Color color = generalPrefs.customThemeSeed ?? defaultColor; return StatefulBuilder( builder: (context, setState) { return PlatformAlertDialog( @@ -165,17 +164,19 @@ class _Body extends ConsumerWidget { }, ), SecondaryButton( - semanticsLabel: getCorePalette() != null - ? context.l10n.mobileSystemColors - : 'Default color', - onPressed: !useDefault - ? () { - setState(() { - useDefault = true; - color = defaultColor; - }); - } - : null, + semanticsLabel: + getCorePalette() != null + ? context.l10n.mobileSystemColors + : 'Default color', + onPressed: + !useDefault + ? () { + setState(() { + useDefault = true; + color = defaultColor; + }); + } + : null, child: Text( getCorePalette() != null ? context.l10n.mobileSystemColors From 856dd001f9678959cc54e78dbd9f8f7f7cf02357 Mon Sep 17 00:00:00 2001 From: Vincent Velociter Date: Mon, 16 Dec 2024 15:22:30 +0100 Subject: [PATCH 03/17] Board adjustments --- lib/src/model/settings/board_preferences.dart | 19 ++ lib/src/view/settings/theme_screen.dart | 232 ++++++++++++------ lib/src/widgets/board_carousel_item.dart | 4 +- lib/src/widgets/board_preview.dart | 4 +- lib/src/widgets/board_thumbnail.dart | 8 +- pubspec.lock | 9 +- pubspec.yaml | 3 +- 7 files changed, 183 insertions(+), 96 deletions(-) diff --git a/lib/src/model/settings/board_preferences.dart b/lib/src/model/settings/board_preferences.dart index 65dd2b3f1b..84eefa4241 100644 --- a/lib/src/model/settings/board_preferences.dart +++ b/lib/src/model/settings/board_preferences.dart @@ -94,12 +94,23 @@ class BoardPreferences extends _$BoardPreferences with PreferencesStorage setShapeColor(ShapeColor shapeColor) { return save(state.copyWith(shapeColor: shapeColor)); } + + Future setBrightness(double brightness) { + return save(state.copyWith(brightness: brightness)); + } + + Future setHue(double hue) { + return save(state.copyWith(hue: hue)); + } } @Freezed(fromJson: true, toJson: true) class BoardPrefs with _$BoardPrefs implements Serializable { const BoardPrefs._(); + @Assert( + 'brightness == null || brightness >= -0.5 && brightness <= 0.5, hue == null || hue >= -1 && hue <= 1', + ) const factory BoardPrefs({ required PieceSet pieceSet, required BoardTheme boardTheme, @@ -126,6 +137,8 @@ class BoardPrefs with _$BoardPrefs implements Serializable { @JsonKey(defaultValue: ShapeColor.green, unknownEnumValue: ShapeColor.green) required ShapeColor shapeColor, @JsonKey(defaultValue: false) required bool showBorder, + @JsonKey(defaultValue: 0.0) required double brightness, + @JsonKey(defaultValue: 0.0) required double hue, }) = _BoardPrefs; static const defaults = BoardPrefs( @@ -145,12 +158,18 @@ class BoardPrefs with _$BoardPrefs implements Serializable { dragTargetKind: DragTargetKind.circle, shapeColor: ShapeColor.green, showBorder: false, + brightness: 0.0, + hue: 0.0, ); + bool get hasColorAdjustments => brightness != 0.0 || hue != 0.0; + ChessboardSettings toBoardSettings() { return ChessboardSettings( pieceAssets: pieceSet.assets, colorScheme: boardTheme.colors, + brightness: brightness, + hue: hue, border: showBorder ? BoardBorder(color: darken(boardTheme.colors.darkSquare, 0.2), width: 16.0) diff --git a/lib/src/view/settings/theme_screen.dart b/lib/src/view/settings/theme_screen.dart index 03021beae7..eb601d2a55 100644 --- a/lib/src/view/settings/theme_screen.dart +++ b/lib/src/view/settings/theme_screen.dart @@ -40,9 +40,25 @@ switch (shapeColor) { ShapeColor.yellow => 'Yellow', }; -class _Body extends ConsumerWidget { +class _Body extends ConsumerStatefulWidget { @override - Widget build(BuildContext context, WidgetRef ref) { + ConsumerState<_Body> createState() => _BodyState(); +} + +class _BodyState extends ConsumerState<_Body> { + late double brightness; + late double hue; + + @override + void initState() { + super.initState(); + final boardPrefs = ref.read(boardPreferencesProvider); + brightness = boardPrefs.brightness; + hue = boardPrefs.hue; + } + + @override + Widget build(BuildContext context) { final generalPrefs = ref.watch(generalPreferencesProvider); final boardPrefs = ref.watch(boardPreferencesProvider); @@ -74,6 +90,8 @@ class _Body extends ConsumerWidget { ), }.lock, settings: boardPrefs.toBoardSettings().copyWith( + brightness: brightness, + hue: hue, borderRadius: const BorderRadius.all(Radius.circular(4.0)), boxShadow: boardShadows, ), @@ -82,6 +100,136 @@ class _Body extends ConsumerWidget { ); }, ), + ListSection( + hasLeading: true, + children: [ + SettingsListTile( + icon: const Icon(LichessIcons.chess_board), + settingsLabel: Text(context.l10n.board), + settingsValue: boardPrefs.boardTheme.label, + onTap: () { + pushPlatformRoute( + context, + title: context.l10n.board, + builder: (context) => const BoardThemeScreen(), + ); + }, + ), + PlatformListTile( + leading: const Icon(Icons.brightness_6), + title: Slider.adaptive( + min: -0.5, + max: 0.5, + value: brightness, + onChanged: (value) { + setState(() { + brightness = value; + }); + }, + onChangeEnd: (value) { + ref.read(boardPreferencesProvider.notifier).setBrightness(brightness); + }, + ), + ), + PlatformListTile( + leading: const Icon(Icons.invert_colors), + title: Slider.adaptive( + min: -1.0, + max: 1.0, + value: hue, + onChanged: (value) { + setState(() { + hue = value; + }); + }, + onChangeEnd: (value) { + ref.read(boardPreferencesProvider.notifier).setHue(hue); + }, + ), + ), + AnimatedCrossFade( + duration: const Duration(milliseconds: 300), + crossFadeState: + brightness != 0.0 || hue != 0.0 + ? CrossFadeState.showSecond + : CrossFadeState.showFirst, + firstChild: const SizedBox.shrink(), + secondChild: PlatformListTile( + leading: const Icon(Icons.cancel), + title: Text(context.l10n.boardReset), + onTap: () { + setState(() { + brightness = 0.0; + hue = 0.0; + }); + ref.read(boardPreferencesProvider.notifier).setBrightness(0.0); + ref.read(boardPreferencesProvider.notifier).setHue(0.0); + }, + ), + ), + ], + ), + ListSection( + hasLeading: true, + children: [ + SettingsListTile( + icon: const Icon(LichessIcons.chess_pawn), + settingsLabel: Text(context.l10n.pieceSet), + settingsValue: boardPrefs.pieceSet.label, + onTap: () { + pushPlatformRoute( + context, + title: context.l10n.pieceSet, + builder: (context) => const PieceSetScreen(), + ); + }, + ), + SettingsListTile( + icon: const Icon(LichessIcons.arrow_full_upperright), + settingsLabel: const Text('Shape color'), + settingsValue: shapeColorL10n(context, boardPrefs.shapeColor), + onTap: () { + showChoicePicker( + context, + choices: ShapeColor.values, + selectedItem: boardPrefs.shapeColor, + labelBuilder: + (t) => Text.rich( + TextSpan( + children: [ + TextSpan(text: shapeColorL10n(context, t)), + const TextSpan(text: ' '), + WidgetSpan(child: Container(width: 15, height: 15, color: t.color)), + ], + ), + ), + onSelectedItemChanged: (ShapeColor? value) { + ref + .read(boardPreferencesProvider.notifier) + .setShapeColor(value ?? ShapeColor.green); + }, + ); + }, + ), + SwitchSettingTile( + leading: const Icon(Icons.location_on), + title: Text(context.l10n.preferencesBoardCoordinates), + value: boardPrefs.coordinates, + onChanged: (value) { + ref.read(boardPreferencesProvider.notifier).toggleCoordinates(); + }, + ), + SwitchSettingTile( + // TODO translate + leading: const Icon(Icons.border_outer), + title: const Text('Show border'), + value: boardPrefs.showBorder, + onChanged: (value) { + ref.read(boardPreferencesProvider.notifier).toggleBorder(); + }, + ), + ], + ), ListSection( hasLeading: true, children: [ @@ -91,12 +239,9 @@ class _Body extends ConsumerWidget { Theme.of(context).platform == TargetPlatform.iOS ? const EdgeInsets.symmetric(horizontal: 14, vertical: 8) : null, - title: const Text('Custom theme'), + title: const Text('App theme'), // TODO translate - subtitle: const Text( - 'Configure your own app theme using a seed color. Disable to use the chessboard theme.', - maxLines: 3, - ), + subtitle: const Text('Configure your own app theme using a seed color.', maxLines: 3), value: generalPrefs.customThemeEnabled, onChanged: (value) { ref.read(generalPreferencesProvider.notifier).toggleCustomTheme(); @@ -222,79 +367,6 @@ class _Body extends ConsumerWidget { ), ], ), - ListSection( - hasLeading: true, - children: [ - SettingsListTile( - icon: const Icon(LichessIcons.chess_board), - settingsLabel: Text(context.l10n.board), - settingsValue: boardPrefs.boardTheme.label, - onTap: () { - pushPlatformRoute( - context, - title: context.l10n.board, - builder: (context) => const BoardThemeScreen(), - ); - }, - ), - SettingsListTile( - icon: const Icon(LichessIcons.chess_pawn), - settingsLabel: Text(context.l10n.pieceSet), - settingsValue: boardPrefs.pieceSet.label, - onTap: () { - pushPlatformRoute( - context, - title: context.l10n.pieceSet, - builder: (context) => const PieceSetScreen(), - ); - }, - ), - SettingsListTile( - icon: const Icon(LichessIcons.arrow_full_upperright), - settingsLabel: const Text('Shape color'), - settingsValue: shapeColorL10n(context, boardPrefs.shapeColor), - onTap: () { - showChoicePicker( - context, - choices: ShapeColor.values, - selectedItem: boardPrefs.shapeColor, - labelBuilder: - (t) => Text.rich( - TextSpan( - children: [ - TextSpan(text: shapeColorL10n(context, t)), - const TextSpan(text: ' '), - WidgetSpan(child: Container(width: 15, height: 15, color: t.color)), - ], - ), - ), - onSelectedItemChanged: (ShapeColor? value) { - ref - .read(boardPreferencesProvider.notifier) - .setShapeColor(value ?? ShapeColor.green); - }, - ); - }, - ), - SwitchSettingTile( - leading: const Icon(Icons.location_on), - title: Text(context.l10n.preferencesBoardCoordinates), - value: boardPrefs.coordinates, - onChanged: (value) { - ref.read(boardPreferencesProvider.notifier).toggleCoordinates(); - }, - ), - SwitchSettingTile( - // TODO translate - leading: const Icon(Icons.border_outer), - title: const Text('Show border'), - value: boardPrefs.showBorder, - onChanged: (value) { - ref.read(boardPreferencesProvider.notifier).toggleBorder(); - }, - ), - ], - ), ], ); } diff --git a/lib/src/widgets/board_carousel_item.dart b/lib/src/widgets/board_carousel_item.dart index a6e12f65c1..90d50c71ba 100644 --- a/lib/src/widgets/board_carousel_item.dart +++ b/lib/src/widgets/board_carousel_item.dart @@ -84,14 +84,12 @@ class BoardCarouselItem extends ConsumerWidget { fen: fen, orientation: orientation, lastMove: lastMove, - settings: ChessboardSettings( + settings: boardPrefs.toBoardSettings().copyWith( enableCoordinates: false, borderRadius: const BorderRadius.only( topLeft: Radius.circular(10.0), topRight: Radius.circular(10.0), ), - pieceAssets: boardPrefs.pieceSet.assets, - colorScheme: boardPrefs.boardTheme.colors, ), ), ), diff --git a/lib/src/widgets/board_preview.dart b/lib/src/widgets/board_preview.dart index 531bc0a6d9..91846aca68 100644 --- a/lib/src/widgets/board_preview.dart +++ b/lib/src/widgets/board_preview.dart @@ -88,13 +88,11 @@ class _SmallBoardPreviewState extends ConsumerState { fen: widget.fen, orientation: widget.orientation, lastMove: widget.lastMove as NormalMove?, - settings: ChessboardSettings( + settings: boardPrefs.toBoardSettings().copyWith( enableCoordinates: false, borderRadius: const BorderRadius.all(Radius.circular(4.0)), boxShadow: boardShadows, animationDuration: const Duration(milliseconds: 150), - pieceAssets: boardPrefs.pieceSet.assets, - colorScheme: boardPrefs.boardTheme.colors, ), ), const SizedBox(width: 10.0), diff --git a/lib/src/widgets/board_thumbnail.dart b/lib/src/widgets/board_thumbnail.dart index 0607cad0dc..3f23c5b844 100644 --- a/lib/src/widgets/board_thumbnail.dart +++ b/lib/src/widgets/board_thumbnail.dart @@ -76,13 +76,11 @@ class _BoardThumbnailState extends ConsumerState { fen: widget.fen, orientation: widget.orientation, lastMove: widget.lastMove as NormalMove?, - settings: ChessboardSettings( + settings: boardPrefs.toBoardSettings().copyWith( enableCoordinates: false, borderRadius: const BorderRadius.all(Radius.circular(4.0)), boxShadow: boardShadows, - animationDuration: widget.animationDuration!, - pieceAssets: boardPrefs.pieceSet.assets, - colorScheme: boardPrefs.boardTheme.colors, + animationDuration: widget.animationDuration, ), ) : StaticChessboard( @@ -95,6 +93,8 @@ class _BoardThumbnailState extends ConsumerState { boxShadow: boardShadows, pieceAssets: boardPrefs.pieceSet.assets, colorScheme: boardPrefs.boardTheme.colors, + brightness: boardPrefs.brightness, + hue: boardPrefs.hue, ); final maybeTappableBoard = diff --git a/pubspec.lock b/pubspec.lock index accc6a41dd..45fb76ab74 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -201,11 +201,10 @@ packages: chessground: dependency: "direct main" description: - name: chessground - sha256: "118e11871baa08022be827087bc90b82f0bda535d504278787f9717ad949132b" - url: "https://pub.dev" - source: hosted - version: "6.1.0" + path: "../flutter-chessground" + relative: true + source: path + version: "6.2.0" ci: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index bdcc0193d9..8600a41706 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,7 +14,8 @@ dependencies: async: ^2.10.0 auto_size_text: ^3.0.0 cached_network_image: ^3.2.2 - chessground: ^6.1.0 + chessground: + path: ../flutter-chessground clock: ^1.1.1 collection: ^1.17.0 connectivity_plus: ^6.0.2 From 163604a80133d5c68eecb5e2798a7916e75bb290 Mon Sep 17 00:00:00 2001 From: Vincent Velociter Date: Mon, 16 Dec 2024 15:46:27 +0100 Subject: [PATCH 04/17] Fixes --- lib/src/model/settings/board_preferences.dart | 8 +- .../view/settings/settings_tab_screen.dart | 11 +- lib/src/view/settings/theme_screen.dart | 76 +++++++------ lib/src/widgets/board_carousel_item.dart | 101 ++++++++++-------- lib/src/widgets/change_colors.dart | 81 ++++++++++++++ 5 files changed, 185 insertions(+), 92 deletions(-) create mode 100644 lib/src/widgets/change_colors.dart diff --git a/lib/src/model/settings/board_preferences.dart b/lib/src/model/settings/board_preferences.dart index 84eefa4241..402e659789 100644 --- a/lib/src/model/settings/board_preferences.dart +++ b/lib/src/model/settings/board_preferences.dart @@ -95,12 +95,8 @@ class BoardPreferences extends _$BoardPreferences with PreferencesStorage setBrightness(double brightness) { - return save(state.copyWith(brightness: brightness)); - } - - Future setHue(double hue) { - return save(state.copyWith(hue: hue)); + Future adjustColors({double? brightness, double? hue}) { + return save(state.copyWith(brightness: brightness ?? state.brightness, hue: hue ?? state.hue)); } } diff --git a/lib/src/view/settings/settings_tab_screen.dart b/lib/src/view/settings/settings_tab_screen.dart index c279dae0ca..687952a257 100644 --- a/lib/src/view/settings/settings_tab_screen.dart +++ b/lib/src/view/settings/settings_tab_screen.dart @@ -222,10 +222,13 @@ class _Body extends ConsumerWidget { } }, ), - SettingsListTile( - icon: const Icon(Icons.palette_outlined), - settingsLabel: Text(context.l10n.mobileTheme), - settingsValue: '${boardPrefs.boardTheme.label} / ${boardPrefs.pieceSet.label}', + PlatformListTile( + leading: const Icon(Icons.palette_outlined), + title: Text(context.l10n.mobileTheme), + trailing: + Theme.of(context).platform == TargetPlatform.iOS + ? const CupertinoListTileChevron() + : null, onTap: () { pushPlatformRoute(context, title: 'Theme', builder: (context) => const ThemeScreen()); }, diff --git a/lib/src/view/settings/theme_screen.dart b/lib/src/view/settings/theme_screen.dart index eb601d2a55..e001bdcc25 100644 --- a/lib/src/view/settings/theme_screen.dart +++ b/lib/src/view/settings/theme_screen.dart @@ -17,6 +17,7 @@ import 'package:lichess_mobile/src/view/settings/board_theme_screen.dart'; import 'package:lichess_mobile/src/view/settings/piece_set_screen.dart'; import 'package:lichess_mobile/src/widgets/adaptive_choice_picker.dart'; import 'package:lichess_mobile/src/widgets/buttons.dart'; +import 'package:lichess_mobile/src/widgets/change_colors.dart'; import 'package:lichess_mobile/src/widgets/list.dart'; import 'package:lichess_mobile/src/widgets/platform_alert_dialog.dart'; import 'package:lichess_mobile/src/widgets/platform_scaffold.dart'; @@ -75,25 +76,29 @@ class _BodyState extends ConsumerState<_Body> { return Padding( padding: const EdgeInsets.symmetric(horizontal: horizontalPadding, vertical: 16), child: Center( - child: Chessboard.fixed( - size: boardSize, - orientation: Side.white, - lastMove: const NormalMove(from: Square.e2, to: Square.e4), - fen: 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1', - shapes: - { - Circle(color: boardPrefs.shapeColor.color, orig: Square.fromName('b8')), - Arrow( - color: boardPrefs.shapeColor.color, - orig: Square.fromName('b8'), - dest: Square.fromName('c6'), - ), - }.lock, - settings: boardPrefs.toBoardSettings().copyWith( - brightness: brightness, - hue: hue, - borderRadius: const BorderRadius.all(Radius.circular(4.0)), - boxShadow: boardShadows, + child: ChangeColors( + brightness: brightness, + hue: hue, + child: Chessboard.fixed( + size: boardSize, + orientation: Side.white, + lastMove: const NormalMove(from: Square.e2, to: Square.e4), + fen: 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1', + shapes: + { + Circle(color: boardPrefs.shapeColor.color, orig: Square.fromName('b8')), + Arrow( + color: boardPrefs.shapeColor.color, + orig: Square.fromName('b8'), + dest: Square.fromName('c6'), + ), + }.lock, + settings: boardPrefs.toBoardSettings().copyWith( + brightness: 0.0, + hue: 0.0, + borderRadius: const BorderRadius.all(Radius.circular(4.0)), + boxShadow: boardShadows, + ), ), ), ), @@ -103,18 +108,6 @@ class _BodyState extends ConsumerState<_Body> { ListSection( hasLeading: true, children: [ - SettingsListTile( - icon: const Icon(LichessIcons.chess_board), - settingsLabel: Text(context.l10n.board), - settingsValue: boardPrefs.boardTheme.label, - onTap: () { - pushPlatformRoute( - context, - title: context.l10n.board, - builder: (context) => const BoardThemeScreen(), - ); - }, - ), PlatformListTile( leading: const Icon(Icons.brightness_6), title: Slider.adaptive( @@ -127,7 +120,7 @@ class _BodyState extends ConsumerState<_Body> { }); }, onChangeEnd: (value) { - ref.read(boardPreferencesProvider.notifier).setBrightness(brightness); + ref.read(boardPreferencesProvider.notifier).adjustColors(brightness: brightness); }, ), ), @@ -143,7 +136,7 @@ class _BodyState extends ConsumerState<_Body> { }); }, onChangeEnd: (value) { - ref.read(boardPreferencesProvider.notifier).setHue(hue); + ref.read(boardPreferencesProvider.notifier).adjustColors(hue: hue); }, ), ), @@ -162,8 +155,9 @@ class _BodyState extends ConsumerState<_Body> { brightness = 0.0; hue = 0.0; }); - ref.read(boardPreferencesProvider.notifier).setBrightness(0.0); - ref.read(boardPreferencesProvider.notifier).setHue(0.0); + ref + .read(boardPreferencesProvider.notifier) + .adjustColors(brightness: 0.0, hue: 0.0); }, ), ), @@ -172,6 +166,18 @@ class _BodyState extends ConsumerState<_Body> { ListSection( hasLeading: true, children: [ + SettingsListTile( + icon: const Icon(LichessIcons.chess_board), + settingsLabel: Text(context.l10n.board), + settingsValue: boardPrefs.boardTheme.label, + onTap: () { + pushPlatformRoute( + context, + title: context.l10n.board, + builder: (context) => const BoardThemeScreen(), + ); + }, + ), SettingsListTile( icon: const Icon(LichessIcons.chess_pawn), settingsLabel: Text(context.l10n.pieceSet), diff --git a/lib/src/widgets/board_carousel_item.dart b/lib/src/widgets/board_carousel_item.dart index 90d50c71ba..57fecd5e64 100644 --- a/lib/src/widgets/board_carousel_item.dart +++ b/lib/src/widgets/board_carousel_item.dart @@ -7,6 +7,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:lichess_mobile/src/model/settings/board_preferences.dart'; import 'package:lichess_mobile/src/styles/styles.dart'; import 'package:lichess_mobile/src/widgets/buttons.dart'; +import 'package:lichess_mobile/src/widgets/change_colors.dart'; import 'package:lichess_mobile/src/widgets/platform.dart'; const _kBoardCarouselItemMargin = EdgeInsets.symmetric(vertical: 8.0, horizontal: 6.0); @@ -51,58 +52,64 @@ class BoardCarouselItem extends ConsumerWidget { return LayoutBuilder( builder: (context, constraints) { final boardSize = constraints.biggest.shortestSide - _kBoardCarouselItemMargin.horizontal; - final card = PlatformCard( - color: backgroundColor, - margin: - Theme.of(context).platform == TargetPlatform.iOS - ? EdgeInsets.zero - : _kBoardCarouselItemMargin, - child: AdaptiveInkWell( - splashColor: splashColor, - borderRadius: BorderRadius.circular(10), - onTap: onTap, - child: Stack( - children: [ - ShaderMask( - blendMode: BlendMode.dstOut, - shaderCallback: (bounds) { - return LinearGradient( - begin: Alignment.center, - end: Alignment.bottomCenter, - colors: [ - backgroundColor.withValues(alpha: 0.25), - backgroundColor.withValues(alpha: 1.0), - ], - stops: const [0.3, 1.00], - tileMode: TileMode.clamp, - ).createShader(bounds); - }, - child: SizedBox( - height: boardSize, - child: Chessboard.fixed( - size: boardSize, - fen: fen, - orientation: orientation, - lastMove: lastMove, - settings: boardPrefs.toBoardSettings().copyWith( - enableCoordinates: false, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(10.0), - topRight: Radius.circular(10.0), + final card = ChangeColors( + brightness: boardPrefs.brightness, + hue: boardPrefs.hue, + child: PlatformCard( + color: backgroundColor, + margin: + Theme.of(context).platform == TargetPlatform.iOS + ? EdgeInsets.zero + : _kBoardCarouselItemMargin, + child: AdaptiveInkWell( + splashColor: splashColor, + borderRadius: BorderRadius.circular(10), + onTap: onTap, + child: Stack( + children: [ + ShaderMask( + blendMode: BlendMode.dstOut, + shaderCallback: (bounds) { + return LinearGradient( + begin: Alignment.center, + end: Alignment.bottomCenter, + colors: [ + backgroundColor.withValues(alpha: 0.25), + backgroundColor.withValues(alpha: 1.0), + ], + stops: const [0.3, 1.00], + tileMode: TileMode.clamp, + ).createShader(bounds); + }, + child: SizedBox( + height: boardSize, + child: Chessboard.fixed( + size: boardSize, + fen: fen, + orientation: orientation, + lastMove: lastMove, + settings: boardPrefs.toBoardSettings().copyWith( + brightness: 0.0, + hue: 0.0, + enableCoordinates: false, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(10.0), + topRight: Radius.circular(10.0), + ), ), ), ), ), - ), - Positioned( - left: 0, - bottom: 8, - child: DefaultTextStyle.merge( - style: const TextStyle(color: Colors.white), - child: description, + Positioned( + left: 0, + bottom: 8, + child: DefaultTextStyle.merge( + style: const TextStyle(color: Colors.white), + child: description, + ), ), - ), - ], + ], + ), ), ), ); diff --git a/lib/src/widgets/change_colors.dart b/lib/src/widgets/change_colors.dart new file mode 100644 index 0000000000..38ce7eebe3 --- /dev/null +++ b/lib/src/widgets/change_colors.dart @@ -0,0 +1,81 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; + +// Based upon: https://stackoverflow.com/questions/64639589/how-to-adjust-hue-saturation-and-brightness-of-an-image-in-flutter +// from BananaNeil: https://stackoverflow.com/users/937841/banananeil. +// This is, in turn, based upon: https://stackoverflow.com/a/7917978/937841 + +/// Use the [ChangeColors] widget to change the brightness and hue of any widget, including images. +/// +/// Example: +/// +/// ``` +/// ChangeColors( +/// hue: 0.55, +/// brightness: 0.2, +/// child: Image.asset('myImage.png'), +/// ); +/// ``` +/// +/// To achieve a greyscale effect, you may also use the +/// [ChangeColors.greyscale] constructor. +/// +class ChangeColors extends StatelessWidget { + const ChangeColors({this.brightness = 0.0, this.hue = 0.0, required this.child, super.key}); + + /// Negative value will make it darker (-1 is darkest). + /// Positive value will make it lighter (1 is the maximum, but you can go above it). + /// Note: 0.0 is unchanged. + final double brightness; + + /// From -1.0 to 1.0 (Note: 1.0 wraps into -1.0, such as 1.2 is the same as -0.8). + /// Note: 0.0 is unchanged. Adding or subtracting multiples of 2.0 also keeps it unchanged. + final double hue; + + final Widget child; + + @override + Widget build(BuildContext context) { + return ColorFiltered( + colorFilter: ColorFilter.matrix(_adjustMatrix(hue: hue * pi, brightness: brightness)), + child: child, + ); + } +} + +List _adjustMatrix({required double hue, required double brightness}) { + if (hue == 0 && brightness == 0) { + // dart format off + return [ + 1, 0, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 1, 0, + ]; + // dart format on + } + final brightnessValue = brightness <= 0 ? brightness * 255 : brightness * 100; + return List.from([ + 0.213 + cos(hue) * 0.787 + sin(hue) * -0.213, + 0.715 + cos(hue) * -0.715 + sin(hue) * -0.715, + 0.072 + cos(hue) * -0.072 + sin(hue) * 0.928, + 0, + brightnessValue, + 0.213 + cos(hue) * -0.213 + sin(hue) * 0.143, + 0.715 + cos(hue) * 0.285 + sin(hue) * 0.140, + 0.072 + cos(hue) * -0.072 + sin(hue) * -0.283, + 0, + brightnessValue, + 0.213 + cos(hue) * -0.213 + sin(hue) * -0.787, + 0.715 + cos(hue) * -0.715 + sin(hue) * 0.715, + 0.072 + cos(hue) * 0.928 + sin(hue) * 0.072, + 0, + brightnessValue, + 0, + 0, + 0, + 1, + 0, + ]).map((i) => i).toList(); +} From 1bfe9ab947539cd389f9f5ef6b60b58a19e99403 Mon Sep 17 00:00:00 2001 From: Vincent Velociter Date: Mon, 16 Dec 2024 18:57:27 +0100 Subject: [PATCH 05/17] More fixes --- .../model/settings/general_preferences.dart | 10 +---- lib/src/view/settings/theme_screen.dart | 41 ++++++++++--------- lib/src/widgets/board_carousel_item.dart | 8 ++-- lib/src/widgets/board_preview.dart | 6 ++- lib/src/widgets/board_thumbnail.dart | 10 +++-- 5 files changed, 39 insertions(+), 36 deletions(-) diff --git a/lib/src/model/settings/general_preferences.dart b/lib/src/model/settings/general_preferences.dart index b61dfc3b77..f788e60dd5 100644 --- a/lib/src/model/settings/general_preferences.dart +++ b/lib/src/model/settings/general_preferences.dart @@ -49,14 +49,6 @@ class GeneralPreferences extends _$GeneralPreferences with PreferencesStorage toggleCustomTheme() async { await save(state.copyWith(customThemeEnabled: !state.customThemeEnabled)); - if (state.customThemeEnabled == false) { - final boardTheme = ref.read(boardPreferencesProvider).boardTheme; - if (boardTheme == BoardTheme.system) { - await ref.read(boardPreferencesProvider.notifier).setBoardTheme(BoardTheme.brown); - } - } else { - await ref.read(boardPreferencesProvider.notifier).setBoardTheme(BoardTheme.system); - } } Future setCustomThemeSeed(Color? color) { @@ -88,7 +80,7 @@ class GeneralPrefs with _$GeneralPrefs implements Serializable { isSoundEnabled: true, soundTheme: SoundTheme.standard, masterVolume: 0.8, - customThemeEnabled: true, + customThemeEnabled: false, ); factory GeneralPrefs.fromJson(Map json) { diff --git a/lib/src/view/settings/theme_screen.dart b/lib/src/view/settings/theme_screen.dart index e001bdcc25..10d58c8748 100644 --- a/lib/src/view/settings/theme_screen.dart +++ b/lib/src/view/settings/theme_screen.dart @@ -65,6 +65,8 @@ class _BodyState extends ConsumerState<_Body> { const horizontalPadding = 16.0; + final bool hasAjustedColors = brightness != 0.0 || hue != 0.0; + return ListView( children: [ LayoutBuilder( @@ -140,26 +142,27 @@ class _BodyState extends ConsumerState<_Body> { }, ), ), - AnimatedCrossFade( - duration: const Duration(milliseconds: 300), - crossFadeState: - brightness != 0.0 || hue != 0.0 - ? CrossFadeState.showSecond - : CrossFadeState.showFirst, - firstChild: const SizedBox.shrink(), - secondChild: PlatformListTile( - leading: const Icon(Icons.cancel), - title: Text(context.l10n.boardReset), - onTap: () { - setState(() { - brightness = 0.0; - hue = 0.0; - }); - ref - .read(boardPreferencesProvider.notifier) - .adjustColors(brightness: 0.0, hue: 0.0); - }, + PlatformListTile( + leading: Opacity( + opacity: hasAjustedColors ? 1.0 : 0.5, + child: const Icon(Icons.cancel), + ), + title: Opacity( + opacity: hasAjustedColors ? 1.0 : 0.5, + child: Text(context.l10n.boardReset), ), + onTap: + hasAjustedColors + ? () { + setState(() { + brightness = 0.0; + hue = 0.0; + }); + ref + .read(boardPreferencesProvider.notifier) + .adjustColors(brightness: 0.0, hue: 0.0); + } + : null, ), ], ), diff --git a/lib/src/widgets/board_carousel_item.dart b/lib/src/widgets/board_carousel_item.dart index 57fecd5e64..2559c8398f 100644 --- a/lib/src/widgets/board_carousel_item.dart +++ b/lib/src/widgets/board_carousel_item.dart @@ -53,8 +53,8 @@ class BoardCarouselItem extends ConsumerWidget { builder: (context, constraints) { final boardSize = constraints.biggest.shortestSide - _kBoardCarouselItemMargin.horizontal; final card = ChangeColors( - brightness: boardPrefs.brightness, hue: boardPrefs.hue, + brightness: boardPrefs.brightness, child: PlatformCard( color: backgroundColor, margin: @@ -88,14 +88,14 @@ class BoardCarouselItem extends ConsumerWidget { fen: fen, orientation: orientation, lastMove: lastMove, - settings: boardPrefs.toBoardSettings().copyWith( - brightness: 0.0, - hue: 0.0, + settings: ChessboardSettings( enableCoordinates: false, borderRadius: const BorderRadius.only( topLeft: Radius.circular(10.0), topRight: Radius.circular(10.0), ), + pieceAssets: boardPrefs.pieceSet.assets, + colorScheme: boardPrefs.boardTheme.colors, ), ), ), diff --git a/lib/src/widgets/board_preview.dart b/lib/src/widgets/board_preview.dart index 91846aca68..0f74802a2a 100644 --- a/lib/src/widgets/board_preview.dart +++ b/lib/src/widgets/board_preview.dart @@ -88,7 +88,11 @@ class _SmallBoardPreviewState extends ConsumerState { fen: widget.fen, orientation: widget.orientation, lastMove: widget.lastMove as NormalMove?, - settings: boardPrefs.toBoardSettings().copyWith( + settings: ChessboardSettings( + pieceAssets: boardPrefs.pieceSet.assets, + colorScheme: boardPrefs.boardTheme.colors, + brightness: boardPrefs.brightness, + hue: boardPrefs.hue, enableCoordinates: false, borderRadius: const BorderRadius.all(Radius.circular(4.0)), boxShadow: boardShadows, diff --git a/lib/src/widgets/board_thumbnail.dart b/lib/src/widgets/board_thumbnail.dart index 3f23c5b844..c13fa9e49f 100644 --- a/lib/src/widgets/board_thumbnail.dart +++ b/lib/src/widgets/board_thumbnail.dart @@ -76,11 +76,15 @@ class _BoardThumbnailState extends ConsumerState { fen: widget.fen, orientation: widget.orientation, lastMove: widget.lastMove as NormalMove?, - settings: boardPrefs.toBoardSettings().copyWith( + settings: ChessboardSettings( enableCoordinates: false, borderRadius: const BorderRadius.all(Radius.circular(4.0)), boxShadow: boardShadows, - animationDuration: widget.animationDuration, + animationDuration: widget.animationDuration!, + pieceAssets: boardPrefs.pieceSet.assets, + colorScheme: boardPrefs.boardTheme.colors, + hue: boardPrefs.hue, + brightness: boardPrefs.brightness, ), ) : StaticChessboard( @@ -93,8 +97,8 @@ class _BoardThumbnailState extends ConsumerState { boxShadow: boardShadows, pieceAssets: boardPrefs.pieceSet.assets, colorScheme: boardPrefs.boardTheme.colors, - brightness: boardPrefs.brightness, hue: boardPrefs.hue, + brightness: boardPrefs.brightness, ); final maybeTappableBoard = From 35e905e73aacc6dce24699f9f2cd6c30627ea5c7 Mon Sep 17 00:00:00 2001 From: Vincent Velociter Date: Tue, 17 Dec 2024 15:02:25 +0100 Subject: [PATCH 06/17] More work on custom theme --- lib/src/app.dart | 34 +- lib/src/constants.dart | 2 + lib/src/init.dart | 44 +- .../model/settings/general_preferences.dart | 23 +- .../view/settings/settings_tab_screen.dart | 2 - lib/src/view/settings/theme_screen.dart | 653 ++++++++++-------- 6 files changed, 437 insertions(+), 321 deletions(-) diff --git a/lib/src/app.dart b/lib/src/app.dart index 40cf12ec4c..a7740b476f 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -17,7 +17,6 @@ import 'package:lichess_mobile/src/navigation.dart'; import 'package:lichess_mobile/src/network/connectivity.dart'; import 'package:lichess_mobile/src/network/http.dart'; import 'package:lichess_mobile/src/network/socket.dart'; -import 'package:lichess_mobile/src/styles/lichess_colors.dart'; import 'package:lichess_mobile/src/styles/styles.dart'; import 'package:lichess_mobile/src/utils/screen.dart'; @@ -140,27 +139,22 @@ class _AppState extends ConsumerState { final dynamicColorScheme = brightness == Brightness.light ? fixedLightScheme : fixedDarkScheme; - ColorScheme colorScheme; - if (generalPrefs.customThemeEnabled) { - if (generalPrefs.customThemeSeed != null) { - colorScheme = ColorScheme.fromSeed( - seedColor: generalPrefs.customThemeSeed!, - brightness: brightness, - ); - } else if (dynamicColorScheme != null) { - colorScheme = dynamicColorScheme; - } else { - colorScheme = ColorScheme.fromSeed( - seedColor: LichessColors.primary[500]!, - brightness: brightness, - ); - } - } else { - colorScheme = ColorScheme.fromSeed( + final ColorScheme colorScheme = switch (generalPrefs.appThemeSeed) { + AppThemeSeed.color => ColorScheme.fromSeed( + seedColor: generalPrefs.customThemeSeed ?? kDefaultSeedColor, + brightness: brightness, + ), + AppThemeSeed.board => ColorScheme.fromSeed( seedColor: boardTheme.colors.darkSquare, brightness: brightness, - ); - } + ), + AppThemeSeed.system => + dynamicColorScheme ?? + ColorScheme.fromSeed( + seedColor: boardTheme.colors.darkSquare, + brightness: brightness, + ), + }; final cupertinoThemeData = CupertinoThemeData( primaryColor: colorScheme.primary, diff --git a/lib/src/constants.dart b/lib/src/constants.dart index dc0d78eeea..079c0343fe 100644 --- a/lib/src/constants.dart +++ b/lib/src/constants.dart @@ -37,6 +37,8 @@ const kClueLessDeviation = 230; // UI +const kDefaultSeedColor = Color(0xFFD64F00); + const kGoldenRatio = 1.61803398875; /// Flex golden ratio base (flex has to be an int). diff --git a/lib/src/init.dart b/lib/src/init.dart index d4ac98c44f..3c47e67ed6 100644 --- a/lib/src/init.dart +++ b/lib/src/init.dart @@ -32,6 +32,11 @@ Future setupFirstLaunch() async { final appVersion = Version.parse(pInfo.version); final installedVersion = prefs.getString('installed_version'); + // TODO remove this migration code after a few releases + if (installedVersion != null && Version.parse(installedVersion) <= Version(0, 13, 9)) { + _migrateThemeSettings(); + } + if (installedVersion == null || Version.parse(installedVersion) != appVersion) { prefs.setString('installed_version', appVersion.canonicalizedVersion); } @@ -49,6 +54,26 @@ Future setupFirstLaunch() async { } } +Future _migrateThemeSettings() async { + final prefs = LichessBinding.instance.sharedPreferences; + try { + final stored = LichessBinding.instance.sharedPreferences.getString( + PrefCategory.general.storageKey, + ); + if (stored == null) { + return; + } + final generalPrefs = GeneralPrefs.fromJson(jsonDecode(stored) as Map); + final migrated = generalPrefs.copyWith( + // ignore: deprecated_member_use_from_same_package + appThemeSeed: generalPrefs.systemColors == true ? AppThemeSeed.system : AppThemeSeed.board, + ); + await prefs.setString(PrefCategory.general.storageKey, jsonEncode(migrated.toJson())); + } catch (e) { + _logger.warning('Failed to migrate theme settings: $e'); + } +} + Future initializeLocalNotifications(Locale locale) async { final l10n = await AppLocalizations.delegate.load(locale); await FlutterLocalNotificationsPlugin().initialize( @@ -86,27 +111,10 @@ Future preloadPieceImages() async { /// /// This is meant to be called once during app initialization. Future androidDisplayInitialization(WidgetsBinding widgetsBinding) async { - final prefs = LichessBinding.instance.sharedPreferences; - - // On android 12+ get core palette and set the board theme to system if it is not set + // On android 12+ set core palette and make system board try { await DynamicColorPlugin.getCorePalette().then((value) { setCorePalette(value); - - if (getCorePalette() != null) { - if (prefs.getString(PrefCategory.general.storageKey) == null) { - prefs.setString( - PrefCategory.general.storageKey, - jsonEncode(GeneralPrefs.defaults.copyWith(customThemeEnabled: true)), - ); - } - if (prefs.getString(PrefCategory.board.storageKey) == null) { - prefs.setString( - PrefCategory.board.storageKey, - jsonEncode(BoardPrefs.defaults.copyWith(boardTheme: BoardTheme.system)), - ); - } - } }); } catch (e) { _logger.fine('Device does not support core palette: $e'); diff --git a/lib/src/model/settings/general_preferences.dart b/lib/src/model/settings/general_preferences.dart index f788e60dd5..aa9571ee6c 100644 --- a/lib/src/model/settings/general_preferences.dart +++ b/lib/src/model/settings/general_preferences.dart @@ -1,7 +1,6 @@ import 'dart:ui' show Color, Locale; import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:lichess_mobile/src/model/settings/board_preferences.dart'; import 'package:lichess_mobile/src/model/settings/preferences_storage.dart'; import 'package:lichess_mobile/src/utils/json.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -54,6 +53,10 @@ class GeneralPreferences extends _$GeneralPreferences with PreferencesStorage setCustomThemeSeed(Color? color) { return save(state.copyWith(customThemeSeed: color)); } + + Future setAppThemeSeed(AppThemeSeed seed) { + return save(state.copyWith(appThemeSeed: seed)); + } } @Freezed(fromJson: true, toJson: true) @@ -71,6 +74,12 @@ class GeneralPrefs with _$GeneralPrefs implements Serializable { /// Custom theme seed color @ColorConverter() Color? customThemeSeed, + @Deprecated('Use appThemeSeed instead') bool? systemColors, + + /// App theme seed + @JsonKey(unknownEnumValue: AppThemeSeed.color, defaultValue: AppThemeSeed.color) + required AppThemeSeed appThemeSeed, + /// Locale to use in the app, use system locale if null @LocaleConverter() Locale? locale, }) = _GeneralPrefs; @@ -81,6 +90,7 @@ class GeneralPrefs with _$GeneralPrefs implements Serializable { soundTheme: SoundTheme.standard, masterVolume: 0.8, customThemeEnabled: false, + appThemeSeed: AppThemeSeed.color, ); factory GeneralPrefs.fromJson(Map json) { @@ -88,6 +98,17 @@ class GeneralPrefs with _$GeneralPrefs implements Serializable { } } +enum AppThemeSeed { + /// The app theme is based on the user's system theme (only available on Android 10+). + system, + + /// The app theme is based on the chessboard. + board, + + /// The app theme is based on a specific color. + color, +} + /// Describes the background theme of the app. enum BackgroundThemeMode { /// Use either the light or dark theme based on what the user has selected in diff --git a/lib/src/view/settings/settings_tab_screen.dart b/lib/src/view/settings/settings_tab_screen.dart index 687952a257..6396d7287d 100644 --- a/lib/src/view/settings/settings_tab_screen.dart +++ b/lib/src/view/settings/settings_tab_screen.dart @@ -8,7 +8,6 @@ import 'package:lichess_mobile/src/model/account/account_repository.dart'; import 'package:lichess_mobile/src/model/auth/auth_controller.dart'; import 'package:lichess_mobile/src/model/auth/auth_session.dart'; import 'package:lichess_mobile/src/model/common/preloaded_data.dart'; -import 'package:lichess_mobile/src/model/settings/board_preferences.dart'; import 'package:lichess_mobile/src/model/settings/general_preferences.dart'; import 'package:lichess_mobile/src/navigation.dart'; import 'package:lichess_mobile/src/styles/lichess_icons.dart'; @@ -82,7 +81,6 @@ class _Body extends ConsumerWidget { }); final generalPrefs = ref.watch(generalPreferencesProvider); - final boardPrefs = ref.watch(boardPreferencesProvider); final authController = ref.watch(authControllerProvider); final userSession = ref.watch(authSessionProvider); final packageInfo = ref.read(preloadedDataProvider).requireValue.packageInfo; diff --git a/lib/src/view/settings/theme_screen.dart b/lib/src/view/settings/theme_screen.dart index 10d58c8748..c4f3a516b5 100644 --- a/lib/src/view/settings/theme_screen.dart +++ b/lib/src/view/settings/theme_screen.dart @@ -1,34 +1,51 @@ -import 'dart:math' as math; +import 'dart:ui' show ImageFilter; import 'package:chessground/chessground.dart'; import 'package:dartchess/dartchess.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_colorpicker/flutter_colorpicker.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:lichess_mobile/src/constants.dart'; import 'package:lichess_mobile/src/model/settings/board_preferences.dart'; import 'package:lichess_mobile/src/model/settings/general_preferences.dart'; -import 'package:lichess_mobile/src/styles/lichess_colors.dart'; import 'package:lichess_mobile/src/styles/lichess_icons.dart'; +import 'package:lichess_mobile/src/styles/styles.dart'; import 'package:lichess_mobile/src/utils/color_palette.dart'; import 'package:lichess_mobile/src/utils/l10n_context.dart'; import 'package:lichess_mobile/src/utils/navigation.dart'; import 'package:lichess_mobile/src/view/settings/board_theme_screen.dart'; import 'package:lichess_mobile/src/view/settings/piece_set_screen.dart'; +import 'package:lichess_mobile/src/widgets/adaptive_action_sheet.dart'; import 'package:lichess_mobile/src/widgets/adaptive_choice_picker.dart'; import 'package:lichess_mobile/src/widgets/buttons.dart'; import 'package:lichess_mobile/src/widgets/change_colors.dart'; import 'package:lichess_mobile/src/widgets/list.dart'; +import 'package:lichess_mobile/src/widgets/platform.dart'; import 'package:lichess_mobile/src/widgets/platform_alert_dialog.dart'; -import 'package:lichess_mobile/src/widgets/platform_scaffold.dart'; import 'package:lichess_mobile/src/widgets/settings.dart'; +const _kBoardSize = 200.0; + class ThemeScreen extends StatelessWidget { const ThemeScreen({super.key}); @override Widget build(BuildContext context) { - return PlatformScaffold(appBar: const PlatformAppBar(title: Text('Theme')), body: _Body()); + return PlatformWidget( + androidBuilder: (context) => const Scaffold(body: _Body()), + iosBuilder: + (context) => CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + automaticBackgroundVisibility: false, + backgroundColor: Styles.cupertinoAppBarColor + .resolveFrom(context) + .withValues(alpha: 0.0), + border: null, + ), + child: const _Body(), + ), + ); } } @@ -42,6 +59,8 @@ switch (shapeColor) { }; class _Body extends ConsumerStatefulWidget { + const _Body(); + @override ConsumerState<_Body> createState() => _BodyState(); } @@ -50,6 +69,10 @@ class _BodyState extends ConsumerState<_Body> { late double brightness; late double hue; + double headerOpacity = 0; + + bool openAdjustColorSection = false; + @override void initState() { super.initState(); @@ -58,220 +81,296 @@ class _BodyState extends ConsumerState<_Body> { hue = boardPrefs.hue; } + bool handleScrollNotification(ScrollNotification notification) { + if (notification is ScrollUpdateNotification && notification.depth == 0) { + final ScrollMetrics metrics = notification.metrics; + double scrollExtent = 0.0; + switch (metrics.axisDirection) { + case AxisDirection.up: + scrollExtent = metrics.extentAfter; + case AxisDirection.down: + scrollExtent = metrics.extentBefore; + case AxisDirection.right: + case AxisDirection.left: + break; + } + + final opacity = scrollExtent > 0.0 ? 1.0 : 0.0; + + if (opacity != headerOpacity) { + setState(() { + headerOpacity = opacity; + }); + } + } + return false; + } + + void _showColorPicker() { + final generalPrefs = ref.read(generalPreferencesProvider); + showAdaptiveDialog( + context: context, + barrierDismissible: false, + builder: (context) { + bool useDefault = generalPrefs.customThemeSeed == null; + Color color = generalPrefs.customThemeSeed ?? kDefaultSeedColor; + return StatefulBuilder( + builder: (context, setState) { + return PlatformAlertDialog( + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ColorPicker( + enableAlpha: false, + pickerColor: color, + onColorChanged: (c) { + setState(() { + useDefault = false; + color = c; + }); + }, + ), + SecondaryButton( + semanticsLabel: 'Default color', + onPressed: + !useDefault + ? () { + setState(() { + useDefault = true; + color = kDefaultSeedColor; + }); + } + : null, + child: const Text('Default color'), + ), + SecondaryButton( + semanticsLabel: context.l10n.cancel, + onPressed: () { + Navigator.of(context).pop(false); + }, + child: Text(context.l10n.cancel), + ), + SecondaryButton( + semanticsLabel: context.l10n.ok, + onPressed: () { + if (useDefault) { + Navigator.of(context).pop(null); + } else { + Navigator.of(context).pop(color); + } + }, + child: Text(context.l10n.ok), + ), + ], + ), + ), + ); + }, + ); + }, + ).then((color) { + if (color != false) { + ref.read(generalPreferencesProvider.notifier).setCustomThemeSeed(color as Color?); + } + }); + } + @override Widget build(BuildContext context) { final generalPrefs = ref.watch(generalPreferencesProvider); final boardPrefs = ref.watch(boardPreferencesProvider); - const horizontalPadding = 16.0; - final bool hasAjustedColors = brightness != 0.0 || hue != 0.0; - return ListView( - children: [ - LayoutBuilder( - builder: (context, constraints) { - final double boardSize = math.min( - 250, - constraints.biggest.shortestSide - horizontalPadding * 2, - ); - return Padding( - padding: const EdgeInsets.symmetric(horizontal: horizontalPadding, vertical: 16), - child: Center( - child: ChangeColors( - brightness: brightness, - hue: hue, - child: Chessboard.fixed( - size: boardSize, - orientation: Side.white, - lastMove: const NormalMove(from: Square.e2, to: Square.e4), - fen: 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1', - shapes: - { - Circle(color: boardPrefs.shapeColor.color, orig: Square.fromName('b8')), - Arrow( - color: boardPrefs.shapeColor.color, - orig: Square.fromName('b8'), - dest: Square.fromName('c6'), - ), - }.lock, - settings: boardPrefs.toBoardSettings().copyWith( - brightness: 0.0, - hue: 0.0, - borderRadius: const BorderRadius.all(Radius.circular(4.0)), - boxShadow: boardShadows, + final backgroundColor = Styles.cupertinoAppBarColor.resolveFrom(context); + + return NotificationListener( + onNotification: handleScrollNotification, + child: CustomScrollView( + slivers: [ + if (Theme.of(context).platform == TargetPlatform.iOS) + PinnedHeaderSliver( + child: ClipRect( + child: BackdropFilter( + enabled: backgroundColor.alpha != 0xFF, + filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + decoration: ShapeDecoration( + color: headerOpacity == 1.0 ? backgroundColor : backgroundColor.withAlpha(0), + shape: LinearBorder.bottom( + side: BorderSide( + color: + headerOpacity == 1.0 ? const Color(0x4D000000) : Colors.transparent, + width: 0.0, + ), + ), ), + padding: + Styles.bodyPadding + + EdgeInsets.only(top: MediaQuery.paddingOf(context).top), + child: _BoardPreview(boardPrefs: boardPrefs, brightness: brightness, hue: hue), ), ), ), - ); - }, - ), - ListSection( - hasLeading: true, - children: [ - PlatformListTile( - leading: const Icon(Icons.brightness_6), - title: Slider.adaptive( - min: -0.5, - max: 0.5, - value: brightness, - onChanged: (value) { - setState(() { - brightness = value; - }); - }, - onChangeEnd: (value) { - ref.read(boardPreferencesProvider.notifier).adjustColors(brightness: brightness); - }, - ), - ), - PlatformListTile( - leading: const Icon(Icons.invert_colors), - title: Slider.adaptive( - min: -1.0, - max: 1.0, - value: hue, - onChanged: (value) { - setState(() { - hue = value; - }); - }, - onChangeEnd: (value) { - ref.read(boardPreferencesProvider.notifier).adjustColors(hue: hue); - }, + ) + else + SliverAppBar( + pinned: true, + title: const Text('Theme'), + bottom: PreferredSize( + preferredSize: const Size.fromHeight(_kBoardSize + 16.0), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: _BoardPreview(boardPrefs: boardPrefs, brightness: brightness, hue: hue), + ), ), ), - PlatformListTile( - leading: Opacity( - opacity: hasAjustedColors ? 1.0 : 0.5, - child: const Icon(Icons.cancel), - ), - title: Opacity( - opacity: hasAjustedColors ? 1.0 : 0.5, - child: Text(context.l10n.boardReset), + SliverList.list( + children: [ + ListSection( + hasLeading: true, + children: [ + SettingsListTile( + icon: const Icon(LichessIcons.chess_board), + settingsLabel: Text(context.l10n.board), + settingsValue: boardPrefs.boardTheme.label, + onTap: () { + pushPlatformRoute( + context, + title: context.l10n.board, + builder: (context) => const BoardThemeScreen(), + ); + }, + ), + SettingsListTile( + icon: const Icon(LichessIcons.chess_pawn), + settingsLabel: Text(context.l10n.pieceSet), + settingsValue: boardPrefs.pieceSet.label, + onTap: () { + pushPlatformRoute( + context, + title: context.l10n.pieceSet, + builder: (context) => const PieceSetScreen(), + ); + }, + ), + SettingsListTile( + icon: const Icon(LichessIcons.arrow_full_upperright), + settingsLabel: const Text('Shape color'), + settingsValue: shapeColorL10n(context, boardPrefs.shapeColor), + onTap: () { + showChoicePicker( + context, + choices: ShapeColor.values, + selectedItem: boardPrefs.shapeColor, + labelBuilder: + (t) => Text.rich( + TextSpan( + children: [ + TextSpan(text: shapeColorL10n(context, t)), + const TextSpan(text: ' '), + WidgetSpan( + child: Container(width: 15, height: 15, color: t.color), + ), + ], + ), + ), + onSelectedItemChanged: (ShapeColor? value) { + ref + .read(boardPreferencesProvider.notifier) + .setShapeColor(value ?? ShapeColor.green); + }, + ); + }, + ), + SwitchSettingTile( + leading: const Icon(Icons.location_on), + title: Text(context.l10n.preferencesBoardCoordinates), + value: boardPrefs.coordinates, + onChanged: (value) { + ref.read(boardPreferencesProvider.notifier).toggleCoordinates(); + }, + ), + SwitchSettingTile( + // TODO translate + leading: const Icon(Icons.border_outer), + title: const Text('Show border'), + value: boardPrefs.showBorder, + onChanged: (value) { + ref.read(boardPreferencesProvider.notifier).toggleBorder(); + }, + ), + ], ), - onTap: - hasAjustedColors - ? () { + ListSection( + header: SettingsSectionTitle(context.l10n.advancedSettings), + hasLeading: true, + children: [ + PlatformListTile( + leading: const Icon(Icons.brightness_6), + title: Slider.adaptive( + min: -0.5, + max: 0.5, + value: brightness, + onChanged: (value) { setState(() { - brightness = 0.0; - hue = 0.0; + brightness = value; }); + }, + onChangeEnd: (value) { ref .read(boardPreferencesProvider.notifier) - .adjustColors(brightness: 0.0, hue: 0.0); - } - : null, - ), - ], - ), - ListSection( - hasLeading: true, - children: [ - SettingsListTile( - icon: const Icon(LichessIcons.chess_board), - settingsLabel: Text(context.l10n.board), - settingsValue: boardPrefs.boardTheme.label, - onTap: () { - pushPlatformRoute( - context, - title: context.l10n.board, - builder: (context) => const BoardThemeScreen(), - ); - }, - ), - SettingsListTile( - icon: const Icon(LichessIcons.chess_pawn), - settingsLabel: Text(context.l10n.pieceSet), - settingsValue: boardPrefs.pieceSet.label, - onTap: () { - pushPlatformRoute( - context, - title: context.l10n.pieceSet, - builder: (context) => const PieceSetScreen(), - ); - }, - ), - SettingsListTile( - icon: const Icon(LichessIcons.arrow_full_upperright), - settingsLabel: const Text('Shape color'), - settingsValue: shapeColorL10n(context, boardPrefs.shapeColor), - onTap: () { - showChoicePicker( - context, - choices: ShapeColor.values, - selectedItem: boardPrefs.shapeColor, - labelBuilder: - (t) => Text.rich( - TextSpan( - children: [ - TextSpan(text: shapeColorL10n(context, t)), - const TextSpan(text: ' '), - WidgetSpan(child: Container(width: 15, height: 15, color: t.color)), - ], - ), - ), - onSelectedItemChanged: (ShapeColor? value) { - ref - .read(boardPreferencesProvider.notifier) - .setShapeColor(value ?? ShapeColor.green); - }, - ); - }, - ), - SwitchSettingTile( - leading: const Icon(Icons.location_on), - title: Text(context.l10n.preferencesBoardCoordinates), - value: boardPrefs.coordinates, - onChanged: (value) { - ref.read(boardPreferencesProvider.notifier).toggleCoordinates(); - }, - ), - SwitchSettingTile( - // TODO translate - leading: const Icon(Icons.border_outer), - title: const Text('Show border'), - value: boardPrefs.showBorder, - onChanged: (value) { - ref.read(boardPreferencesProvider.notifier).toggleBorder(); - }, - ), - ], - ), - ListSection( - hasLeading: true, - children: [ - SwitchSettingTile( - leading: const Icon(Icons.colorize_outlined), - padding: - Theme.of(context).platform == TargetPlatform.iOS - ? const EdgeInsets.symmetric(horizontal: 14, vertical: 8) - : null, - title: const Text('App theme'), - // TODO translate - subtitle: const Text('Configure your own app theme using a seed color.', maxLines: 3), - value: generalPrefs.customThemeEnabled, - onChanged: (value) { - ref.read(generalPreferencesProvider.notifier).toggleCustomTheme(); - }, - ), - AnimatedCrossFade( - duration: const Duration(milliseconds: 300), - crossFadeState: - generalPrefs.customThemeEnabled - ? CrossFadeState.showSecond - : CrossFadeState.showFirst, - firstChild: const SizedBox.shrink(), - secondChild: ListSection( - margin: EdgeInsets.zero, - cupertinoBorderRadius: BorderRadius.zero, - cupertinoClipBehavior: Clip.none, - children: [ + .adjustColors(brightness: brightness); + }, + ), + ), PlatformListTile( - leading: const Icon(Icons.color_lens), - title: const Text('Seed color'), - trailing: + leading: const Icon(Icons.invert_colors), + title: Slider.adaptive( + min: -1.0, + max: 1.0, + value: hue, + onChanged: (value) { + setState(() { + hue = value; + }); + }, + onChangeEnd: (value) { + ref.read(boardPreferencesProvider.notifier).adjustColors(hue: hue); + }, + ), + ), + PlatformListTile( + leading: Opacity( + opacity: hasAjustedColors ? 1.0 : 0.5, + child: const Icon(Icons.cancel), + ), + title: Opacity( + opacity: hasAjustedColors ? 1.0 : 0.5, + child: Text(context.l10n.boardReset), + ), + onTap: + hasAjustedColors + ? () { + setState(() { + brightness = 0.0; + hue = 0.0; + }); + ref + .read(boardPreferencesProvider.notifier) + .adjustColors(brightness: 0.0, hue: 0.0); + } + : null, + ), + PlatformListTile( + leading: const Icon(Icons.colorize_outlined), + title: const Text('App theme'), + trailing: switch (generalPrefs.appThemeSeed) { + AppThemeSeed.board => Text(context.l10n.board), + AppThemeSeed.system => Text(context.l10n.mobileSystemColors), + AppThemeSeed.color => generalPrefs.customThemeSeed != null ? Container( width: 20, @@ -281,102 +380,96 @@ class _BodyState extends ConsumerState<_Body> { shape: BoxShape.circle, ), ) - : getCorePalette() != null - ? Text(context.l10n.mobileSystemColors) : Container( width: 20, height: 20, - decoration: BoxDecoration( - color: LichessColors.primary[500], + decoration: const BoxDecoration( + color: kDefaultSeedColor, shape: BoxShape.circle, ), ), + }, onTap: () { - showAdaptiveDialog( + showAdaptiveActionSheet( context: context, - barrierDismissible: false, - builder: (context) { - final defaultColor = - getCorePalettePrimary() ?? LichessColors.primary[500]!; - bool useDefault = generalPrefs.customThemeSeed == null; - Color color = generalPrefs.customThemeSeed ?? defaultColor; - return StatefulBuilder( - builder: (context, setState) { - return PlatformAlertDialog( - content: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ColorPicker( - enableAlpha: false, - pickerColor: color, - onColorChanged: (c) { - setState(() { - useDefault = false; - color = c; - }); - }, - ), - SecondaryButton( - semanticsLabel: - getCorePalette() != null - ? context.l10n.mobileSystemColors - : 'Default color', - onPressed: - !useDefault - ? () { - setState(() { - useDefault = true; - color = defaultColor; - }); - } - : null, - child: Text( - getCorePalette() != null - ? context.l10n.mobileSystemColors - : 'Default color', - ), - ), - SecondaryButton( - semanticsLabel: context.l10n.cancel, - onPressed: () { - Navigator.of(context).pop(false); + actions: + AppThemeSeed.values + .where((t) => t != AppThemeSeed.system || getCorePalette() != null) + .map( + (t) => BottomSheetAction( + makeLabel: + (context) => switch (t) { + AppThemeSeed.board => Text(context.l10n.board), + AppThemeSeed.system => Text( + context.l10n.mobileSystemColors, + ), + AppThemeSeed.color => const Text('Custom color'), }, - child: Text(context.l10n.cancel), - ), - SecondaryButton( - semanticsLabel: context.l10n.ok, - onPressed: () { - if (useDefault) { - Navigator.of(context).pop(null); - } else { - Navigator.of(context).pop(color); - } - }, - child: Text(context.l10n.ok), - ), - ], + onPressed: (context) { + ref + .read(generalPreferencesProvider.notifier) + .setAppThemeSeed(t); + + if (t == AppThemeSeed.color) { + _showColorPicker(); + } + }, + dismissOnPress: true, ), - ), - ); - }, - ); - }, - ).then((color) { - if (color != false) { - ref - .read(generalPreferencesProvider.notifier) - .setCustomThemeSeed(color as Color?); - } - }); + ) + .toList(), + ); }, ), ], ), - ), - ], + ], + ), + const SliverSafeArea( + top: false, + sliver: SliverToBoxAdapter(child: SizedBox(height: 16.0)), + ), + ], + ), + ); + } +} + +class _BoardPreview extends StatelessWidget { + const _BoardPreview({required this.boardPrefs, required this.brightness, required this.hue}); + + final BoardPrefs boardPrefs; + final double brightness; + final double hue; + + @override + Widget build(BuildContext context) { + return Center( + child: ChangeColors( + brightness: brightness, + hue: hue, + child: Chessboard.fixed( + size: _kBoardSize, + orientation: Side.white, + lastMove: const NormalMove(from: Square.e2, to: Square.e4), + fen: 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1', + shapes: + { + Circle(color: boardPrefs.shapeColor.color, orig: Square.fromName('b8')), + Arrow( + color: boardPrefs.shapeColor.color, + orig: Square.fromName('b8'), + dest: Square.fromName('c6'), + ), + }.lock, + settings: boardPrefs.toBoardSettings().copyWith( + brightness: 0.0, + hue: 0.0, + borderRadius: const BorderRadius.all(Radius.circular(4.0)), + boxShadow: boardShadows, + ), ), - ], + ), ); } } From 9b5e19c6ffc86c54434181cc1612d3600917d94a Mon Sep 17 00:00:00 2001 From: Vincent Velociter Date: Wed, 18 Dec 2024 18:24:21 +0100 Subject: [PATCH 07/17] Tweak custom theme --- lib/main.dart | 17 +++++------------ lib/src/constants.dart | 2 +- lib/src/init.dart | 7 +++++-- lib/src/model/settings/general_preferences.dart | 4 ++-- lib/src/view/settings/theme_screen.dart | 5 ++++- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index c04e4e8423..0ec26f1d80 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -26,6 +26,10 @@ Future main() async { await lichessBinding.preloadSharedPreferences(); + if (defaultTargetPlatform == TargetPlatform.android) { + await androidDisplayInitialization(widgetsBinding); + } + await preloadPieceImages(); await setupFirstLaunch(); @@ -38,18 +42,7 @@ Future main() async { await lichessBinding.initializeFirebase(); - if (defaultTargetPlatform == TargetPlatform.android) { - await androidDisplayInitialization(widgetsBinding); - } - - runApp( - ProviderScope( - observers: [ - ProviderLogger(), - ], - child: const AppInitializationScreen(), - ), - ); + runApp(ProviderScope(observers: [ProviderLogger()], child: const AppInitializationScreen())); } Future migrateSharedPreferences() async { diff --git a/lib/src/constants.dart b/lib/src/constants.dart index 079c0343fe..811cbe7051 100644 --- a/lib/src/constants.dart +++ b/lib/src/constants.dart @@ -37,7 +37,7 @@ const kClueLessDeviation = 230; // UI -const kDefaultSeedColor = Color(0xFFD64F00); +const kDefaultSeedColor = Color.fromARGB(255, 191, 128, 29); const kGoldenRatio = 1.61803398875; diff --git a/lib/src/init.dart b/lib/src/init.dart index 3c47e67ed6..f3132fe82c 100644 --- a/lib/src/init.dart +++ b/lib/src/init.dart @@ -65,8 +65,11 @@ Future _migrateThemeSettings() async { } final generalPrefs = GeneralPrefs.fromJson(jsonDecode(stored) as Map); final migrated = generalPrefs.copyWith( - // ignore: deprecated_member_use_from_same_package - appThemeSeed: generalPrefs.systemColors == true ? AppThemeSeed.system : AppThemeSeed.board, + appThemeSeed: + // ignore: deprecated_member_use_from_same_package + getCorePalette() != null && generalPrefs.systemColors == true + ? AppThemeSeed.system + : AppThemeSeed.board, ); await prefs.setString(PrefCategory.general.storageKey, jsonEncode(migrated.toJson())); } catch (e) { diff --git a/lib/src/model/settings/general_preferences.dart b/lib/src/model/settings/general_preferences.dart index aa9571ee6c..a612c4fa82 100644 --- a/lib/src/model/settings/general_preferences.dart +++ b/lib/src/model/settings/general_preferences.dart @@ -77,7 +77,7 @@ class GeneralPrefs with _$GeneralPrefs implements Serializable { @Deprecated('Use appThemeSeed instead') bool? systemColors, /// App theme seed - @JsonKey(unknownEnumValue: AppThemeSeed.color, defaultValue: AppThemeSeed.color) + @JsonKey(unknownEnumValue: AppThemeSeed.board, defaultValue: AppThemeSeed.board) required AppThemeSeed appThemeSeed, /// Locale to use in the app, use system locale if null @@ -90,7 +90,7 @@ class GeneralPrefs with _$GeneralPrefs implements Serializable { soundTheme: SoundTheme.standard, masterVolume: 0.8, customThemeEnabled: false, - appThemeSeed: AppThemeSeed.color, + appThemeSeed: AppThemeSeed.board, ); factory GeneralPrefs.fromJson(Map json) { diff --git a/lib/src/view/settings/theme_screen.dart b/lib/src/view/settings/theme_screen.dart index c4f3a516b5..3503eb1c58 100644 --- a/lib/src/view/settings/theme_screen.dart +++ b/lib/src/view/settings/theme_screen.dart @@ -121,8 +121,11 @@ class _BodyState extends ConsumerState<_Body> { child: Column( mainAxisSize: MainAxisSize.min, children: [ - ColorPicker( + HueRingPicker( enableAlpha: false, + colorPickerHeight: 200, + displayThumbColor: false, + portraitOnly: true, pickerColor: color, onColorChanged: (c) { setState(() { From cf7bab2c4b5afbacbcd5aa7003d449e48774d3ec Mon Sep 17 00:00:00 2001 From: Vincent Velociter Date: Wed, 18 Dec 2024 18:55:40 +0100 Subject: [PATCH 08/17] Fix brightness adjust matrix --- lib/src/model/settings/board_preferences.dart | 2 +- lib/src/view/settings/theme_screen.dart | 4 ++-- lib/src/widgets/change_colors.dart | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/model/settings/board_preferences.dart b/lib/src/model/settings/board_preferences.dart index 402e659789..e6e310d265 100644 --- a/lib/src/model/settings/board_preferences.dart +++ b/lib/src/model/settings/board_preferences.dart @@ -105,7 +105,7 @@ class BoardPrefs with _$BoardPrefs implements Serializable { const BoardPrefs._(); @Assert( - 'brightness == null || brightness >= -0.5 && brightness <= 0.5, hue == null || hue >= -1 && hue <= 1', + 'brightness == null || brightness >= -1.0 && brightness <= 1.0, hue == null || hue >= -1 && hue <= 1', ) const factory BoardPrefs({ required PieceSet pieceSet, diff --git a/lib/src/view/settings/theme_screen.dart b/lib/src/view/settings/theme_screen.dart index 3503eb1c58..97fa11988e 100644 --- a/lib/src/view/settings/theme_screen.dart +++ b/lib/src/view/settings/theme_screen.dart @@ -314,8 +314,8 @@ class _BodyState extends ConsumerState<_Body> { PlatformListTile( leading: const Icon(Icons.brightness_6), title: Slider.adaptive( - min: -0.5, - max: 0.5, + min: -1.0, + max: 1.0, value: brightness, onChanged: (value) { setState(() { diff --git a/lib/src/widgets/change_colors.dart b/lib/src/widgets/change_colors.dart index 38ce7eebe3..97526f6541 100644 --- a/lib/src/widgets/change_colors.dart +++ b/lib/src/widgets/change_colors.dart @@ -55,7 +55,7 @@ List _adjustMatrix({required double hue, required double brightness}) { ]; // dart format on } - final brightnessValue = brightness <= 0 ? brightness * 255 : brightness * 100; + final brightnessValue = brightness <= 0 ? brightness * 100 : brightness * 100; return List.from([ 0.213 + cos(hue) * 0.787 + sin(hue) * -0.213, 0.715 + cos(hue) * -0.715 + sin(hue) * -0.715, From b5e323b3fb577335498c3d25e4fb03a57ba45f86 Mon Sep 17 00:00:00 2001 From: Vincent Velociter Date: Wed, 18 Dec 2024 18:58:32 +0100 Subject: [PATCH 09/17] Tweak --- lib/src/widgets/change_colors.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/src/widgets/change_colors.dart b/lib/src/widgets/change_colors.dart index 97526f6541..54a41d5f13 100644 --- a/lib/src/widgets/change_colors.dart +++ b/lib/src/widgets/change_colors.dart @@ -17,10 +17,6 @@ import 'package:flutter/material.dart'; /// child: Image.asset('myImage.png'), /// ); /// ``` -/// -/// To achieve a greyscale effect, you may also use the -/// [ChangeColors.greyscale] constructor. -/// class ChangeColors extends StatelessWidget { const ChangeColors({this.brightness = 0.0, this.hue = 0.0, required this.child, super.key}); From 9d7117380a45676ea5e17edef1c75203ead262a3 Mon Sep 17 00:00:00 2001 From: Vincent Velociter Date: Thu, 19 Dec 2024 13:14:55 +0100 Subject: [PATCH 10/17] Update chessground --- pubspec.lock | 7 ++++--- pubspec.yaml | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 45fb76ab74..ce3f85acbd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -201,9 +201,10 @@ packages: chessground: dependency: "direct main" description: - path: "../flutter-chessground" - relative: true - source: path + name: chessground + sha256: "0913b4cb80ea514db5d2aa61006d1f8ca77bc353b8da30bc0c243b6d4276a113" + url: "https://pub.dev" + source: hosted version: "6.2.0" ci: dependency: transitive diff --git a/pubspec.yaml b/pubspec.yaml index 8600a41706..390dc33e60 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,8 +14,7 @@ dependencies: async: ^2.10.0 auto_size_text: ^3.0.0 cached_network_image: ^3.2.2 - chessground: - path: ../flutter-chessground + chessground: ^6.2.0 clock: ^1.1.1 collection: ^1.17.0 connectivity_plus: ^6.0.2 From bf64ac70dcce28d2f3cf7f0dde3fc31f617e0d7c Mon Sep 17 00:00:00 2001 From: Vincent Velociter Date: Thu, 19 Dec 2024 13:15:05 +0100 Subject: [PATCH 11/17] Improve theme screen for tablets --- lib/src/view/settings/theme_screen.dart | 31 +++++++++++++++++++------ 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/src/view/settings/theme_screen.dart b/lib/src/view/settings/theme_screen.dart index 97fa11988e..1b4c590d20 100644 --- a/lib/src/view/settings/theme_screen.dart +++ b/lib/src/view/settings/theme_screen.dart @@ -14,6 +14,7 @@ import 'package:lichess_mobile/src/styles/styles.dart'; import 'package:lichess_mobile/src/utils/color_palette.dart'; import 'package:lichess_mobile/src/utils/l10n_context.dart'; import 'package:lichess_mobile/src/utils/navigation.dart'; +import 'package:lichess_mobile/src/utils/screen.dart'; import 'package:lichess_mobile/src/view/settings/board_theme_screen.dart'; import 'package:lichess_mobile/src/view/settings/piece_set_screen.dart'; import 'package:lichess_mobile/src/widgets/adaptive_action_sheet.dart'; @@ -25,8 +26,6 @@ import 'package:lichess_mobile/src/widgets/platform.dart'; import 'package:lichess_mobile/src/widgets/platform_alert_dialog.dart'; import 'package:lichess_mobile/src/widgets/settings.dart'; -const _kBoardSize = 200.0; - class ThemeScreen extends StatelessWidget { const ThemeScreen({super.key}); @@ -186,6 +185,8 @@ class _BodyState extends ConsumerState<_Body> { final bool hasAjustedColors = brightness != 0.0 || hue != 0.0; + final boardSize = isTabletOrLarger(context) ? 350.0 : 200.0; + final backgroundColor = Styles.cupertinoAppBarColor.resolveFrom(context); return NotificationListener( @@ -213,7 +214,12 @@ class _BodyState extends ConsumerState<_Body> { padding: Styles.bodyPadding + EdgeInsets.only(top: MediaQuery.paddingOf(context).top), - child: _BoardPreview(boardPrefs: boardPrefs, brightness: brightness, hue: hue), + child: _BoardPreview( + size: boardSize, + boardPrefs: boardPrefs, + brightness: brightness, + hue: hue, + ), ), ), ), @@ -223,10 +229,15 @@ class _BodyState extends ConsumerState<_Body> { pinned: true, title: const Text('Theme'), bottom: PreferredSize( - preferredSize: const Size.fromHeight(_kBoardSize + 16.0), + preferredSize: Size.fromHeight(boardSize + 16.0), child: Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), - child: _BoardPreview(boardPrefs: boardPrefs, brightness: brightness, hue: hue), + child: _BoardPreview( + size: boardSize, + boardPrefs: boardPrefs, + brightness: brightness, + hue: hue, + ), ), ), ), @@ -439,11 +450,17 @@ class _BodyState extends ConsumerState<_Body> { } class _BoardPreview extends StatelessWidget { - const _BoardPreview({required this.boardPrefs, required this.brightness, required this.hue}); + const _BoardPreview({ + required this.size, + required this.boardPrefs, + required this.brightness, + required this.hue, + }); final BoardPrefs boardPrefs; final double brightness; final double hue; + final double size; @override Widget build(BuildContext context) { @@ -452,7 +469,7 @@ class _BoardPreview extends StatelessWidget { brightness: brightness, hue: hue, child: Chessboard.fixed( - size: _kBoardSize, + size: size, orientation: Side.white, lastMove: const NormalMove(from: Square.e2, to: Square.e4), fen: 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1', From 6e2226a2cd27293fc38d93ac27f33000e0e38019 Mon Sep 17 00:00:00 2001 From: Vincent Velociter Date: Thu, 19 Dec 2024 16:11:50 +0100 Subject: [PATCH 12/17] Upgrade chessground, improve color filter --- lib/src/model/settings/board_preferences.dart | 18 +++-- lib/src/view/settings/theme_screen.dart | 19 ++--- lib/src/widgets/board_carousel_item.dart | 2 +- lib/src/widgets/change_colors.dart | 77 ------------------- pubspec.lock | 4 +- pubspec.yaml | 2 +- 6 files changed, 24 insertions(+), 98 deletions(-) delete mode 100644 lib/src/widgets/change_colors.dart diff --git a/lib/src/model/settings/board_preferences.dart b/lib/src/model/settings/board_preferences.dart index e6e310d265..3b8335e8fd 100644 --- a/lib/src/model/settings/board_preferences.dart +++ b/lib/src/model/settings/board_preferences.dart @@ -11,6 +11,9 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'board_preferences.freezed.dart'; part 'board_preferences.g.dart'; +const kBoardDefaultBrightnessFilter = 1.0; +const kBoardDefaultHueFilter = 0.0; + @riverpod class BoardPreferences extends _$BoardPreferences with PreferencesStorage { // ignore: avoid_public_notifier_properties @@ -104,9 +107,7 @@ class BoardPreferences extends _$BoardPreferences with PreferencesStorage= -1.0 && brightness <= 1.0, hue == null || hue >= -1 && hue <= 1', - ) + @Assert('brightness >= 0.2 && brightness <= 1.4, hue >= 0.0 && hue <= 360.0') const factory BoardPrefs({ required PieceSet pieceSet, required BoardTheme boardTheme, @@ -133,8 +134,8 @@ class BoardPrefs with _$BoardPrefs implements Serializable { @JsonKey(defaultValue: ShapeColor.green, unknownEnumValue: ShapeColor.green) required ShapeColor shapeColor, @JsonKey(defaultValue: false) required bool showBorder, - @JsonKey(defaultValue: 0.0) required double brightness, - @JsonKey(defaultValue: 0.0) required double hue, + @JsonKey(defaultValue: kBoardDefaultBrightnessFilter) required double brightness, + @JsonKey(defaultValue: kBoardDefaultBrightnessFilter) required double hue, }) = _BoardPrefs; static const defaults = BoardPrefs( @@ -154,11 +155,12 @@ class BoardPrefs with _$BoardPrefs implements Serializable { dragTargetKind: DragTargetKind.circle, shapeColor: ShapeColor.green, showBorder: false, - brightness: 0.0, - hue: 0.0, + brightness: kBoardDefaultBrightnessFilter, + hue: kBoardDefaultHueFilter, ); - bool get hasColorAdjustments => brightness != 0.0 || hue != 0.0; + bool get hasColorAdjustments => + brightness != kBoardDefaultBrightnessFilter || hue != kBoardDefaultHueFilter; ChessboardSettings toBoardSettings() { return ChessboardSettings( diff --git a/lib/src/view/settings/theme_screen.dart b/lib/src/view/settings/theme_screen.dart index 1b4c590d20..c147d15d54 100644 --- a/lib/src/view/settings/theme_screen.dart +++ b/lib/src/view/settings/theme_screen.dart @@ -183,7 +183,8 @@ class _BodyState extends ConsumerState<_Body> { final generalPrefs = ref.watch(generalPreferencesProvider); final boardPrefs = ref.watch(boardPreferencesProvider); - final bool hasAjustedColors = brightness != 0.0 || hue != 0.0; + final bool hasAjustedColors = + brightness != kBoardDefaultBrightnessFilter || hue != kBoardDefaultHueFilter; final boardSize = isTabletOrLarger(context) ? 350.0 : 200.0; @@ -325,8 +326,8 @@ class _BodyState extends ConsumerState<_Body> { PlatformListTile( leading: const Icon(Icons.brightness_6), title: Slider.adaptive( - min: -1.0, - max: 1.0, + min: 0.2, + max: 1.4, value: brightness, onChanged: (value) { setState(() { @@ -343,8 +344,8 @@ class _BodyState extends ConsumerState<_Body> { PlatformListTile( leading: const Icon(Icons.invert_colors), title: Slider.adaptive( - min: -1.0, - max: 1.0, + min: 0.0, + max: 360.0, value: hue, onChanged: (value) { setState(() { @@ -369,12 +370,12 @@ class _BodyState extends ConsumerState<_Body> { hasAjustedColors ? () { setState(() { - brightness = 0.0; - hue = 0.0; + brightness = kBoardDefaultBrightnessFilter; + hue = kBoardDefaultHueFilter; }); ref .read(boardPreferencesProvider.notifier) - .adjustColors(brightness: 0.0, hue: 0.0); + .adjustColors(brightness: brightness, hue: hue); } : null, ), @@ -465,7 +466,7 @@ class _BoardPreview extends StatelessWidget { @override Widget build(BuildContext context) { return Center( - child: ChangeColors( + child: BrightnessHueFilter( brightness: brightness, hue: hue, child: Chessboard.fixed( diff --git a/lib/src/widgets/board_carousel_item.dart b/lib/src/widgets/board_carousel_item.dart index 2559c8398f..acefe85ac3 100644 --- a/lib/src/widgets/board_carousel_item.dart +++ b/lib/src/widgets/board_carousel_item.dart @@ -52,7 +52,7 @@ class BoardCarouselItem extends ConsumerWidget { return LayoutBuilder( builder: (context, constraints) { final boardSize = constraints.biggest.shortestSide - _kBoardCarouselItemMargin.horizontal; - final card = ChangeColors( + final card = BrightnessHueFilter( hue: boardPrefs.hue, brightness: boardPrefs.brightness, child: PlatformCard( diff --git a/lib/src/widgets/change_colors.dart b/lib/src/widgets/change_colors.dart deleted file mode 100644 index 54a41d5f13..0000000000 --- a/lib/src/widgets/change_colors.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; - -// Based upon: https://stackoverflow.com/questions/64639589/how-to-adjust-hue-saturation-and-brightness-of-an-image-in-flutter -// from BananaNeil: https://stackoverflow.com/users/937841/banananeil. -// This is, in turn, based upon: https://stackoverflow.com/a/7917978/937841 - -/// Use the [ChangeColors] widget to change the brightness and hue of any widget, including images. -/// -/// Example: -/// -/// ``` -/// ChangeColors( -/// hue: 0.55, -/// brightness: 0.2, -/// child: Image.asset('myImage.png'), -/// ); -/// ``` -class ChangeColors extends StatelessWidget { - const ChangeColors({this.brightness = 0.0, this.hue = 0.0, required this.child, super.key}); - - /// Negative value will make it darker (-1 is darkest). - /// Positive value will make it lighter (1 is the maximum, but you can go above it). - /// Note: 0.0 is unchanged. - final double brightness; - - /// From -1.0 to 1.0 (Note: 1.0 wraps into -1.0, such as 1.2 is the same as -0.8). - /// Note: 0.0 is unchanged. Adding or subtracting multiples of 2.0 also keeps it unchanged. - final double hue; - - final Widget child; - - @override - Widget build(BuildContext context) { - return ColorFiltered( - colorFilter: ColorFilter.matrix(_adjustMatrix(hue: hue * pi, brightness: brightness)), - child: child, - ); - } -} - -List _adjustMatrix({required double hue, required double brightness}) { - if (hue == 0 && brightness == 0) { - // dart format off - return [ - 1, 0, 0, 0, 0, - 0, 1, 0, 0, 0, - 0, 0, 1, 0, 0, - 0, 0, 0, 1, 0, - ]; - // dart format on - } - final brightnessValue = brightness <= 0 ? brightness * 100 : brightness * 100; - return List.from([ - 0.213 + cos(hue) * 0.787 + sin(hue) * -0.213, - 0.715 + cos(hue) * -0.715 + sin(hue) * -0.715, - 0.072 + cos(hue) * -0.072 + sin(hue) * 0.928, - 0, - brightnessValue, - 0.213 + cos(hue) * -0.213 + sin(hue) * 0.143, - 0.715 + cos(hue) * 0.285 + sin(hue) * 0.140, - 0.072 + cos(hue) * -0.072 + sin(hue) * -0.283, - 0, - brightnessValue, - 0.213 + cos(hue) * -0.213 + sin(hue) * -0.787, - 0.715 + cos(hue) * -0.715 + sin(hue) * 0.715, - 0.072 + cos(hue) * 0.928 + sin(hue) * 0.072, - 0, - brightnessValue, - 0, - 0, - 0, - 1, - 0, - ]).map((i) => i).toList(); -} diff --git a/pubspec.lock b/pubspec.lock index ce3f85acbd..decbb5c858 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -202,10 +202,10 @@ packages: dependency: "direct main" description: name: chessground - sha256: "0913b4cb80ea514db5d2aa61006d1f8ca77bc353b8da30bc0c243b6d4276a113" + sha256: "6ae599b48e8802e75d3b27e71816cb04c74667f54266d5d22f75f7fb11503c47" url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.2.2" ci: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 390dc33e60..9306efaf4d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: async: ^2.10.0 auto_size_text: ^3.0.0 cached_network_image: ^3.2.2 - chessground: ^6.2.0 + chessground: ^6.2.2 clock: ^1.1.1 collection: ^1.17.0 connectivity_plus: ^6.0.2 From 756635cc6fc57f7f726e6cd74c41fc135a31fa9b Mon Sep 17 00:00:00 2001 From: Vincent Velociter Date: Thu, 19 Dec 2024 16:14:18 +0100 Subject: [PATCH 13/17] Remove old import --- lib/src/view/settings/theme_screen.dart | 1 - lib/src/widgets/board_carousel_item.dart | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/src/view/settings/theme_screen.dart b/lib/src/view/settings/theme_screen.dart index c147d15d54..4d5e3fb293 100644 --- a/lib/src/view/settings/theme_screen.dart +++ b/lib/src/view/settings/theme_screen.dart @@ -20,7 +20,6 @@ import 'package:lichess_mobile/src/view/settings/piece_set_screen.dart'; import 'package:lichess_mobile/src/widgets/adaptive_action_sheet.dart'; import 'package:lichess_mobile/src/widgets/adaptive_choice_picker.dart'; import 'package:lichess_mobile/src/widgets/buttons.dart'; -import 'package:lichess_mobile/src/widgets/change_colors.dart'; import 'package:lichess_mobile/src/widgets/list.dart'; import 'package:lichess_mobile/src/widgets/platform.dart'; import 'package:lichess_mobile/src/widgets/platform_alert_dialog.dart'; diff --git a/lib/src/widgets/board_carousel_item.dart b/lib/src/widgets/board_carousel_item.dart index acefe85ac3..a8b1a0ba3b 100644 --- a/lib/src/widgets/board_carousel_item.dart +++ b/lib/src/widgets/board_carousel_item.dart @@ -7,7 +7,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:lichess_mobile/src/model/settings/board_preferences.dart'; import 'package:lichess_mobile/src/styles/styles.dart'; import 'package:lichess_mobile/src/widgets/buttons.dart'; -import 'package:lichess_mobile/src/widgets/change_colors.dart'; import 'package:lichess_mobile/src/widgets/platform.dart'; const _kBoardCarouselItemMargin = EdgeInsets.symmetric(vertical: 8.0, horizontal: 6.0); From 64d4ee2393fd4168547afdece36bb72b28d6782a Mon Sep 17 00:00:00 2001 From: Vincent Velociter Date: Thu, 19 Dec 2024 16:20:21 +0100 Subject: [PATCH 14/17] Restore puzzle tab icon color --- lib/src/view/puzzle/puzzle_tab_screen.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/src/view/puzzle/puzzle_tab_screen.dart b/lib/src/view/puzzle/puzzle_tab_screen.dart index ed6b65764c..a870b218b9 100644 --- a/lib/src/view/puzzle/puzzle_tab_screen.dart +++ b/lib/src/view/puzzle/puzzle_tab_screen.dart @@ -369,7 +369,14 @@ class _PuzzleMenuListTile extends StatelessWidget { Theme.of(context).platform == TargetPlatform.iOS ? const EdgeInsets.symmetric(vertical: 10.0, horizontal: 14.0) : null, - leading: Icon(icon, size: Styles.mainListTileIconSize), + leading: Icon( + icon, + size: Styles.mainListTileIconSize, + color: + Theme.of(context).platform == TargetPlatform.iOS + ? CupertinoTheme.of(context).primaryColor + : Theme.of(context).colorScheme.primary, + ), title: Text(title, style: Styles.mainListTileTitle), subtitle: Text(subtitle, maxLines: 3), trailing: From 1809ec43497a1742e04bf3714bf517da5f08d377 Mon Sep 17 00:00:00 2001 From: Vincent Velociter Date: Thu, 19 Dec 2024 16:22:17 +0100 Subject: [PATCH 15/17] Fix theme screen default values --- lib/src/view/settings/theme_screen.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/view/settings/theme_screen.dart b/lib/src/view/settings/theme_screen.dart index 4d5e3fb293..ac1741be72 100644 --- a/lib/src/view/settings/theme_screen.dart +++ b/lib/src/view/settings/theme_screen.dart @@ -483,8 +483,8 @@ class _BoardPreview extends StatelessWidget { ), }.lock, settings: boardPrefs.toBoardSettings().copyWith( - brightness: 0.0, - hue: 0.0, + brightness: kBoardDefaultBrightnessFilter, + hue: kBoardDefaultHueFilter, borderRadius: const BorderRadius.all(Radius.circular(4.0)), boxShadow: boardShadows, ), From 74d557509d06a8bda93910cca85086c0a4fc923b Mon Sep 17 00:00:00 2001 From: Vincent Velociter Date: Thu, 19 Dec 2024 16:26:41 +0100 Subject: [PATCH 16/17] Remove unused function --- lib/src/utils/color_palette.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/src/utils/color_palette.dart b/lib/src/utils/color_palette.dart index 2419eb73b5..6ecdd18d82 100644 --- a/lib/src/utils/color_palette.dart +++ b/lib/src/utils/color_palette.dart @@ -45,10 +45,6 @@ void setCorePalette(CorePalette? palette) { } } -Color? getCorePalettePrimary() { - return _corePalette?.primary != null ? Color(_corePalette!.primary.get(50)) : null; -} - /// Get the core palette if available (android 12+ only). CorePalette? getCorePalette() { return _corePalette; From 341755c2d682a37d69f7c1649820acfad92736b2 Mon Sep 17 00:00:00 2001 From: Vincent Velociter Date: Thu, 19 Dec 2024 16:33:31 +0100 Subject: [PATCH 17/17] No need to migrate if core palette is null --- lib/src/init.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/src/init.dart b/lib/src/init.dart index f3132fe82c..655a4a5d31 100644 --- a/lib/src/init.dart +++ b/lib/src/init.dart @@ -55,6 +55,9 @@ Future setupFirstLaunch() async { } Future _migrateThemeSettings() async { + if (getCorePalette() == null) { + return; + } final prefs = LichessBinding.instance.sharedPreferences; try { final stored = LichessBinding.instance.sharedPreferences.getString( @@ -67,9 +70,7 @@ Future _migrateThemeSettings() async { final migrated = generalPrefs.copyWith( appThemeSeed: // ignore: deprecated_member_use_from_same_package - getCorePalette() != null && generalPrefs.systemColors == true - ? AppThemeSeed.system - : AppThemeSeed.board, + generalPrefs.systemColors == true ? AppThemeSeed.system : AppThemeSeed.board, ); await prefs.setString(PrefCategory.general.storageKey, jsonEncode(migrated.toJson())); } catch (e) {