master #81

Closed
malo wants to merge 322 commits from master into feat/bloc_layout/new-package
26 changed files with 611 additions and 346 deletions
Showing only changes of commit 96781880f4 - Show all commits

View File

@ -19,7 +19,4 @@ import 'package:wyatt_ui_components/src/features/features.dart';
extension ThemeComponentBuildContext on BuildContext { extension ThemeComponentBuildContext on BuildContext {
ComponentThemeData get components => ComponentTheme.of(this); 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;
} }

View File

@ -1,4 +1,3 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
// Copyright (C) 2023 WYATT GROUP // Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details. // Please see the AUTHORS file for details.
// //
@ -52,4 +51,7 @@ class MultiColor {
} }
return b; return b;
} }
@override
String toString() => 'MultiColor(_colors: $_colors, _color: $_color)';
} }

View File

@ -62,4 +62,10 @@ abstract class ButtonStyle<T> {
/// ///
/// Default to `null` /// Default to `null`
final BoxShadow? shadow; final BoxShadow? shadow;
@override
String toString() =>
'ButtonStyle(radius: $radius, padding: $padding, foregroundColors: '
'$foregroundColors, backgroundColors: $backgroundColors, borderColors: '
'$borderColors, stroke: $stroke, shadow: $shadow)';
} }

View File

@ -18,7 +18,6 @@ import 'dart:ui';
import 'package:copy_with_extension/copy_with_extension.dart'; import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:flutter/widgets.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/core/utils/multi_color.dart';
import 'package:wyatt_ui_components/src/domain/entities/buttons/button_style.dart'; import 'package:wyatt_ui_components/src/domain/entities/buttons/button_style.dart';
@ -38,19 +37,6 @@ class FileSelectionButtonStyle extends ButtonStyle<FileSelectionButtonStyle> {
super.shadow, 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. /// Used for interpolation.
static FileSelectionButtonStyle? lerp( static FileSelectionButtonStyle? lerp(
FileSelectionButtonStyle? a, FileSelectionButtonStyle? a,

View File

@ -18,7 +18,6 @@ import 'dart:ui';
import 'package:copy_with_extension/copy_with_extension.dart'; import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:flutter/widgets.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/core/utils/multi_color.dart';
import 'package:wyatt_ui_components/src/domain/entities/buttons/button_style.dart'; import 'package:wyatt_ui_components/src/domain/entities/buttons/button_style.dart';
@ -37,17 +36,6 @@ class FlatButtonStyle extends ButtonStyle<FlatButtonStyle> {
super.shadow, 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. /// Used for interpolation.
static FlatButtonStyle? lerp( static FlatButtonStyle? lerp(
FlatButtonStyle? a, FlatButtonStyle? a,
@ -86,4 +74,8 @@ class FlatButtonStyle extends ButtonStyle<FlatButtonStyle> {
/// ///
/// Default to `TextTheme.labelLarge` /// Default to `TextTheme.labelLarge`
final TextStyle? label; final TextStyle? label;
@override
String toString() =>
'FlatButtonStyle(label: $label), inherited: ${super.toString()}';
} }

View File

@ -18,7 +18,6 @@ import 'dart:ui';
import 'package:copy_with_extension/copy_with_extension.dart'; import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:flutter/widgets.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/core/utils/multi_color.dart';
import 'package:wyatt_ui_components/src/domain/entities/buttons/button_style.dart'; import 'package:wyatt_ui_components/src/domain/entities/buttons/button_style.dart';
@ -37,18 +36,6 @@ class SimpleIconButtonStyle extends ButtonStyle<SimpleIconButtonStyle> {
super.shadow, 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. /// Used for interpolation.
static SimpleIconButtonStyle? lerp( static SimpleIconButtonStyle? lerp(
SimpleIconButtonStyle? a, SimpleIconButtonStyle? a,

View File

@ -18,7 +18,6 @@ import 'dart:ui';
import 'package:copy_with_extension/copy_with_extension.dart'; import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:flutter/widgets.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/core/utils/multi_color.dart';
import 'package:wyatt_ui_components/src/domain/entities/buttons/button_style.dart'; import 'package:wyatt_ui_components/src/domain/entities/buttons/button_style.dart';
@ -38,19 +37,6 @@ class SymbolButtonStyle extends ButtonStyle<SymbolButtonStyle> {
super.shadow, 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. /// Used for interpolation.
static SymbolButtonStyle? lerp( static SymbolButtonStyle? lerp(
SymbolButtonStyle? a, SymbolButtonStyle? a,

View File

@ -25,7 +25,7 @@
UIKit and Design System used in Wyatt Studio. UIKit and Design System used in Wyatt Studio.
## Theme negociation ## Theme negotiation
When building a component, most of its attributes can be 'null'. 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. 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: You have several possibilities:
1) Pass the "radius" into the constructor, `Button(radius: 12)`. 1) Pass the "radius" into the constructor, `Button(radius: 12)`.
2) Set up a theme extension `ButtonThemeExtension(radius: 15)`. 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). 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).

View File

@ -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_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/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_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'; import 'package:wyatt_ui_kit/src/core/mixin/export_bloc_mixin.dart';
part 'file_selection_button.g.dart'; part 'file_selection_button.g.dart';
@ -39,6 +40,7 @@ class FileSelectionButton extends FileSelectionButtonComponent
super.invalidStyle, super.invalidStyle,
super.onPressed, super.onPressed,
super.mainAxisSize, super.mainAxisSize,
this.themeResolver,
super.key, super.key,
}); });
@ -74,6 +76,8 @@ class FileSelectionButton extends FileSelectionButtonComponent
@override @override
FileSelectionButtonStyle? get invalidStyle => FileSelectionButtonStyle? get invalidStyle =>
super.invalidStyle as FileSelectionButtonStyle?; super.invalidStyle as FileSelectionButtonStyle?;
final FileSelectionButtonThemeResolver? themeResolver;
@override @override
Widget build(BuildContext context) => exportBloc( Widget build(BuildContext context) => exportBloc(
@ -90,6 +94,7 @@ class FileSelectionButton extends FileSelectionButtonComponent
invalidStyle: invalidStyle, invalidStyle: invalidStyle,
onPressed: onPressed, onPressed: onPressed,
mainAxisSize: mainAxisSize, mainAxisSize: mainAxisSize,
themeResolver: themeResolver,
key: key, key: key,
), ),
); );

