From 96781880f490d7c2251a8209f1f79712fd3b1d8f Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Wed, 15 Feb 2023 20:14:32 +0100 Subject: [PATCH] feat(ui): move style implementation from component to ui_kit with theme resolver --- .../extensions/build_context_extensions.dart | 3 - .../lib/src/core/utils/multi_color.dart | 4 +- .../domain/entities/buttons/button_style.dart | 6 + .../buttons/file_selection_button_style.dart | 14 -- .../entities/buttons/flat_button_style.dart | 16 +- .../buttons/simple_icon_button_style.dart | 13 -- .../entities/buttons/symbol_button_style.dart | 14 -- packages/wyatt_ui_kit/README.md | 8 +- .../file_selection_button.dart | 5 + .../file_selection_button.g.dart | 1 + .../file_selection_button_screen.dart | 198 +++++++----------- .../file_selection_button_theme_resolver.dart | 59 ++++++ .../buttons/flat_button/flat_button.dart | 5 + .../buttons/flat_button/flat_button.g.dart | 1 + .../flat_button/flat_button_screen.dart | 83 ++++---- .../flat_button_theme_resolver.dart | 84 ++++++++ .../simple_icon_button.dart | 4 + .../simple_icon_button.g.dart | 1 + .../simple_icon_button_theme_resolver.dart | 57 +++++ .../simple_icon_screen.dart | 86 ++++---- .../buttons/symbol_button/symbol_button.dart | 5 + .../symbol_button/symbol_button.g.dart | 1 + .../symbol_button/symbol_button_screen.dart | 143 ++++++------- .../symbol_button_theme_resolver.dart | 59 ++++++ .../src/core/extensions/theme_extensions.dart | 3 + .../lib/src/core/helpers/theme_resolver.dart | 84 ++++++++ 26 files changed, 611 insertions(+), 346 deletions(-) create mode 100644 packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button_theme_resolver.dart create mode 100644 packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button_theme_resolver.dart create mode 100644 packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button_theme_resolver.dart create mode 100644 packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button_theme_resolver.dart create mode 100644 packages/wyatt_ui_kit/lib/src/core/helpers/theme_resolver.dart diff --git a/packages/wyatt_ui_components/lib/src/core/extensions/build_context_extensions.dart b/packages/wyatt_ui_components/lib/src/core/extensions/build_context_extensions.dart index c1184fae..be5e6045 100644 --- a/packages/wyatt_ui_components/lib/src/core/extensions/build_context_extensions.dart +++ b/packages/wyatt_ui_components/lib/src/core/extensions/build_context_extensions.dart @@ -19,7 +19,4 @@ import 'package:wyatt_ui_components/src/features/features.dart'; extension ThemeComponentBuildContext on BuildContext { ComponentThemeData get components => ComponentTheme.of(this); - TextTheme get textTheme => Theme.of(this).textTheme; - ColorScheme get colorScheme => Theme.of(this).colorScheme; - ButtonThemeData get buttonTheme => Theme.of(this).buttonTheme; } diff --git a/packages/wyatt_ui_components/lib/src/core/utils/multi_color.dart b/packages/wyatt_ui_components/lib/src/core/utils/multi_color.dart index c4f36156..8f36e970 100644 --- a/packages/wyatt_ui_components/lib/src/core/utils/multi_color.dart +++ b/packages/wyatt_ui_components/lib/src/core/utils/multi_color.dart @@ -1,4 +1,3 @@ -// ignore_for_file: public_member_api_docs, sort_constructors_first // Copyright (C) 2023 WYATT GROUP // Please see the AUTHORS file for details. // @@ -52,4 +51,7 @@ class MultiColor { } return b; } + + @override + String toString() => 'MultiColor(_colors: $_colors, _color: $_color)'; } diff --git a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/button_style.dart b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/button_style.dart index 6e0d8ee7..de126460 100644 --- a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/button_style.dart +++ b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/button_style.dart @@ -62,4 +62,10 @@ abstract class ButtonStyle { /// /// Default to `null` final BoxShadow? shadow; + + @override + String toString() => + 'ButtonStyle(radius: $radius, padding: $padding, foregroundColors: ' + '$foregroundColors, backgroundColors: $backgroundColors, borderColors: ' + '$borderColors, stroke: $stroke, shadow: $shadow)'; } diff --git a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/file_selection_button_style.dart b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/file_selection_button_style.dart index 5290e5c4..e543229b 100644 --- a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/file_selection_button_style.dart +++ b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/file_selection_button_style.dart @@ -18,7 +18,6 @@ import 'dart:ui'; import 'package:copy_with_extension/copy_with_extension.dart'; import 'package:flutter/widgets.dart'; -import 'package:wyatt_ui_components/src/core/extensions/build_context_extensions.dart'; import 'package:wyatt_ui_components/src/core/utils/multi_color.dart'; import 'package:wyatt_ui_components/src/domain/entities/buttons/button_style.dart'; @@ -38,19 +37,6 @@ class FileSelectionButtonStyle extends ButtonStyle { super.shadow, }); - /// Used in negociation to build a style from Flutter default values. - factory FileSelectionButtonStyle.fromFlutter(BuildContext context) => - FileSelectionButtonStyle( - title: context.textTheme.labelLarge, - subTitle: context.textTheme.labelSmall, - radius: (context.buttonTheme.shape is RoundedRectangleBorder) - ? (context.buttonTheme.shape as RoundedRectangleBorder).borderRadius - : null, - padding: context.buttonTheme.padding, - foregroundColors: MultiColor.single(context.colorScheme.onPrimary), - backgroundColors: MultiColor.single(context.colorScheme.primary), - ); - /// Used for interpolation. static FileSelectionButtonStyle? lerp( FileSelectionButtonStyle? a, diff --git a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/flat_button_style.dart b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/flat_button_style.dart index 7842365e..cc57472b 100644 --- a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/flat_button_style.dart +++ b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/flat_button_style.dart @@ -18,7 +18,6 @@ import 'dart:ui'; import 'package:copy_with_extension/copy_with_extension.dart'; import 'package:flutter/widgets.dart'; -import 'package:wyatt_ui_components/src/core/extensions/build_context_extensions.dart'; import 'package:wyatt_ui_components/src/core/utils/multi_color.dart'; import 'package:wyatt_ui_components/src/domain/entities/buttons/button_style.dart'; @@ -37,17 +36,6 @@ class FlatButtonStyle extends ButtonStyle { super.shadow, }); - /// Used in negociation to build a style from Flutter default values. - factory FlatButtonStyle.fromFlutter(BuildContext context) => FlatButtonStyle( - label: context.textTheme.labelLarge, - radius: (context.buttonTheme.shape is RoundedRectangleBorder) - ? (context.buttonTheme.shape as RoundedRectangleBorder).borderRadius - : null, - padding: context.buttonTheme.padding, - foregroundColors: MultiColor.single(context.colorScheme.onPrimary), - backgroundColors: MultiColor.single(context.colorScheme.primary), - ); - /// Used for interpolation. static FlatButtonStyle? lerp( FlatButtonStyle? a, @@ -86,4 +74,8 @@ class FlatButtonStyle extends ButtonStyle { /// /// Default to `TextTheme.labelLarge` final TextStyle? label; + + @override + String toString() => + 'FlatButtonStyle(label: $label), inherited: ${super.toString()}'; } diff --git a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/simple_icon_button_style.dart b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/simple_icon_button_style.dart index f559b956..3d468e63 100644 --- a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/simple_icon_button_style.dart +++ b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/simple_icon_button_style.dart @@ -18,7 +18,6 @@ import 'dart:ui'; import 'package:copy_with_extension/copy_with_extension.dart'; import 'package:flutter/widgets.dart'; -import 'package:wyatt_ui_components/src/core/extensions/build_context_extensions.dart'; import 'package:wyatt_ui_components/src/core/utils/multi_color.dart'; import 'package:wyatt_ui_components/src/domain/entities/buttons/button_style.dart'; @@ -37,18 +36,6 @@ class SimpleIconButtonStyle extends ButtonStyle { super.shadow, }); - /// Used in negociation to build a style from Flutter default values. - factory SimpleIconButtonStyle.fromFlutter(BuildContext context) => - SimpleIconButtonStyle( - dimension: context.buttonTheme.height, - radius: (context.buttonTheme.shape is RoundedRectangleBorder) - ? (context.buttonTheme.shape as RoundedRectangleBorder).borderRadius - : null, - padding: context.buttonTheme.padding, - foregroundColors: MultiColor.single(context.colorScheme.onPrimary), - backgroundColors: MultiColor.single(context.colorScheme.primary), - ); - /// Used for interpolation. static SimpleIconButtonStyle? lerp( SimpleIconButtonStyle? a, diff --git a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/symbol_button_style.dart b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/symbol_button_style.dart index 581f5ebc..ab5c44b8 100644 --- a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/symbol_button_style.dart +++ b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/symbol_button_style.dart @@ -18,7 +18,6 @@ import 'dart:ui'; import 'package:copy_with_extension/copy_with_extension.dart'; import 'package:flutter/widgets.dart'; -import 'package:wyatt_ui_components/src/core/extensions/build_context_extensions.dart'; import 'package:wyatt_ui_components/src/core/utils/multi_color.dart'; import 'package:wyatt_ui_components/src/domain/entities/buttons/button_style.dart'; @@ -38,19 +37,6 @@ class SymbolButtonStyle extends ButtonStyle { super.shadow, }); - /// Used in negociation to build a style from Flutter default values. - factory SymbolButtonStyle.fromFlutter(BuildContext context) => - SymbolButtonStyle( - label: context.textTheme.labelLarge, - dimension: context.buttonTheme.height, - radius: (context.buttonTheme.shape is RoundedRectangleBorder) - ? (context.buttonTheme.shape as RoundedRectangleBorder).borderRadius - : null, - padding: context.buttonTheme.padding, - foregroundColors: MultiColor.single(context.colorScheme.onPrimary), - backgroundColors: MultiColor.single(context.colorScheme.primary), - ); - /// Used for interpolation. static SymbolButtonStyle? lerp( SymbolButtonStyle? a, diff --git a/packages/wyatt_ui_kit/README.md b/packages/wyatt_ui_kit/README.md index 5f2c54c7..8ee9ac74 100644 --- a/packages/wyatt_ui_kit/README.md +++ b/packages/wyatt_ui_kit/README.md @@ -25,7 +25,7 @@ UIKit and Design System used in Wyatt Studio. -## Theme negociation +## Theme negotiation When building a component, most of its attributes can be 'null'. The `build()` method then starts to negotiate the theme in the tree to obtain the most consistent style possible. @@ -36,7 +36,11 @@ When you build a component `Button({double? radius})`. You have several possibilities: 1) Pass the "radius" into the constructor, `Button(radius: 12)`. 2) Set up a theme extension `ButtonThemeExtension(radius: 15)`. -3) Let `wyatt_ui_kit` "negotiate" and try to find a suitable style in the flutter theme. If this negotiation phase fails, then the style is simply not applied. +3) Let `wyatt_ui_kit` "negotiate" and try to find a suitable style in the flutter theme. + +If this negotiation phase fails, then: +- If the value is mandatory: a hardcoded value in "wyatt_ui_kit" is chosen. +- If not, the style is simply not applied. If, for example, you don't use option 1, then the radius will be 15. If you use neither option 1 nor option 2 then the radius will be 4 as this is the [official Material Design value](https://m2.material.io/design/shape/about-shape.html#shape-customization-tool). diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button.dart index 44759cc7..d7520b4e 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button.dart @@ -19,6 +19,7 @@ import 'package:wyatt_component_copy_with_extension/component_copy_with_extensio import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; import 'package:wyatt_ui_kit/src/components/buttons/cubit/invalid_button_cubit.dart'; import 'package:wyatt_ui_kit/src/components/buttons/file_selection_button/file_selection_button_screen.dart'; +import 'package:wyatt_ui_kit/src/components/buttons/file_selection_button/file_selection_button_theme_resolver.dart'; import 'package:wyatt_ui_kit/src/core/mixin/export_bloc_mixin.dart'; part 'file_selection_button.g.dart'; @@ -39,6 +40,7 @@ class FileSelectionButton extends FileSelectionButtonComponent super.invalidStyle, super.onPressed, super.mainAxisSize, + this.themeResolver, super.key, }); @@ -74,6 +76,8 @@ class FileSelectionButton extends FileSelectionButtonComponent @override FileSelectionButtonStyle? get invalidStyle => super.invalidStyle as FileSelectionButtonStyle?; + + final FileSelectionButtonThemeResolver? themeResolver; @override Widget build(BuildContext context) => exportBloc( @@ -90,6 +94,7 @@ class FileSelectionButton extends FileSelectionButtonComponent invalidStyle: invalidStyle, onPressed: onPressed, mainAxisSize: mainAxisSize, + themeResolver: themeResolver, key: key, ), ); diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button.g.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button.g.dart index 79759e3b..d4fdf1d7 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button.g.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button.g.dart @@ -75,6 +75,7 @@ class $FileSelectionButtonCWProxyImpl invalidStyle: invalidStyle ?? _value.invalidStyle, onPressed: onPressed ?? _value.onPressed, mainAxisSize: mainAxisSize ?? _value.mainAxisSize, + themeResolver: themeResolver ?? _value.themeResolver, key: key ?? _value.key, ); } diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button_screen.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button_screen.dart index 72196edf..a8a121bc 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button_screen.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button_screen.dart @@ -22,10 +22,9 @@ import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; import 'package:wyatt_ui_kit/src/components/buttons/cubit/button_cubit.dart'; import 'package:wyatt_ui_kit/src/components/buttons/cubit/invalid_button_cubit.dart'; +import 'package:wyatt_ui_kit/src/components/buttons/file_selection_button/file_selection_button_theme_resolver.dart'; import 'package:wyatt_ui_kit/src/components/gradients/gradient_text.dart'; -import 'package:wyatt_ui_kit/src/core/extensions/theme_extensions.dart'; import 'package:wyatt_ui_kit/src/core/helpers/linear_gradient_helper.dart'; -import 'package:wyatt_ui_kit/src/domain/button_theme_extension/file_selection_button_theme_extension.dart'; class FileSelectionButtonScreen extends CubitScreen { @@ -42,6 +41,7 @@ class FileSelectionButtonScreen this.invalidStyle, this.onPressed, this.mainAxisSize, + this.themeResolver, super.key, }); @@ -59,138 +59,80 @@ class FileSelectionButtonScreen final FileSelectionButtonStyle? invalidStyle; final void Function(ControlState state)? onPressed; + final FileSelectionButtonThemeResolver? themeResolver; @override InvalidButtonCubit create(BuildContext context) => InvalidButtonCubit(); /// Negotiate the theme to get a complete style. - FileSelectionButtonStyle negotiate(BuildContext context, ButtonState state) { - // Define default style from Flutter values. - FileSelectionButtonStyle style = - FileSelectionButtonStyle.fromFlutter(context); + FileSelectionButtonStyle resolve(BuildContext context, ButtonState state) { + final FileSelectionButtonThemeResolver resolver = themeResolver ?? + FileSelectionButtonThemeResolver( + computeExtensionValueFn: ( + context, + defaultValue, + themeExtension, { + extra, + }) { + FileSelectionButtonStyle? style = defaultValue; + switch (extra?.state) { + case ControlState.disabled: + style = themeExtension.disabledStyle; + break; + case ControlState.focused: + style = themeExtension.focusedStyle; + break; + case ControlState.hovered: + style = themeExtension.hoveredStyle; + break; + case ControlState.tapped: + style = themeExtension.tappedStyle; + break; + case ControlState.normal: + case null: + style = themeExtension.normalStyle; + break; + } + if (extra?.isSelected ?? false) { + style = themeExtension.selectedStyle; + } + if (extra?.isInvalid ?? false) { + style = themeExtension.invalidStyle; + } - // Try to retrieve custom theme extension - final fileSelectionButtonThemeExtension = - context.themeExtension(); + return style; + }, + customStyleFn: (context, {extra}) { + FileSelectionButtonStyle? style; + switch (extra?.state) { + case ControlState.disabled: + style = disabledStyle; + break; + case ControlState.focused: + style = focusedStyle; + break; + case ControlState.hovered: + style = hoveredStyle; + break; + case ControlState.tapped: + style = tappedStyle; + break; + case ControlState.normal: + case null: + style = normalStyle; + break; + } + if (extra?.isSelected ?? false) { + style = selectedStyle; + } + if (extra?.isInvalid ?? false) { + style = invalidStyle; + } - switch (state.state) { - case ControlState.disabled: - style = disabledStyle ?? - fileSelectionButtonThemeExtension?.disabledStyle ?? - style.copyWith( - foregroundColors: - MultiColor.single(context.colorScheme.onSurface), - backgroundColors: MultiColor.single(context.colorScheme.surface), - ); - break; - case ControlState.hovered: - style = hoveredStyle ?? - fileSelectionButtonThemeExtension?.hoveredStyle ?? - style; - break; - case ControlState.tapped: - style = tappedStyle ?? - fileSelectionButtonThemeExtension?.tappedStyle ?? - style; - break; - case ControlState.focused: - style = focusedStyle ?? - fileSelectionButtonThemeExtension?.focusedStyle ?? - style; - break; - case ControlState.normal: - style = normalStyle ?? - fileSelectionButtonThemeExtension?.normalStyle ?? - style; - break; - } - - // Apply extra theme - if (state.isSelected) { - // TODO(hpcl): enhance copyWith to copy only non-null attributes of an object - style = style.copyWith( - title: (selectedStyle ?? - fileSelectionButtonThemeExtension?.selectedStyle ?? - style) - .title, - subTitle: (selectedStyle ?? - fileSelectionButtonThemeExtension?.selectedStyle ?? - style) - .subTitle, - radius: (selectedStyle ?? - fileSelectionButtonThemeExtension?.selectedStyle ?? - style) - .radius, - padding: (selectedStyle ?? - fileSelectionButtonThemeExtension?.selectedStyle ?? - style) - .padding, - foregroundColors: (selectedStyle ?? - fileSelectionButtonThemeExtension?.selectedStyle ?? - style) - .foregroundColors, - backgroundColors: (selectedStyle ?? - fileSelectionButtonThemeExtension?.selectedStyle ?? - style) - .backgroundColors, - borderColors: (selectedStyle ?? - fileSelectionButtonThemeExtension?.selectedStyle ?? - style) - .borderColors, - stroke: (selectedStyle ?? - fileSelectionButtonThemeExtension?.selectedStyle ?? - style) - .stroke, - shadow: (selectedStyle ?? - fileSelectionButtonThemeExtension?.selectedStyle ?? - style) - .shadow, - ); - } - - if (state.isInvalid) { - // TODO(hpcl): enhance copyWith to copy only non-null attributes of an object - style = style.copyWith( - title: (invalidStyle ?? - fileSelectionButtonThemeExtension?.invalidStyle ?? - style) - .title, - subTitle: (invalidStyle ?? - fileSelectionButtonThemeExtension?.invalidStyle ?? - style) - .subTitle, - radius: (invalidStyle ?? - fileSelectionButtonThemeExtension?.invalidStyle ?? - style) - .radius, - padding: (invalidStyle ?? - fileSelectionButtonThemeExtension?.invalidStyle ?? - style) - .padding, - foregroundColors: (invalidStyle ?? - fileSelectionButtonThemeExtension?.invalidStyle ?? - style) - .foregroundColors, - backgroundColors: (invalidStyle ?? - fileSelectionButtonThemeExtension?.invalidStyle ?? - style) - .backgroundColors, - borderColors: (invalidStyle ?? - fileSelectionButtonThemeExtension?.invalidStyle ?? - style) - .borderColors, - stroke: (invalidStyle ?? - fileSelectionButtonThemeExtension?.invalidStyle ?? - style) - .stroke, - shadow: (invalidStyle ?? - fileSelectionButtonThemeExtension?.invalidStyle ?? - style) - .shadow, - ); - } - - return style; + return style; + }, + ); + return resolver.negotiate(context, extra: state); } Widget _border( @@ -217,7 +159,7 @@ class FileSelectionButtonScreen @override Widget onBuild(BuildContext context, ButtonState state) { - final style = negotiate(context, state); + final style = resolve(context, state); return Focus( onFocusChange: (hasFocus) => diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button_theme_resolver.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button_theme_resolver.dart new file mode 100644 index 00000000..8d326abf --- /dev/null +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button_theme_resolver.dart @@ -0,0 +1,59 @@ +// Copyright (C) 2023 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:flutter/material.dart'; +import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; +import 'package:wyatt_ui_kit/src/components/buttons/cubit/button_cubit.dart'; +import 'package:wyatt_ui_kit/src/core/helpers/theme_resolver.dart'; +import 'package:wyatt_ui_kit/wyatt_ui_kit.dart'; + +class FileSelectionButtonThemeResolver extends ThemeResolver< + FileSelectionButtonStyle, FileSelectionButtonThemeExtension, ButtonState> { + const FileSelectionButtonThemeResolver({ + required this.computeExtensionValueFn, + required this.customStyleFn, + }); + + @override + FileSelectionButtonStyle computeDefaultValue( + BuildContext context, { + ButtonState? extra, + }) => + FileSelectionButtonStyle( + title: context.textTheme.labelLarge, + subTitle: context.textTheme.labelSmall, + radius: (context.buttonTheme.shape is RoundedRectangleBorder) + ? (context.buttonTheme.shape as RoundedRectangleBorder).borderRadius + : null, + padding: context.buttonTheme.padding, + foregroundColors: MultiColor.single(context.colorScheme.onPrimary), + backgroundColors: MultiColor.single(context.colorScheme.primary), + ); + + @override + final FileSelectionButtonStyle? Function( + BuildContext context, + FileSelectionButtonStyle defaultValue, + FileSelectionButtonThemeExtension themeExtension, { + ButtonState? extra, + }) computeExtensionValueFn; + + @override + final FileSelectionButtonStyle? Function( + BuildContext context, { + ButtonState? extra, + }) customStyleFn; +} diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button.dart index 4821a2c3..e5cf5093 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button.dart @@ -19,6 +19,7 @@ import 'package:wyatt_component_copy_with_extension/component_copy_with_extensio import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; import 'package:wyatt_ui_kit/src/components/buttons/cubit/button_cubit.dart'; import 'package:wyatt_ui_kit/src/components/buttons/flat_button/flat_button_screen.dart'; +import 'package:wyatt_ui_kit/src/components/buttons/flat_button/flat_button_theme_resolver.dart'; import 'package:wyatt_ui_kit/src/core/mixin/export_bloc_mixin.dart'; part 'flat_button.g.dart'; @@ -37,6 +38,7 @@ class FlatButton extends FlatButtonComponent super.tappedStyle, super.onPressed, super.mainAxisSize, + this.themeResolver, super.key, }); @@ -60,6 +62,8 @@ class FlatButton extends FlatButtonComponent @override FlatButtonStyle? get tappedStyle => super.tappedStyle as FlatButtonStyle?; + final FlatButtonThemeResolver? themeResolver; + @override Widget build(BuildContext context) => exportBloc( child: FlatButtonScreen( @@ -73,6 +77,7 @@ class FlatButton extends FlatButtonComponent tappedStyle: tappedStyle, onPressed: onPressed, mainAxisSize: mainAxisSize, + themeResolver: themeResolver, key: key, ), ); diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button.g.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button.g.dart index 87ed36e1..cf5fdf0f 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button.g.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button.g.dart @@ -63,6 +63,7 @@ class $FlatButtonCWProxyImpl implements $FlatButtonComponentCWProxy { tappedStyle: tappedStyle ?? _value.tappedStyle, onPressed: onPressed ?? _value.onPressed, mainAxisSize: mainAxisSize ?? _value.mainAxisSize, + themeResolver: themeResolver ?? _value.themeResolver, key: key ?? _value.key, ); } diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button_screen.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button_screen.dart index 2ef0d172..b0803388 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button_screen.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button_screen.dart @@ -20,11 +20,10 @@ import 'package:gap/gap.dart'; import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; import 'package:wyatt_ui_kit/src/components/buttons/cubit/button_cubit.dart'; +import 'package:wyatt_ui_kit/src/components/buttons/flat_button/flat_button_theme_resolver.dart'; import 'package:wyatt_ui_kit/src/components/gradients/gradient_box_border.dart'; import 'package:wyatt_ui_kit/src/components/gradients/gradient_text.dart'; -import 'package:wyatt_ui_kit/src/core/extensions/theme_extensions.dart'; import 'package:wyatt_ui_kit/src/core/helpers/linear_gradient_helper.dart'; -import 'package:wyatt_ui_kit/src/domain/button_theme_extension/flat_button_theme_extension.dart'; class FlatButtonScreen extends CubitScreen { const FlatButtonScreen({ @@ -38,6 +37,7 @@ class FlatButtonScreen extends CubitScreen { this.tappedStyle, this.onPressed, this.mainAxisSize, + this.themeResolver, super.key, }); @@ -53,49 +53,57 @@ class FlatButtonScreen extends CubitScreen { final FlatButtonStyle? tappedStyle; final void Function(ControlState state)? onPressed; + final FlatButtonThemeResolver? themeResolver; @override ButtonCubit create(BuildContext context) => ButtonCubit(); /// Negotiate the theme to get a complete style. - FlatButtonStyle negotiate(BuildContext context, ControlState state) { - // Define default style from Flutter values. - FlatButtonStyle style = FlatButtonStyle.fromFlutter(context); - - // Try to retrieve custom theme extension - final flatButtonThemeExtension = - context.themeExtension(); - - switch (state) { - case ControlState.disabled: - style = disabledStyle ?? - flatButtonThemeExtension?.disabledStyle ?? - style.copyWith( - foregroundColors: - MultiColor.single(context.colorScheme.onSurface), - backgroundColors: MultiColor.single(context.colorScheme.surface), - ); - break; - case ControlState.hovered: - style = hoveredStyle ?? flatButtonThemeExtension?.hoveredStyle ?? style; - break; - case ControlState.tapped: - style = tappedStyle ?? flatButtonThemeExtension?.tappedStyle ?? style; - break; - case ControlState.focused: - style = focusedStyle ?? flatButtonThemeExtension?.focusedStyle ?? style; - break; - case ControlState.normal: - style = normalStyle ?? flatButtonThemeExtension?.normalStyle ?? style; - break; - } - - return style; + FlatButtonStyle resolve(BuildContext context, ControlState state) { + final FlatButtonThemeResolver resolver = themeResolver ?? + FlatButtonThemeResolver( + computeExtensionValueFn: ( + context, + defaultValue, + themeExtension, { + extra, + }) { + switch (extra) { + case ControlState.disabled: + return themeExtension.disabledStyle; + case ControlState.focused: + return themeExtension.focusedStyle; + case ControlState.hovered: + return themeExtension.hoveredStyle; + case ControlState.tapped: + return themeExtension.tappedStyle; + case ControlState.normal: + case null: + return themeExtension.normalStyle; + } + }, + customStyleFn: (context, {extra}) { + switch (extra) { + case ControlState.disabled: + return disabledStyle; + case ControlState.focused: + return focusedStyle; + case ControlState.hovered: + return hoveredStyle; + case ControlState.tapped: + return tappedStyle; + case ControlState.normal: + case null: + return normalStyle; + } + }, + ); + return resolver.negotiate(context, extra: state); } @override Widget onBuild(BuildContext context, ButtonState state) { - final style = negotiate(context, state.state); + final style = resolve(context, state.state); return Focus( onFocusChange: (hasFocus) => @@ -182,7 +190,6 @@ class FlatButtonScreen extends CubitScreen { Gap(style.padding?.vertical ?? 10), /// Choose color - /// label.style.color ?? /// buttonStyle.label.style.color ?? /// context.textTheme.labelLarge.color /// @@ -191,7 +198,7 @@ class FlatButtonScreen extends CubitScreen { /// buttonStyle.foregroundColor.colors ?? /// null /// - /// More infos in `negociate()` method + /// More infos in ThemeResolver class if (label != null) ...[ Text( label!.text, diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button_theme_resolver.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button_theme_resolver.dart new file mode 100644 index 00000000..3e125b21 --- /dev/null +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button_theme_resolver.dart @@ -0,0 +1,84 @@ +// Copyright (C) 2023 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:flutter/material.dart'; +import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; +import 'package:wyatt_ui_kit/src/core/helpers/theme_resolver.dart'; +import 'package:wyatt_ui_kit/wyatt_ui_kit.dart'; + +class FlatButtonThemeResolver extends ThemeResolver { + const FlatButtonThemeResolver({ + required this.computeExtensionValueFn, + required this.customStyleFn, + }); + + @override + + /// Values taken from + FlatButtonStyle computeDefaultValue( + BuildContext context, { + ControlState? extra, + }) { + MultiColor backgroundColor = MultiColor.single(context.colorScheme.primary); + MultiColor foregroundColor = + MultiColor.single(context.colorScheme.onPrimary); + + switch (extra) { + case ControlState.disabled: + backgroundColor = + MultiColor.single(context.colorScheme.onSurface.withOpacity(0.12)); + foregroundColor = + MultiColor.single(context.colorScheme.onSurface.withOpacity(0.38)); + break; + case ControlState.hovered: + backgroundColor = + MultiColor.single(context.colorScheme.primary.withOpacity(0.92)); + break; + case ControlState.tapped: + backgroundColor = + MultiColor.single(context.colorScheme.primary.withOpacity(0.92)); + break; + case null: + case ControlState.normal: + case ControlState.focused: + break; + } + + return FlatButtonStyle( + label: + context.textTheme.labelLarge?.copyWith(color: foregroundColor.color), + radius: (context.buttonTheme.shape is RoundedRectangleBorder) + ? (context.buttonTheme.shape as RoundedRectangleBorder).borderRadius + : null, + padding: context.buttonTheme.padding, + foregroundColors: foregroundColor, + backgroundColors: backgroundColor, + ); + } + + @override + final FlatButtonStyle? Function( + BuildContext context, + FlatButtonStyle defaultValue, + FlatButtonThemeExtension themeExtension, { + ControlState? extra, + }) computeExtensionValueFn; + + @override + final FlatButtonStyle? Function(BuildContext context, {ControlState? extra}) + customStyleFn; +} diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button.dart index 76a6b4bb..6913ab37 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button.dart @@ -18,6 +18,7 @@ import 'package:flutter/material.dart' hide ButtonStyle; import 'package:wyatt_component_copy_with_extension/component_copy_with_extension.dart'; import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; import 'package:wyatt_ui_kit/src/components/buttons/cubit/button_cubit.dart'; +import 'package:wyatt_ui_kit/src/components/buttons/simple_icon_button/simple_icon_button_theme_resolver.dart'; import 'package:wyatt_ui_kit/src/components/buttons/simple_icon_button/simple_icon_screen.dart'; import 'package:wyatt_ui_kit/src/core/mixin/export_bloc_mixin.dart'; @@ -34,6 +35,7 @@ class SimpleIconButton extends SimpleIconButtonComponent super.focusedStyle, super.tappedStyle, super.onPressed, + this.themeResolver, super.key, }); @@ -62,6 +64,7 @@ class SimpleIconButton extends SimpleIconButtonComponent SimpleIconButtonStyle? get tappedStyle => super.tappedStyle as SimpleIconButtonStyle?; + final SimpleIconButtonThemeResolver? themeResolver; @override Widget build(BuildContext context) => exportBloc( @@ -73,6 +76,7 @@ class SimpleIconButton extends SimpleIconButtonComponent focusedStyle: focusedStyle, tappedStyle: tappedStyle, onPressed: onPressed, + themeResolver: themeResolver, key: key, ), ); diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button.g.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button.g.dart index b846f4f6..31464a10 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button.g.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button.g.dart @@ -51,6 +51,7 @@ class $SimpleIconButtonCWProxyImpl focusedStyle: focusedStyle ?? _value.focusedStyle, tappedStyle: tappedStyle ?? _value.tappedStyle, onPressed: onPressed ?? _value.onPressed, + themeResolver: themeResolver ?? _value.themeResolver, key: key ?? _value.key, ); } diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button_theme_resolver.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button_theme_resolver.dart new file mode 100644 index 00000000..a6dc37ba --- /dev/null +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button_theme_resolver.dart @@ -0,0 +1,57 @@ +// Copyright (C) 2023 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:flutter/material.dart'; +import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; +import 'package:wyatt_ui_kit/src/core/helpers/theme_resolver.dart'; +import 'package:wyatt_ui_kit/wyatt_ui_kit.dart'; + +class SimpleIconButtonThemeResolver extends ThemeResolver { + const SimpleIconButtonThemeResolver({ + required this.computeExtensionValueFn, + required this.customStyleFn, + }); + + @override + SimpleIconButtonStyle computeDefaultValue( + BuildContext context, { + ControlState? extra, + }) => + SimpleIconButtonStyle( + dimension: context.buttonTheme.height, + radius: (context.buttonTheme.shape is RoundedRectangleBorder) + ? (context.buttonTheme.shape as RoundedRectangleBorder).borderRadius + : null, + padding: context.buttonTheme.padding, + foregroundColors: MultiColor.single(context.colorScheme.onPrimary), + backgroundColors: MultiColor.single(context.colorScheme.primary), + ); + + @override + final SimpleIconButtonStyle? Function( + BuildContext context, + SimpleIconButtonStyle defaultValue, + SimpleIconButtonThemeExtension themeExtension, { + ControlState? extra, + }) computeExtensionValueFn; + + @override + final SimpleIconButtonStyle? Function( + BuildContext context, { + ControlState? extra, + }) customStyleFn; +} diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_screen.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_screen.dart index bceab017..13ea6b8b 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_screen.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_screen.dart @@ -19,11 +19,10 @@ import 'package:flutter/services.dart'; import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; import 'package:wyatt_ui_kit/src/components/buttons/cubit/button_cubit.dart'; +import 'package:wyatt_ui_kit/src/components/buttons/simple_icon_button/simple_icon_button_theme_resolver.dart'; import 'package:wyatt_ui_kit/src/components/gradients/gradient_box_border.dart'; import 'package:wyatt_ui_kit/src/components/gradients/gradient_icon.dart'; -import 'package:wyatt_ui_kit/src/core/extensions/theme_extensions.dart'; import 'package:wyatt_ui_kit/src/core/helpers/linear_gradient_helper.dart'; -import 'package:wyatt_ui_kit/src/domain/button_theme_extension/simple_icon_button_theme_extension.dart'; class SimpleIconButtonScreen extends CubitScreen { const SimpleIconButtonScreen({ @@ -34,6 +33,7 @@ class SimpleIconButtonScreen extends CubitScreen { this.focusedStyle, this.tappedStyle, this.onPressed, + this.themeResolver, super.key, }); @@ -46,55 +46,57 @@ class SimpleIconButtonScreen extends CubitScreen { final SimpleIconButtonStyle? tappedStyle; final void Function(ControlState state)? onPressed; + final SimpleIconButtonThemeResolver? themeResolver; @override ButtonCubit create(BuildContext context) => ButtonCubit(); /// Negotiate the theme to get a complete style. - SimpleIconButtonStyle negotiate(BuildContext context, ControlState state) { - // Define default style from Flutter values. - SimpleIconButtonStyle style = SimpleIconButtonStyle.fromFlutter(context); - - // Try to retrieve custom theme extension - final simpleIconButtonThemeExtension = - context.themeExtension(); - - switch (state) { - case ControlState.disabled: - style = disabledStyle ?? - simpleIconButtonThemeExtension?.disabledStyle ?? - style.copyWith( - foregroundColors: - MultiColor.single(context.colorScheme.onSurface), - backgroundColors: MultiColor.single(context.colorScheme.surface), - ); - break; - case ControlState.hovered: - style = hoveredStyle ?? - simpleIconButtonThemeExtension?.hoveredStyle ?? - style; - break; - case ControlState.tapped: - style = - tappedStyle ?? simpleIconButtonThemeExtension?.tappedStyle ?? style; - break; - case ControlState.focused: - style = focusedStyle ?? - simpleIconButtonThemeExtension?.focusedStyle ?? - style; - break; - case ControlState.normal: - style = - normalStyle ?? simpleIconButtonThemeExtension?.normalStyle ?? style; - break; - } - - return style; + SimpleIconButtonStyle resolve(BuildContext context, ControlState state) { + final SimpleIconButtonThemeResolver resolver = themeResolver ?? + SimpleIconButtonThemeResolver( + computeExtensionValueFn: ( + context, + defaultValue, + themeExtension, { + extra, + }) { + switch (extra) { + case ControlState.disabled: + return themeExtension.disabledStyle; + case ControlState.focused: + return themeExtension.focusedStyle; + case ControlState.hovered: + return themeExtension.hoveredStyle; + case ControlState.tapped: + return themeExtension.tappedStyle; + case ControlState.normal: + case null: + return themeExtension.normalStyle; + } + }, + customStyleFn: (context, {extra}) { + switch (extra) { + case ControlState.disabled: + return disabledStyle; + case ControlState.focused: + return focusedStyle; + case ControlState.hovered: + return hoveredStyle; + case ControlState.tapped: + return tappedStyle; + case ControlState.normal: + case null: + return normalStyle; + } + }, + ); + return resolver.negotiate(context, extra: state); } @override Widget onBuild(BuildContext context, ButtonState state) { - final style = negotiate(context, state.state); + final style = resolve(context, state.state); return Focus( onFocusChange: (hasFocus) => diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.dart index 04a44790..9a4f9c4e 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.dart @@ -19,6 +19,7 @@ import 'package:wyatt_component_copy_with_extension/component_copy_with_extensio import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; import 'package:wyatt_ui_kit/src/components/buttons/cubit/selectable_button_cubit.dart'; import 'package:wyatt_ui_kit/src/components/buttons/symbol_button/symbol_button_screen.dart'; +import 'package:wyatt_ui_kit/src/components/buttons/symbol_button/symbol_button_theme_resolver.dart'; import 'package:wyatt_ui_kit/src/core/mixin/export_bloc_mixin.dart'; part 'symbol_button.g.dart'; @@ -36,6 +37,7 @@ class SymbolButton extends SymbolButtonComponent super.tappedStyle, super.selectedStyle, super.mainAxisSize, + this.themeResolver, super.onPressed, super.key, }); @@ -67,6 +69,8 @@ class SymbolButton extends SymbolButtonComponent SymbolButtonStyle? get selectedStyle => super.selectedStyle as SymbolButtonStyle?; + final SymbolButtonThemeResolver? themeResolver; + @override Widget build(BuildContext context) => exportBloc( child: SymbolButtonScreen( @@ -80,6 +84,7 @@ class SymbolButton extends SymbolButtonComponent selectedStyle: selectedStyle, onPressed: onPressed, mainAxisSize: mainAxisSize, + themeResolver: themeResolver, key: key, ), ); diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.g.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.g.dart index f6f8d9ca..2f0fd363 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.g.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.g.dart @@ -63,6 +63,7 @@ class $SymbolButtonCWProxyImpl implements $SymbolButtonComponentCWProxy { tappedStyle: tappedStyle ?? _value.tappedStyle, selectedStyle: selectedStyle ?? _value.selectedStyle, mainAxisSize: mainAxisSize ?? _value.mainAxisSize, + themeResolver: themeResolver ?? _value.themeResolver, onPressed: onPressed ?? _value.onPressed, key: key ?? _value.key, ); diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button_screen.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button_screen.dart index e199ebe0..2491b496 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button_screen.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button_screen.dart @@ -21,11 +21,10 @@ import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; import 'package:wyatt_ui_kit/src/components/buttons/cubit/button_cubit.dart'; import 'package:wyatt_ui_kit/src/components/buttons/cubit/selectable_button_cubit.dart'; +import 'package:wyatt_ui_kit/src/components/buttons/symbol_button/symbol_button_theme_resolver.dart'; import 'package:wyatt_ui_kit/src/components/gradients/gradient_box_border.dart'; import 'package:wyatt_ui_kit/src/components/gradients/gradient_text.dart'; -import 'package:wyatt_ui_kit/src/core/extensions/theme_extensions.dart'; import 'package:wyatt_ui_kit/src/core/helpers/linear_gradient_helper.dart'; -import 'package:wyatt_ui_kit/src/domain/button_theme_extension/symbol_button_theme_extension.dart'; class SymbolButtonScreen extends CubitScreen { @@ -40,6 +39,7 @@ class SymbolButtonScreen this.selectedStyle, this.onPressed, this.mainAxisSize, + this.themeResolver, super.key, }); @@ -55,94 +55,79 @@ class SymbolButtonScreen final SymbolButtonStyle? selectedStyle; final void Function(ControlState state)? onPressed; + final SymbolButtonThemeResolver? themeResolver; @override SelectableButtonCubit create(BuildContext context) => SelectableButtonCubit(); /// Negotiate the theme to get a complete style. - SymbolButtonStyle negotiate(BuildContext context, ButtonState state) { - // Define default style from Flutter values. - SymbolButtonStyle style = SymbolButtonStyle.fromFlutter(context); + SymbolButtonStyle resolve(BuildContext context, ButtonState state) { + final SymbolButtonThemeResolver resolver = themeResolver ?? + SymbolButtonThemeResolver( + computeExtensionValueFn: ( + context, + defaultValue, + themeExtension, { + extra, + }) { + SymbolButtonStyle? style = defaultValue; + switch (extra?.state) { + case ControlState.disabled: + style = themeExtension.disabledStyle; + break; + case ControlState.focused: + style = themeExtension.focusedStyle; + break; + case ControlState.hovered: + style = themeExtension.hoveredStyle; + break; + case ControlState.tapped: + style = themeExtension.tappedStyle; + break; + case ControlState.normal: + case null: + style = themeExtension.normalStyle; + break; + } + if (extra?.isSelected ?? false) { + style = themeExtension.selectedStyle; + } - // Try to retrieve custom theme extension - final symbolButtonThemeExtension = - context.themeExtension(); + return style; + }, + customStyleFn: (context, {extra}) { + SymbolButtonStyle? style; + switch (extra?.state) { + case ControlState.disabled: + style = disabledStyle; + break; + case ControlState.focused: + style = focusedStyle; + break; + case ControlState.hovered: + style = hoveredStyle; + break; + case ControlState.tapped: + style = tappedStyle; + break; + case ControlState.normal: + case null: + style = normalStyle; + break; + } + if (extra?.isSelected ?? false) { + style = selectedStyle; + } - switch (state.state) { - case ControlState.disabled: - style = disabledStyle ?? - symbolButtonThemeExtension?.disabledStyle ?? - style.copyWith( - foregroundColors: - MultiColor.single(context.colorScheme.onSurface), - backgroundColors: MultiColor.single(context.colorScheme.surface), - ); - break; - case ControlState.hovered: - style = - hoveredStyle ?? symbolButtonThemeExtension?.hoveredStyle ?? style; - break; - case ControlState.tapped: - style = tappedStyle ?? symbolButtonThemeExtension?.tappedStyle ?? style; - break; - case ControlState.focused: - style = - focusedStyle ?? symbolButtonThemeExtension?.focusedStyle ?? style; - break; - case ControlState.normal: - style = normalStyle ?? symbolButtonThemeExtension?.normalStyle ?? style; - break; - } - - // Apply extra theme - if (state.isSelected) { - // TODO(hpcl): enhance copyWith to copy only non-null attributes of an object - style = style.copyWith( - label: (selectedStyle ?? - symbolButtonThemeExtension?.selectedStyle ?? - style) - .label, - dimension: (selectedStyle ?? - symbolButtonThemeExtension?.selectedStyle ?? - style) - .dimension, - radius: (selectedStyle ?? - symbolButtonThemeExtension?.selectedStyle ?? - style) - .radius, - padding: (selectedStyle ?? - symbolButtonThemeExtension?.selectedStyle ?? - style) - .padding, - foregroundColors: (selectedStyle ?? - symbolButtonThemeExtension?.selectedStyle ?? - style) - .foregroundColors, - backgroundColors: (selectedStyle ?? - symbolButtonThemeExtension?.selectedStyle ?? - style) - .backgroundColors, - borderColors: (selectedStyle ?? - symbolButtonThemeExtension?.selectedStyle ?? - style) - .borderColors, - stroke: (selectedStyle ?? - symbolButtonThemeExtension?.selectedStyle ?? - style) - .stroke, - shadow: (selectedStyle ?? - symbolButtonThemeExtension?.selectedStyle ?? - style) - .shadow, - ); - } - - return style; + return style; + }, + ); + return resolver.negotiate(context, extra: state); } @override Widget onBuild(BuildContext context, ButtonState state) { - final style = negotiate(context, state); + final style = resolve(context, state); return Focus( onFocusChange: (hasFocus) => diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button_theme_resolver.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button_theme_resolver.dart new file mode 100644 index 00000000..ed446330 --- /dev/null +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button_theme_resolver.dart @@ -0,0 +1,59 @@ +// Copyright (C) 2023 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:flutter/material.dart'; +import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; +import 'package:wyatt_ui_kit/src/components/buttons/cubit/button_cubit.dart'; +import 'package:wyatt_ui_kit/src/core/helpers/theme_resolver.dart'; +import 'package:wyatt_ui_kit/wyatt_ui_kit.dart'; + +class SymbolButtonThemeResolver extends ThemeResolver { + const SymbolButtonThemeResolver({ + required this.computeExtensionValueFn, + required this.customStyleFn, + }); + + @override + SymbolButtonStyle computeDefaultValue( + BuildContext context, { + ButtonState? extra, + }) => + SymbolButtonStyle( + label: context.textTheme.labelLarge, + dimension: context.buttonTheme.height, + radius: (context.buttonTheme.shape is RoundedRectangleBorder) + ? (context.buttonTheme.shape as RoundedRectangleBorder).borderRadius + : null, + padding: context.buttonTheme.padding, + foregroundColors: MultiColor.single(context.colorScheme.onPrimary), + backgroundColors: MultiColor.single(context.colorScheme.primary), + ); + + @override + final SymbolButtonStyle? Function( + BuildContext context, + SymbolButtonStyle defaultValue, + SymbolButtonThemeExtension themeExtension, { + ButtonState? extra, + }) computeExtensionValueFn; + + @override + final SymbolButtonStyle? Function( + BuildContext context, { + ButtonState? extra, + }) customStyleFn; +} diff --git a/packages/wyatt_ui_kit/lib/src/core/extensions/theme_extensions.dart b/packages/wyatt_ui_kit/lib/src/core/extensions/theme_extensions.dart index 021dca70..5e39a69c 100644 --- a/packages/wyatt_ui_kit/lib/src/core/extensions/theme_extensions.dart +++ b/packages/wyatt_ui_kit/lib/src/core/extensions/theme_extensions.dart @@ -18,4 +18,7 @@ import 'package:flutter/material.dart'; extension BuildContextThemeExtension on BuildContext { T? themeExtension() => Theme.of(this).extension(); + TextTheme get textTheme => Theme.of(this).textTheme; + ColorScheme get colorScheme => Theme.of(this).colorScheme; + ButtonThemeData get buttonTheme => Theme.of(this).buttonTheme; } diff --git a/packages/wyatt_ui_kit/lib/src/core/helpers/theme_resolver.dart b/packages/wyatt_ui_kit/lib/src/core/helpers/theme_resolver.dart new file mode 100644 index 00000000..f54aeaa3 --- /dev/null +++ b/packages/wyatt_ui_kit/lib/src/core/helpers/theme_resolver.dart @@ -0,0 +1,84 @@ +// Copyright (C) 2023 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:flutter/material.dart'; +import 'package:wyatt_ui_kit/src/core/extensions/theme_extensions.dart'; + +/// {@template theme_resolver} +/// In charge of theme negotiation and merge. +/// +/// When you build a component `Component({double? radius})`. +/// You have several possibilities: +/// 1) Pass the "radius" into the constructor, `Component(radius: 12)`. +/// 2) Set up a theme extension `ComponentThemeExtension(radius: 15)`. +/// 3) Let `wyatt_ui_kit` "negotiate" and try to find a suitable style in the +/// flutter theme. +/// +/// If this negotiation phase fails, then: +/// - If the value is mandatory: a hardcoded value in "wyatt_ui_kit" is chosen. +/// - If not, the style is simply not applied. +/// {@endtemplate} +abstract class ThemeResolver { + /// {@macro theme_resolver} + const ThemeResolver(); + + S? Function( + BuildContext context, + S defaultValue, + T themeExtension, { + E? extra, + }) get computeExtensionValueFn; + S? Function(BuildContext context, {E? extra}) get customStyleFn; + + /// Compute default value from Flutter Theme or with hardcoded values. + S computeDefaultValue(BuildContext context, {E? extra}); + + /// Compute values from the extension if found + S? computeExtensionValue( + BuildContext context, + S defaultValue, { + E? extra, + }) { + final themeExtension = findExtension(context); + if (themeExtension != null) { + return computeExtensionValueFn(context, defaultValue, themeExtension); + } + return defaultValue; + } + + /// Compute custom value + S? computeCustomValue( + BuildContext context, + S previousPhaseValue, { + E? extra, + }) { + final customStyle = customStyleFn(context, extra: extra); + if (customStyle != null) { + return customStyle; + } + return previousPhaseValue; + } + + T? findExtension(BuildContext context) => context.themeExtension(); + + /// Choose most suitable style for a given context. + S negotiate(BuildContext context, {E? extra}) { + S style = computeDefaultValue(context, extra: extra); + style = computeExtensionValue(context, style, extra: extra) ?? style; + style = computeCustomValue(context, style, extra: extra) ?? style; + return style; + } +}