diff --git a/lib/src/view/play/create_custom_game_screen.dart b/lib/src/view/play/create_custom_game_screen.dart index c24f8d954..bb66a9d9f 100644 --- a/lib/src/view/play/create_custom_game_screen.dart +++ b/lib/src/view/play/create_custom_game_screen.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:ui'; import 'package:deep_pick/deep_pick.dart'; import 'package:flutter/cupertino.dart'; @@ -53,14 +52,7 @@ class CreateCustomGameScreen extends StatelessWidget { } Widget _buildIos(BuildContext context) { - return CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar( - automaticBackgroundVisibility: false, - backgroundColor: CupertinoTheme.of(context).barBackgroundColor.withValues(alpha: 0.0), - border: null, - ), - child: const _CupertinoBody(), - ); + return const _CupertinoBody(); } Widget _buildAndroid(BuildContext context) { @@ -131,40 +123,13 @@ class _CupertinoBody extends StatefulWidget { class _CupertinoBodyState extends State<_CupertinoBody> { _ViewMode _selectedSegment = _ViewMode.create; - double headerOpacity = 0; void setViewMode(_ViewMode mode) { setState(() { _selectedSegment = mode; - headerOpacity = 0.0; }); } - 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; - } - @override Widget build(BuildContext context) { final tabSwitcher = CupertinoSlidingSegmentedControl<_ViewMode>( @@ -181,79 +146,30 @@ class _CupertinoBodyState extends State<_CupertinoBody> { } }, ); - return NotificationListener( - onNotification: handleScrollNotification, - child: - _selectedSegment == _ViewMode.create - ? _TabView( - cupertinoTabSwitcher: tabSwitcher, - cupertinoHeaderOpacity: headerOpacity, - sliver: _CreateGameBody(setViewMode: setViewMode), - ) - : _TabView( - cupertinoTabSwitcher: tabSwitcher, - cupertinoHeaderOpacity: headerOpacity, - sliver: _ChallengesBody(setViewMode: setViewMode), - ), + return CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + middle: Text(context.l10n.custom), + bottom: PreferredSize(preferredSize: const Size.fromHeight(44.0), child: tabSwitcher), + ), + child: _TabView( + sliver: + _selectedSegment == _ViewMode.create + ? _CreateGameBody(setViewMode: setViewMode) + : _ChallengesBody(setViewMode: setViewMode), + ), ); } } class _TabView extends StatelessWidget { - const _TabView({ - required this.sliver, - this.cupertinoTabSwitcher, - this.cupertinoHeaderOpacity = 0.0, - }); + const _TabView({required this.sliver}); final Widget sliver; - final Widget? cupertinoTabSwitcher; - final double cupertinoHeaderOpacity; @override Widget build(BuildContext context) { - final edgeInsets = - MediaQuery.paddingOf(context) - - (cupertinoTabSwitcher != null - ? EdgeInsets.only(top: MediaQuery.paddingOf(context).top) - : EdgeInsets.zero) + - Styles.verticalBodyPadding; - final backgroundColor = CupertinoTheme.of(context).barBackgroundColor; - return CustomScrollView( - slivers: [ - if (cupertinoTabSwitcher != null) - PinnedHeaderSliver( - child: ClipRect( - child: BackdropFilter( - enabled: backgroundColor.a != 1, - filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), - child: AnimatedContainer( - duration: const Duration(milliseconds: 200), - decoration: ShapeDecoration( - color: - cupertinoHeaderOpacity == 1.0 - ? backgroundColor - : backgroundColor.withAlpha(0), - shape: LinearBorder.bottom( - side: BorderSide( - color: - cupertinoHeaderOpacity == 1.0 - ? const Color(0x4D000000) - : Colors.transparent, - width: 0.0, - ), - ), - ), - padding: - Styles.bodyPadding + EdgeInsets.only(top: MediaQuery.paddingOf(context).top), - child: cupertinoTabSwitcher, - ), - ), - ), - ), - SliverPadding(padding: edgeInsets, sliver: sliver), - ], - ); + final edgeInsets = MediaQuery.paddingOf(context) + Styles.verticalBodyPadding; + return CustomScrollView(slivers: [SliverPadding(padding: edgeInsets, sliver: sliver)]); } } diff --git a/lib/src/view/settings/theme_settings_screen.dart b/lib/src/view/settings/theme_settings_screen.dart index 6d0e2d596..efaf8fe57 100644 --- a/lib/src/view/settings/theme_settings_screen.dart +++ b/lib/src/view/settings/theme_settings_screen.dart @@ -1,4 +1,3 @@ -import 'dart:ui' show ImageFilter; import 'package:chessground/chessground.dart'; import 'package:dartchess/dartchess.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart'; @@ -22,7 +21,7 @@ import 'package:lichess_mobile/src/widgets/list.dart'; import 'package:lichess_mobile/src/widgets/platform.dart'; import 'package:lichess_mobile/src/widgets/settings.dart'; -class ThemeSettingsScreen extends ConsumerWidget { +class ThemeSettingsScreen extends ConsumerStatefulWidget { const ThemeSettingsScreen({super.key}); static Route buildRoute(BuildContext context) { @@ -34,17 +33,220 @@ class ThemeSettingsScreen extends ConsumerWidget { } @override - Widget build(BuildContext context, WidgetRef ref) { + ConsumerState createState() => _ThemeSettingsScreenState(); +} + +class _ThemeSettingsScreenState extends ConsumerState { + 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); + final boardSize = isTabletOrLarger(context) ? 350.0 : 200.0; + + final bool hasAjustedColors = + brightness != kBoardDefaultBrightnessFilter || hue != kBoardDefaultHueFilter; + + final body = ListView( + padding: + Theme.of(context).platform == TargetPlatform.iOS + ? (MediaQuery.paddingOf(context) + + EdgeInsets.only(top: boardSize + 32.0 + kMinInteractiveDimensionCupertino)) + : null, + children: [ + ListSection( + hasLeading: true, + children: [ + if (getCorePalette() != null) + SwitchSettingTile( + leading: const Icon(Icons.colorize_outlined), + title: Text(context.l10n.mobileSystemColors), + value: generalPrefs.systemColors, + onChanged: (value) { + ref.read(generalPreferencesProvider.notifier).toggleSystemColors(); + }, + ), + SettingsListTile( + icon: const Icon(Icons.wallpaper), + settingsLabel: Text(context.l10n.background), + settingsValue: + generalPrefs.backgroundTheme?.label(context.l10n) ?? + (generalPrefs.backgroundImage != null ? 'Image' : 'Default'), + onTap: () { + Navigator.of(context).push(BackgroundChoiceScreen.buildRoute(context)); + }, + ), + if (generalPrefs.backgroundTheme != null || generalPrefs.backgroundImage != null) + PlatformListTile( + leading: const Icon(Icons.cancel), + title: const Text('Reset background'), + onTap: () { + ref + .read(generalPreferencesProvider.notifier) + .setBackground(backgroundTheme: null, backgroundImage: null); + }, + ), + SettingsListTile( + icon: const Icon(LichessIcons.chess_board), + settingsLabel: Text(context.l10n.board), + settingsValue: boardPrefs.boardTheme.label, + onTap: () { + Navigator.of(context).push(BoardChoiceScreen.buildRoute(context)); + }, + ), + SettingsListTile( + icon: const Icon(LichessIcons.chess_pawn), + settingsLabel: Text(context.l10n.pieceSet), + settingsValue: boardPrefs.pieceSet.label, + onTap: () { + Navigator.of(context).push(PieceSetScreen.buildRoute(context)); + }, + ), + 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( + header: SettingsSectionTitle(context.l10n.advancedSettings), + hasLeading: true, + children: [ + PlatformListTile( + leading: const Icon(Icons.brightness_6), + title: Slider.adaptive( + min: 0.2, + max: 1.4, + 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: 0.0, + max: 360.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 = kBoardDefaultBrightnessFilter; + hue = kBoardDefaultHueFilter; + }); + ref + .read(boardPreferencesProvider.notifier) + .adjustColors(brightness: brightness, hue: hue); + } + : null, + ), + ], + ), + ], + ); + + final boardPreview = PreferredSize( + preferredSize: Size.fromHeight(boardSize + 32.0), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: _BoardPreview( + size: boardSize, + boardPrefs: boardPrefs, + brightness: brightness, + hue: hue, + ), + ), + ); + return PlatformWidget( - androidBuilder: (context) => const Scaffold(body: _Body()), + androidBuilder: + (context) => Scaffold( + appBar: AppBar(title: Text(context.l10n.mobileTheme), bottom: boardPreview), + body: body, + ), iosBuilder: (context) => CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( - automaticBackgroundVisibility: false, - backgroundColor: CupertinoTheme.of(context).barBackgroundColor.withValues(alpha: 0.0), - border: null, + middle: Text(context.l10n.mobileTheme), + bottom: boardPreview, ), - child: const _Body(), + child: body, ), ); } @@ -59,293 +261,6 @@ switch (shapeColor) { ShapeColor.yellow => 'Yellow', }; -class _Body extends ConsumerStatefulWidget { - const _Body(); - - @override - ConsumerState<_Body> createState() => _BodyState(); -} - -class _BodyState extends ConsumerState<_Body> { - late double brightness; - late double hue; - - double headerOpacity = 0; - - bool openAdjustColorSection = false; - - @override - void initState() { - super.initState(); - final boardPrefs = ref.read(boardPreferencesProvider); - brightness = boardPrefs.brightness; - 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; - } - - @override - Widget build(BuildContext context) { - final generalPrefs = ref.watch(generalPreferencesProvider); - final boardPrefs = ref.watch(boardPreferencesProvider); - - final bool hasAjustedColors = - brightness != kBoardDefaultBrightnessFilter || hue != kBoardDefaultHueFilter; - - final boardSize = isTabletOrLarger(context) ? 350.0 : 200.0; - - final backgroundColor = CupertinoTheme.of(context).barBackgroundColor; - - return NotificationListener( - onNotification: handleScrollNotification, - child: CustomScrollView( - slivers: [ - if (Theme.of(context).platform == TargetPlatform.iOS) - PinnedHeaderSliver( - child: ClipRect( - child: BackdropFilter( - enabled: backgroundColor.a != 1, - 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( - size: boardSize, - boardPrefs: boardPrefs, - brightness: brightness, - hue: hue, - ), - ), - ), - ), - ) - else - SliverAppBar( - backgroundColor: Theme.of( - context, - ).appBarTheme.backgroundColor?.withValues(alpha: headerOpacity), - pinned: true, - title: Text(context.l10n.mobileTheme), - bottom: PreferredSize( - preferredSize: Size.fromHeight(boardSize + 16.0), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0), - child: _BoardPreview( - size: boardSize, - boardPrefs: boardPrefs, - brightness: brightness, - hue: hue, - ), - ), - ), - ), - SliverList.list( - children: [ - ListSection( - hasLeading: true, - children: [ - if (getCorePalette() != null) - SwitchSettingTile( - leading: const Icon(Icons.colorize_outlined), - title: Text(context.l10n.mobileSystemColors), - value: generalPrefs.systemColors, - onChanged: (value) { - ref.read(generalPreferencesProvider.notifier).toggleSystemColors(); - }, - ), - SettingsListTile( - icon: const Icon(Icons.wallpaper), - settingsLabel: Text(context.l10n.background), - settingsValue: - generalPrefs.backgroundTheme?.label(context.l10n) ?? - (generalPrefs.backgroundImage != null ? 'Image' : 'Default'), - onTap: () { - Navigator.of(context).push(BackgroundChoiceScreen.buildRoute(context)); - }, - ), - if (generalPrefs.backgroundTheme != null || generalPrefs.backgroundImage != null) - PlatformListTile( - leading: const Icon(Icons.cancel), - title: const Text('Reset background'), - onTap: () { - ref - .read(generalPreferencesProvider.notifier) - .setBackground(backgroundTheme: null, backgroundImage: null); - }, - ), - SettingsListTile( - icon: const Icon(LichessIcons.chess_board), - settingsLabel: Text(context.l10n.board), - settingsValue: boardPrefs.boardTheme.label, - onTap: () { - Navigator.of(context).push(BoardChoiceScreen.buildRoute(context)); - }, - ), - SettingsListTile( - icon: const Icon(LichessIcons.chess_pawn), - settingsLabel: Text(context.l10n.pieceSet), - settingsValue: boardPrefs.pieceSet.label, - onTap: () { - Navigator.of(context).push(PieceSetScreen.buildRoute(context)); - }, - ), - 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( - header: SettingsSectionTitle(context.l10n.advancedSettings), - hasLeading: true, - children: [ - PlatformListTile( - leading: const Icon(Icons.brightness_6), - title: Slider.adaptive( - min: 0.2, - max: 1.4, - 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: 0.0, - max: 360.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 = kBoardDefaultBrightnessFilter; - hue = kBoardDefaultHueFilter; - }); - ref - .read(boardPreferencesProvider.notifier) - .adjustColors(brightness: brightness, hue: hue); - } - : null, - ), - ], - ), - ], - ), - const SliverSafeArea( - top: false, - sliver: SliverToBoxAdapter(child: SizedBox(height: 16.0)), - ), - ], - ), - ); - } -} - class _BoardPreview extends StatelessWidget { const _BoardPreview({ required this.size,