View File

@ -75,6 +75,7 @@ class $FileSelectionButtonCWProxyImpl
invalidStyle: invalidStyle ?? _value.invalidStyle, invalidStyle: invalidStyle ?? _value.invalidStyle,
onPressed: onPressed ?? _value.onPressed, onPressed: onPressed ?? _value.onPressed,
mainAxisSize: mainAxisSize ?? _value.mainAxisSize, mainAxisSize: mainAxisSize ?? _value.mainAxisSize,
themeResolver: themeResolver ?? _value.themeResolver,
key: key ?? _value.key, key: key ?? _value.key,
); );
} }

View File

@ -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_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/button_cubit.dart';
import 'package:wyatt_ui_kit/src/components/buttons/cubit/invalid_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/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/core/helpers/linear_gradient_helper.dart';
import 'package:wyatt_ui_kit/src/domain/button_theme_extension/file_selection_button_theme_extension.dart';
class FileSelectionButtonScreen class FileSelectionButtonScreen
extends CubitScreen<InvalidButtonCubit, ButtonState> { extends CubitScreen<InvalidButtonCubit, ButtonState> {
@ -42,6 +41,7 @@ class FileSelectionButtonScreen
this.invalidStyle, this.invalidStyle,
this.onPressed, this.onPressed,
this.mainAxisSize, this.mainAxisSize,
this.themeResolver,
super.key, super.key,
}); });
@ -59,138 +59,80 @@ class FileSelectionButtonScreen
final FileSelectionButtonStyle? invalidStyle; final FileSelectionButtonStyle? invalidStyle;
final void Function(ControlState state)? onPressed; final void Function(ControlState state)? onPressed;
final FileSelectionButtonThemeResolver? themeResolver;
@override @override
InvalidButtonCubit create(BuildContext context) => InvalidButtonCubit(); InvalidButtonCubit create(BuildContext context) => InvalidButtonCubit();
/// Negotiate the theme to get a complete style. /// Negotiate the theme to get a complete style.
FileSelectionButtonStyle negotiate(BuildContext context, ButtonState state) { FileSelectionButtonStyle resolve(BuildContext context, ButtonState state) {
// Define default style from Flutter values. final FileSelectionButtonThemeResolver resolver = themeResolver ??
FileSelectionButtonStyle style = FileSelectionButtonThemeResolver(
FileSelectionButtonStyle.fromFlutter(context); 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 return style;
final fileSelectionButtonThemeExtension = },
context.themeExtension<FileSelectionButtonThemeExtension>(); 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) { return style;
case ControlState.disabled: },
style = disabledStyle ?? );
fileSelectionButtonThemeExtension?.disabledStyle ?? return resolver.negotiate(context, extra: state);
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;
} }
Widget _border( Widget _border(
@ -217,7 +159,7 @@ class FileSelectionButtonScreen
@override @override
Widget onBuild(BuildContext context, ButtonState state) { Widget onBuild(BuildContext context, ButtonState state) {
final style = negotiate(context, state); final style = resolve(context, state);
return Focus( return Focus(
onFocusChange: (hasFocus) => onFocusChange: (hasFocus) =>

View File

@ -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 <https://www.gnu.org/licenses/>.
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;
}

View File

@ -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_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/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_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'; import 'package:wyatt_ui_kit/src/core/mixin/export_bloc_mixin.dart';
part 'flat_button.g.dart'; part 'flat_button.g.dart';
@ -37,6 +38,7 @@ class FlatButton extends FlatButtonComponent
super.tappedStyle, super.tappedStyle,
super.onPressed, super.onPressed,
super.mainAxisSize, super.mainAxisSize,
this.themeResolver,
super.key, super.key,
}); });
@ -60,6 +62,8 @@ class FlatButton extends FlatButtonComponent
@override @override
FlatButtonStyle? get tappedStyle => super.tappedStyle as FlatButtonStyle?; FlatButtonStyle? get tappedStyle => super.tappedStyle as FlatButtonStyle?;
final FlatButtonThemeResolver? themeResolver;
@override @override
Widget build(BuildContext context) => exportBloc( Widget build(BuildContext context) => exportBloc(
child: FlatButtonScreen( child: FlatButtonScreen(
@ -73,6 +77,7 @@ class FlatButton extends FlatButtonComponent
tappedStyle: tappedStyle, tappedStyle: tappedStyle,
onPressed: onPressed, onPressed: onPressed,
mainAxisSize: mainAxisSize, mainAxisSize: mainAxisSize,
themeResolver: themeResolver,
key: key, key: key,
), ),
); );

View File

@ -63,6 +63,7 @@ class $FlatButtonCWProxyImpl implements $FlatButtonComponentCWProxy {
tappedStyle: tappedStyle ?? _value.tappedStyle, tappedStyle: tappedStyle ?? _value.tappedStyle,
onPressed: onPressed ?? _value.onPressed, onPressed: onPressed ?? _value.onPressed,
mainAxisSize: mainAxisSize ?? _value.mainAxisSize, mainAxisSize: mainAxisSize ?? _value.mainAxisSize,
themeResolver: themeResolver ?? _value.themeResolver,
key: key ?? _value.key, key: key ?? _value.key,
); );
} }

View File

@ -20,11 +20,10 @@ import 'package:gap/gap.dart';
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.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/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_box_border.dart';
import 'package:wyatt_ui_kit/src/components/gradients/gradient_text.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/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<ButtonCubit, ButtonState> { class FlatButtonScreen extends CubitScreen<ButtonCubit, ButtonState> {
const FlatButtonScreen({ const FlatButtonScreen({
@ -38,6 +37,7 @@ class FlatButtonScreen extends CubitScreen<ButtonCubit, ButtonState> {
this.tappedStyle, this.tappedStyle,
this.onPressed, this.onPressed,
this.mainAxisSize, this.mainAxisSize,
this.themeResolver,
super.key, super.key,
}); });
@ -53,49 +53,57 @@ class FlatButtonScreen extends CubitScreen<ButtonCubit, ButtonState> {
final FlatButtonStyle? tappedStyle; final FlatButtonStyle? tappedStyle;
final void Function(ControlState state)? onPressed; final void Function(ControlState state)? onPressed;
final FlatButtonThemeResolver? themeResolver;
@override @override
ButtonCubit create(BuildContext context) => ButtonCubit(); ButtonCubit create(BuildContext context) => ButtonCubit();
/// Negotiate the theme to get a complete style. /// Negotiate the theme to get a complete style.
FlatButtonStyle negotiate(BuildContext context, ControlState state) { FlatButtonStyle resolve(BuildContext context, ControlState state) {
// Define default style from Flutter values. final FlatButtonThemeResolver resolver = themeResolver ??
FlatButtonStyle style = FlatButtonStyle.fromFlutter(context); FlatButtonThemeResolver(
computeExtensionValueFn: (
// Try to retrieve custom theme extension context,
final flatButtonThemeExtension = defaultValue,
context.themeExtension<FlatButtonThemeExtension>(); themeExtension, {
extra,
switch (state) { }) {
case ControlState.disabled: switch (extra) {
style = disabledStyle ?? case ControlState.disabled:
flatButtonThemeExtension?.disabledStyle ?? return themeExtension.disabledStyle;
style.copyWith( case ControlState.focused:
foregroundColors: return themeExtension.focusedStyle;
MultiColor.single(context.colorScheme.onSurface), case ControlState.hovered:
backgroundColors: MultiColor.single(context.colorScheme.surface), return themeExtension.hoveredStyle;
); case ControlState.tapped:
break; return themeExtension.tappedStyle;
case ControlState.hovered: case ControlState.normal:
style = hoveredStyle ?? flatButtonThemeExtension?.hoveredStyle ?? style; case null:
break; return themeExtension.normalStyle;
case ControlState.tapped: }
style = tappedStyle ?? flatButtonThemeExtension?.tappedStyle ?? style; },
break; customStyleFn: (context, {extra}) {
case ControlState.focused: switch (extra) {
style = focusedStyle ?? flatButtonThemeExtension?.focusedStyle ?? style; case ControlState.disabled:
break; return disabledStyle;
case ControlState.normal: case ControlState.focused:
style = normalStyle ?? flatButtonThemeExtension?.normalStyle ?? style; return focusedStyle;
break; case ControlState.hovered:
} return hoveredStyle;
case ControlState.tapped:
return style; return tappedStyle;
case ControlState.normal:
case null:
return normalStyle;
}
},
);
return resolver.negotiate(context, extra: state);
} }
@override @override
Widget onBuild(BuildContext context, ButtonState state) { Widget onBuild(BuildContext context, ButtonState state) {
final style = negotiate(context, state.state); final style = resolve(context, state.state);
return Focus( return Focus(
onFocusChange: (hasFocus) => onFocusChange: (hasFocus) =>
@ -182,7 +190,6 @@ class FlatButtonScreen extends CubitScreen<ButtonCubit, ButtonState> {
Gap(style.padding?.vertical ?? 10), Gap(style.padding?.vertical ?? 10),
/// Choose color /// Choose color
/// label.style.color ??
/// buttonStyle.label.style.color ?? /// buttonStyle.label.style.color ??
/// context.textTheme.labelLarge.color /// context.textTheme.labelLarge.color
/// ///
@ -191,7 +198,7 @@ class FlatButtonScreen extends CubitScreen<ButtonCubit, ButtonState> {
/// buttonStyle.foregroundColor.colors ?? /// buttonStyle.foregroundColor.colors ??
/// null /// null
/// ///
/// More infos in `negociate()` method /// More infos in ThemeResolver class
if (label != null) ...[ if (label != null) ...[
Text( Text(
label!.text, label!.text,

View File

@ -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 <https://www.gnu.org/licenses/>.
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<FlatButtonStyle,
FlatButtonThemeExtension, ControlState> {
const FlatButtonThemeResolver({
required this.computeExtensionValueFn,
required this.customStyleFn,
});
@override
/// Values taken from <https://api.flutter.dev/flutter/material/ElevatedButton/defaultStyleOf.html>
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;
}

View File

@ -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_component_copy_with_extension/component_copy_with_extension.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.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/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/components/buttons/simple_icon_button/simple_icon_screen.dart';
import 'package:wyatt_ui_kit/src/core/mixin/export_bloc_mixin.dart'; import 'package:wyatt_ui_kit/src/core/mixin/export_bloc_mixin.dart';
@ -34,6 +35,7 @@ class SimpleIconButton extends SimpleIconButtonComponent
super.focusedStyle, super.focusedStyle,
super.tappedStyle, super.tappedStyle,
super.onPressed, super.onPressed,
this.themeResolver,
super.key, super.key,
}); });
@ -62,6 +64,7 @@ class SimpleIconButton extends SimpleIconButtonComponent
SimpleIconButtonStyle? get tappedStyle => SimpleIconButtonStyle? get tappedStyle =>
super.tappedStyle as SimpleIconButtonStyle?; super.tappedStyle as SimpleIconButtonStyle?;
final SimpleIconButtonThemeResolver? themeResolver;
@override @override
Widget build(BuildContext context) => exportBloc( Widget build(BuildContext context) => exportBloc(
@ -73,6 +76,7 @@ class SimpleIconButton extends SimpleIconButtonComponent
focusedStyle: focusedStyle, focusedStyle: focusedStyle,
tappedStyle: tappedStyle, tappedStyle: tappedStyle,
onPressed: onPressed, onPressed: onPressed,
themeResolver: themeResolver,
key: key, key: key,
), ),
); );

View File

@ -51,6 +51,7 @@ class $SimpleIconButtonCWProxyImpl
focusedStyle: focusedStyle ?? _value.focusedStyle, focusedStyle: focusedStyle ?? _value.focusedStyle,
tappedStyle: tappedStyle ?? _value.tappedStyle, tappedStyle: tappedStyle ?? _value.tappedStyle,
onPressed: onPressed ?? _value.onPressed, onPressed: onPressed ?? _value.onPressed,
themeResolver: themeResolver ?? _value.themeResolver,
key: key ?? _value.key, key: key ?? _value.key,
); );
} }

View File

@ -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 <https://www.gnu.org/licenses/>.
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<SimpleIconButtonStyle,
SimpleIconButtonThemeExtension, ControlState> {
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;
}

View File

@ -19,11 +19,10 @@ import 'package:flutter/services.dart';
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.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/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_box_border.dart';
import 'package:wyatt_ui_kit/src/components/gradients/gradient_icon.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/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<ButtonCubit, ButtonState> { class SimpleIconButtonScreen extends CubitScreen<ButtonCubit, ButtonState> {
const SimpleIconButtonScreen({ const SimpleIconButtonScreen({
@ -34,6 +33,7 @@ class SimpleIconButtonScreen extends CubitScreen<ButtonCubit, ButtonState> {
this.focusedStyle, this.focusedStyle,
this.tappedStyle, this.tappedStyle,
this.onPressed, this.onPressed,
this.themeResolver,
super.key, super.key,
}); });
@ -46,55 +46,57 @@ class SimpleIconButtonScreen extends CubitScreen<ButtonCubit, ButtonState> {
final SimpleIconButtonStyle? tappedStyle; final SimpleIconButtonStyle? tappedStyle;
final void Function(ControlState state)? onPressed; final void Function(ControlState state)? onPressed;
final SimpleIconButtonThemeResolver? themeResolver;
@override @override
ButtonCubit create(BuildContext context) => ButtonCubit(); ButtonCubit create(BuildContext context) => ButtonCubit();
/// Negotiate the theme to get a complete style. /// Negotiate the theme to get a complete style.
SimpleIconButtonStyle negotiate(BuildContext context, ControlState state) { SimpleIconButtonStyle resolve(BuildContext context, ControlState state) {
// Define default style from Flutter values. final SimpleIconButtonThemeResolver resolver = themeResolver ??
SimpleIconButtonStyle style = SimpleIconButtonStyle.fromFlutter(context); SimpleIconButtonThemeResolver(
computeExtensionValueFn: (
// Try to retrieve custom theme extension context,
final simpleIconButtonThemeExtension = defaultValue,
context.themeExtension<SimpleIconButtonThemeExtension>(); themeExtension, {
extra,
switch (state) { }) {
case ControlState.disabled: switch (extra) {
style = disabledStyle ?? case ControlState.disabled:
simpleIconButtonThemeExtension?.disabledStyle ?? return themeExtension.disabledStyle;
style.copyWith( case ControlState.focused:
foregroundColors: return themeExtension.focusedStyle;
MultiColor.single(context.colorScheme.onSurface), case ControlState.hovered:
backgroundColors: MultiColor.single(context.colorScheme.surface), return themeExtension.hoveredStyle;
); case ControlState.tapped:
break; return themeExtension.tappedStyle;
case ControlState.hovered: case ControlState.normal:
style = hoveredStyle ?? case null:
simpleIconButtonThemeExtension?.hoveredStyle ?? return themeExtension.normalStyle;
style; }
break; },
case ControlState.tapped: customStyleFn: (context, {extra}) {
style = switch (extra) {
tappedStyle ?? simpleIconButtonThemeExtension?.tappedStyle ?? style; case ControlState.disabled:
break; return disabledStyle;
case ControlState.focused: case ControlState.focused:
style = focusedStyle ?? return focusedStyle;
simpleIconButtonThemeExtension?.focusedStyle ?? case ControlState.hovered:
style; return hoveredStyle;
break; case ControlState.tapped:
case ControlState.normal: return tappedStyle;
style = case ControlState.normal:
normalStyle ?? simpleIconButtonThemeExtension?.normalStyle ?? style; case null:
break; return normalStyle;
} }
},
return style; );
return resolver.negotiate(context, extra: state);
} }
@override @override
Widget onBuild(BuildContext context, ButtonState state) { Widget onBuild(BuildContext context, ButtonState state) {
final style = negotiate(context, state.state); final style = resolve(context, state.state);
return Focus( return Focus(
onFocusChange: (hasFocus) => onFocusChange: (hasFocus) =>

View File

@ -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_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/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_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'; import 'package:wyatt_ui_kit/src/core/mixin/export_bloc_mixin.dart';
part 'symbol_button.g.dart'; part 'symbol_button.g.dart';
@ -36,6 +37,7 @@ class SymbolButton extends SymbolButtonComponent
super.tappedStyle, super.tappedStyle,
super.selectedStyle, super.selectedStyle,
super.mainAxisSize, super.mainAxisSize,
this.themeResolver,
super.onPressed, super.onPressed,
super.key, super.key,
}); });
@ -67,6 +69,8 @@ class SymbolButton extends SymbolButtonComponent
SymbolButtonStyle? get selectedStyle => SymbolButtonStyle? get selectedStyle =>
super.selectedStyle as SymbolButtonStyle?; super.selectedStyle as SymbolButtonStyle?;
final SymbolButtonThemeResolver? themeResolver;
@override @override
Widget build(BuildContext context) => exportBloc( Widget build(BuildContext context) => exportBloc(
child: SymbolButtonScreen( child: SymbolButtonScreen(
@ -80,6 +84,7 @@ class SymbolButton extends SymbolButtonComponent
selectedStyle: selectedStyle, selectedStyle: selectedStyle,
onPressed: onPressed, onPressed: onPressed,
mainAxisSize: mainAxisSize, mainAxisSize: mainAxisSize,
themeResolver: themeResolver,
key: key, key: key,
), ),
); );

View File

@ -63,6 +63,7 @@ class $SymbolButtonCWProxyImpl implements $SymbolButtonComponentCWProxy {
tappedStyle: tappedStyle ?? _value.tappedStyle, tappedStyle: tappedStyle ?? _value.tappedStyle,
selectedStyle: selectedStyle ?? _value.selectedStyle, selectedStyle: selectedStyle ?? _value.selectedStyle,
mainAxisSize: mainAxisSize ?? _value.mainAxisSize, mainAxisSize: mainAxisSize ?? _value.mainAxisSize,
themeResolver: themeResolver ?? _value.themeResolver,
onPressed: onPressed ?? _value.onPressed, onPressed: onPressed ?? _value.onPressed,
key: key ?? _value.key, key: key ?? _value.key,
); );

View File

@ -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_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/button_cubit.dart';
import 'package:wyatt_ui_kit/src/components/buttons/cubit/selectable_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_box_border.dart';
import 'package:wyatt_ui_kit/src/components/gradients/gradient_text.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/core/helpers/linear_gradient_helper.dart';
import 'package:wyatt_ui_kit/src/domain/button_theme_extension/symbol_button_theme_extension.dart';
class SymbolButtonScreen class SymbolButtonScreen
extends CubitScreen<SelectableButtonCubit, ButtonState> { extends CubitScreen<SelectableButtonCubit, ButtonState> {
@ -40,6 +39,7 @@ class SymbolButtonScreen
this.selectedStyle, this.selectedStyle,
this.onPressed, this.onPressed,
this.mainAxisSize, this.mainAxisSize,
this.themeResolver,
super.key, super.key,
}); });
@ -55,94 +55,79 @@ class SymbolButtonScreen
final SymbolButtonStyle? selectedStyle; final SymbolButtonStyle? selectedStyle;
final void Function(ControlState state)? onPressed; final void Function(ControlState state)? onPressed;
final SymbolButtonThemeResolver? themeResolver;
@override @override
SelectableButtonCubit create(BuildContext context) => SelectableButtonCubit(); SelectableButtonCubit create(BuildContext context) => SelectableButtonCubit();
/// Negotiate the theme to get a complete style. /// Negotiate the theme to get a complete style.
SymbolButtonStyle negotiate(BuildContext context, ButtonState state) { SymbolButtonStyle resolve(BuildContext context, ButtonState state) {
// Define default style from Flutter values. final SymbolButtonThemeResolver resolver = themeResolver ??
SymbolButtonStyle style = SymbolButtonStyle.fromFlutter(context); 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 return style;
final symbolButtonThemeExtension = },
context.themeExtension<SymbolButtonThemeExtension>(); 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) { return style;
case ControlState.disabled: },
style = disabledStyle ?? );
symbolButtonThemeExtension?.disabledStyle ?? return resolver.negotiate(context, extra: state);
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;
} }
@override @override
Widget onBuild(BuildContext context, ButtonState state) { Widget onBuild(BuildContext context, ButtonState state) {
final style = negotiate(context, state); final style = resolve(context, state);
return Focus( return Focus(
onFocusChange: (hasFocus) => onFocusChange: (hasFocus) =>

View File

@ -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 <https://www.gnu.org/licenses/>.
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<SymbolButtonStyle,
SymbolButtonThemeExtension, ButtonState> {
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;
}

View File

@ -18,4 +18,7 @@ import 'package:flutter/material.dart';
extension BuildContextThemeExtension on BuildContext { extension BuildContextThemeExtension on BuildContext {
T? themeExtension<T>() => Theme.of(this).extension<T>(); T? themeExtension<T>() => Theme.of(this).extension<T>();
TextTheme get textTheme => Theme.of(this).textTheme;
ColorScheme get colorScheme => Theme.of(this).colorScheme;
ButtonThemeData get buttonTheme => Theme.of(this).buttonTheme;
} }

View File

@ -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 <https://www.gnu.org/licenses/>.
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<S, T, E> {
/// {@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<T>();
/// 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;
}
}