From 168d840b872938b6e1156c769ebd35c125836499 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Mon, 13 Feb 2023 21:21:05 +0100 Subject: [PATCH] feat(ui_kit): add symbol button + enhance bloc control over state --- .../example/lib/buttons/buttons.dart | 85 +++++- .../lib/src/components/buttons/buttons.dart | 1 + .../buttons/cubit/button_cubit.dart | 70 +++-- .../buttons/cubit/button_state.dart | 71 +++++ .../cubit/selectable_button_cubit.dart | 40 +++ .../buttons/flat_button/flat_button.dart | 39 ++- .../buttons/flat_button/flat_button.g.dart | 12 +- .../flat_button/flat_button_screen.dart | 19 +- .../buttons/symbol_button/symbol_button.dart | 86 ++++++ .../symbol_button/symbol_button.g.dart | 65 +++++ .../symbol_button/symbol_button_screen.dart | 249 ++++++++++++++++++ .../lib/src/core/mixin/export_bloc_mixin.dart | 26 ++ .../models/button_state_diagram.puml | 3 +- .../selectable_button_state_diagram.puml | 14 + 14 files changed, 721 insertions(+), 59 deletions(-) create mode 100644 packages/wyatt_ui_kit/lib/src/components/buttons/cubit/button_state.dart create mode 100644 packages/wyatt_ui_kit/lib/src/components/buttons/cubit/selectable_button_cubit.dart create mode 100644 packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.dart create mode 100644 packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.g.dart create mode 100644 packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button_screen.dart create mode 100644 packages/wyatt_ui_kit/lib/src/core/mixin/export_bloc_mixin.dart create mode 100644 packages/wyatt_ui_kit/models/selectable_button_state_diagram.puml diff --git a/packages/wyatt_ui_kit/example/lib/buttons/buttons.dart b/packages/wyatt_ui_kit/example/lib/buttons/buttons.dart index 3b049711..363ced46 100644 --- a/packages/wyatt_ui_kit/example/lib/buttons/buttons.dart +++ b/packages/wyatt_ui_kit/example/lib/buttons/buttons.dart @@ -24,11 +24,12 @@ const _color2 = Color(0xFF446DF4); const _color3 = Color.fromARGB(255, 26, 132, 247); const _color4 = Color.fromARGB(255, 19, 68, 228); const _colors = [_color1, _color2]; -const _colorsDarken = [_color2, _color3]; +const _colorsDarken = [_color3, _color4]; const _disabled = Color(0xFF6B7280); const _background = Color(0xFF16191D); const _disabledBackground = Color(0xFF16191D + 0x66FFFFFF); const _disabledColors = [Color(0xFF60656A), Color(0xFF383C40)]; +const _selectedColors = [Color(0xFF50CE99), Color(0xFF339572)]; class Buttons extends StatelessWidget { const Buttons({super.key}); @@ -41,30 +42,30 @@ class Buttons extends StatelessWidget { style: Theme.of(context).textTheme.titleLarge, ), const Gap(20), - const FlatButton( - label: TextWrapper('Voir notre savoir faire'), - normalStyle: FlatButtonStyle( + FlatButton( + label: const TextWrapper('Voir notre savoir faire'), + normalStyle: const FlatButtonStyle( foregroundColors: MultiColor(_colors), backgroundColors: MultiColor.single(Colors.transparent), borderColors: MultiColor(_colors), stroke: 3, ), - hoveredStyle: FlatButtonStyle( + hoveredStyle: const FlatButtonStyle( foregroundColors: MultiColor.single(Colors.white), backgroundColors: MultiColor(_colors), borderColors: MultiColor(_colors), stroke: 3, ), - tappedStyle: FlatButtonStyle( + tappedStyle: const FlatButtonStyle( foregroundColors: MultiColor.single(Colors.white), backgroundColors: MultiColor(_colorsDarken), borderColors: MultiColor(_colorsDarken), stroke: 3, ), - prefix: Icon( + prefix: const Icon( Icons.arrow_forward_rounded, ), - suffix: Icon( + suffix: const Icon( Icons.arrow_forward_rounded, ), ), @@ -169,7 +170,7 @@ class Buttons extends StatelessWidget { ), const Gap(20), FlatButton( - onPressed: () => print('pressed'), + onPressed: (state) => print('pressed, from $state'), label: TextWrapper( 'Démarrer mon projet', style: @@ -191,6 +192,72 @@ class Buttons extends StatelessWidget { backgroundColors: MultiColor.single(Colors.red), ), ), + const Gap(20), + ColoredBox( + color: _background, + child: Center( + child: Column( + children: [ + const Gap(20), + SymbolButton( + icon: const Icon( + Icons.android, + size: 25, + ), + label: const TextWrapper('Texte'), + normalStyle: const SymbolButtonStyle( + borderColors: MultiColor(_disabledColors), + foregroundColors: MultiColor.single(Colors.white), + backgroundColors: MultiColor.single(_disabledBackground), + stroke: 1, + ), + hoveredStyle: const SymbolButtonStyle( + borderColors: MultiColor(_disabledColors), + foregroundColors: MultiColor.single(Colors.white), + backgroundColors: MultiColor.single(_background), + stroke: 1, + ), + selectedStyle: const SymbolButtonStyle( + borderColors: MultiColor(_selectedColors), + foregroundColors: MultiColor.single(Colors.white), + backgroundColors: MultiColor.single(_disabledBackground), + stroke: 1, + ), + ) + ..bloc.onClickUpIn() + ..bloc.freeze(), + const Gap(20), + SymbolButton( + icon: const Icon( + Icons.android, + size: 25, + ), + label: const TextWrapper('Texte'), + normalStyle: const SymbolButtonStyle( + borderColors: MultiColor(_disabledColors), + foregroundColors: MultiColor.single(Colors.white), + backgroundColors: MultiColor.single(_disabledBackground), + stroke: 1, + ), + hoveredStyle: const SymbolButtonStyle( + borderColors: MultiColor(_disabledColors), + foregroundColors: MultiColor.single(Colors.white), + backgroundColors: MultiColor.single(_background), + stroke: 1, + ), + selectedStyle: const SymbolButtonStyle( + borderColors: MultiColor(_selectedColors), + foregroundColors: MultiColor.single(Colors.white), + backgroundColors: MultiColor.single(_disabledBackground), + stroke: 1, + ), + )..bloc.freeze(), + const Gap(20), + ], + ), + ), + ), + const Gap(20), ], ); } diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/buttons.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/buttons.dart index 654c0f72..c8bab7cb 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/buttons.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/buttons.dart @@ -15,3 +15,4 @@ // along with this program. If not, see . export './flat_button/flat_button.dart'; +export './symbol_button/symbol_button.dart'; diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/cubit/button_cubit.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/cubit/button_cubit.dart index 1af53d3c..d317a181 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/cubit/button_cubit.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/cubit/button_cubit.dart @@ -1,65 +1,99 @@ +// 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 'dart:async'; +import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; -class ButtonCubit extends Cubit { - ButtonCubit() : super(ControlState.normal); +part 'button_state.dart'; + +class ButtonCubit extends Cubit { + ButtonCubit() : super(const ButtonState.initial(ControlState.normal)); FutureOr onMouseEnter() async { - if (state == ControlState.disabled) { + if (state.isDisabled || state.isFreezed) { return; } - emit(ControlState.hovered); + emit(state.copyWith(state: ControlState.hovered)); } FutureOr onMouseLeave() async { - if (state == ControlState.disabled) { + if (state.isDisabled || state.isFreezed) { return; } - emit(ControlState.normal); + + emit(state.copyWith(state: ControlState.normal)); } FutureOr onFocus() async { - if (state == ControlState.disabled) { + if (state.isDisabled || state.isFreezed) { return; } - emit(ControlState.focused); + emit(state.copyWith(state: ControlState.focused)); } FutureOr onUnfocus() async { - if (state == ControlState.disabled) { + if (state.isDisabled || state.isFreezed) { return; } - emit(ControlState.normal); + emit(state.copyWith(state: ControlState.normal)); } FutureOr onClickDown() async { - if (state == ControlState.disabled) { + if (state.isDisabled || state.isFreezed) { return; } - emit(ControlState.tapped); + emit(state.copyWith(state: ControlState.tapped)); } FutureOr onClickUpIn() async { - if (state == ControlState.disabled) { + if (state.isDisabled) { return; } - emit(ControlState.hovered); + emit(state.copyWith(state: ControlState.hovered)); } FutureOr onClickUpOut() async { - if (state == ControlState.disabled) { + if (state.isDisabled) { return; } - emit(ControlState.normal); + emit(state.copyWith(state: ControlState.normal)); } FutureOr disable() async { - emit(ControlState.disabled); + if (state.isFreezed) { + return; + } + emit(state.copyWith(state: ControlState.disabled)); } FutureOr enable() async { - emit(ControlState.normal); + if (state.isFreezed) { + return; + } + emit(state.copyWith(state: ControlState.normal)); + } + + FutureOr freeze() async { + emit(state.copyWith(freezed: true)); + } + + FutureOr unfreeze() async { + emit(state.copyWith(freezed: false)); } } diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/cubit/button_state.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/cubit/button_state.dart new file mode 100644 index 00000000..1d2e5350 --- /dev/null +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/cubit/button_state.dart @@ -0,0 +1,71 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +// 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 . + +part of 'button_cubit.dart'; + +class ButtonState extends Equatable { + const ButtonState({ + required this.state, + required this.selected, + required this.invalid, + required this.freezed, + }); + + const ButtonState.initial(this.state) + : selected = false, + invalid = false, + freezed = false; + + final ControlState state; + + // Not in control state, because a button state can be + // a control state + extra state + // e.g : hover + invalid, or selected + tapped + final bool selected; + final bool invalid; + final bool freezed; + + bool get isDisabled => state.isDisabled(); + bool get isEnabled => state.isEnabled(); + bool get isFocused => state.isFocused(); + bool get isHovered => state.isHovered(); + bool get isTapped => state.isTapped(); + + // only for consistence + bool get isSelected => selected; + bool get isInvalid => invalid; + bool get isFreezed => freezed; + + @override + List get props => [state, selected, invalid, freezed]; + + @override + bool? get stringify => true; + + ButtonState copyWith({ + ControlState? state, + bool? selected, + bool? invalid, + bool? freezed, + }) => + ButtonState( + state: state ?? this.state, + selected: selected ?? this.selected, + invalid: invalid ?? this.invalid, + freezed: freezed ?? this.freezed, + ); +} diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/cubit/selectable_button_cubit.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/cubit/selectable_button_cubit.dart new file mode 100644 index 00000000..a271e09a --- /dev/null +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/cubit/selectable_button_cubit.dart @@ -0,0 +1,40 @@ +// 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 'dart:async'; + +import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; +import 'package:wyatt_ui_kit/src/components/buttons/cubit/button_cubit.dart'; + +class SelectableButtonCubit extends ButtonCubit { + @override + FutureOr onClickUpIn() async { + if (state.isDisabled || state.isFreezed) { + return; + } + emit( + state.copyWith(state: ControlState.hovered, selected: !state.selected), + ); + } + + @override + FutureOr onClickUpOut() async { + if (state.isDisabled || state.isFreezed) { + return; + } + emit(state.copyWith(state: ControlState.normal, selected: !state.selected)); + } +} 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 3779e590..4821a2c3 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 @@ -17,13 +17,16 @@ 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/flat_button/flat_button_screen.dart'; +import 'package:wyatt_ui_kit/src/core/mixin/export_bloc_mixin.dart'; part 'flat_button.g.dart'; @ComponentCopyWithExtension() -class FlatButton extends FlatButtonComponent with $FlatButtonCWMixin { - const FlatButton({ +class FlatButton extends FlatButtonComponent + with $FlatButtonCWMixin, ExportBloc { + FlatButton({ super.prefix, super.suffix, super.label, @@ -33,9 +36,15 @@ class FlatButton extends FlatButtonComponent with $FlatButtonCWMixin { super.focusedStyle, super.tappedStyle, super.onPressed, + super.mainAxisSize, super.key, }); + final ButtonCubit _cubit = ButtonCubit(); + + @override + ButtonCubit get bloc => _cubit; + @override FlatButtonStyle? get disabledStyle => super.disabledStyle as FlatButtonStyle?; @@ -52,17 +61,19 @@ class FlatButton extends FlatButtonComponent with $FlatButtonCWMixin { FlatButtonStyle? get tappedStyle => super.tappedStyle as FlatButtonStyle?; @override - Widget build(BuildContext context) => FlatButtonScreen( - prefix: prefix, - suffix: suffix, - label: label, - disabledStyle: disabledStyle, - normalStyle: normalStyle, - hoveredStyle: hoveredStyle, - focusedStyle: focusedStyle, - tappedStyle: tappedStyle, - onPressed: onPressed, - mainAxisSize: mainAxisSize, - key: key, + Widget build(BuildContext context) => exportBloc( + child: FlatButtonScreen( + prefix: prefix, + suffix: suffix, + label: label, + disabledStyle: disabledStyle, + normalStyle: normalStyle, + hoveredStyle: hoveredStyle, + focusedStyle: focusedStyle, + tappedStyle: tappedStyle, + onPressed: onPressed, + mainAxisSize: mainAxisSize, + 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 e797d659..fc436468 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 @@ -10,6 +10,9 @@ class $FlatButtonCWProxyImpl implements $FlatButtonComponentCWProxy { const $FlatButtonCWProxyImpl(this._value); final FlatButton _value; @override + FlatButton mainAxisSize(MainAxisSize? mainAxisSize) => + this(mainAxisSize: mainAxisSize); + @override FlatButton prefix(Widget? prefix) => this(prefix: prefix); @override FlatButton suffix(Widget? suffix) => this(suffix: suffix); @@ -31,15 +34,13 @@ class $FlatButtonCWProxyImpl implements $FlatButtonComponentCWProxy { FlatButton tappedStyle(ButtonStyle? tappedStyle) => this(tappedStyle: tappedStyle); @override - FlatButton onPressed(void Function()? onPressed) => + FlatButton onPressed(void Function(ControlState)? onPressed) => this(onPressed: onPressed); @override - FlatButton mainAxisSize(MainAxisSize? mainAxisSize) => - this(mainAxisSize: mainAxisSize); - @override FlatButton key(Key? key) => this(key: key); @override FlatButton call({ + MainAxisSize? mainAxisSize, Widget? prefix, Widget? suffix, TextWrapper? label, @@ -48,8 +49,7 @@ class $FlatButtonCWProxyImpl implements $FlatButtonComponentCWProxy { ButtonStyle? hoveredStyle, ButtonStyle? focusedStyle, ButtonStyle? tappedStyle, - void Function()? onPressed, - MainAxisSize? mainAxisSize, + void Function(ControlState)? onPressed, Key? key, }) => FlatButton( 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 51a8be42..1d44a8f0 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 @@ -25,7 +25,7 @@ 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'; -class FlatButtonScreen extends CubitScreen { +class FlatButtonScreen extends CubitScreen { const FlatButtonScreen({ this.prefix, this.suffix, @@ -50,7 +50,7 @@ class FlatButtonScreen extends CubitScreen { final FlatButtonStyle? focusedStyle; final FlatButtonStyle? tappedStyle; - final VoidCallback? onPressed; + final void Function(ControlState state)? onPressed; final MainAxisSize? mainAxisSize; @@ -58,11 +58,11 @@ class FlatButtonScreen extends CubitScreen { ButtonCubit create(BuildContext context) => ButtonCubit(); @override - Widget onBuild(BuildContext context, ControlState state) { + Widget onBuild(BuildContext context, ButtonState state) { // Set a default style FlatButtonStyle? style = normalStyle ?? const FlatButtonStyle(); - switch (state) { + switch (state.state) { case ControlState.disabled: style = disabledStyle ?? style; break; @@ -76,8 +76,7 @@ class FlatButtonScreen extends CubitScreen { style = focusedStyle ?? style; break; case ControlState.normal: - case ControlState.selected: - case ControlState.invalid: + // already done break; } @@ -85,8 +84,8 @@ class FlatButtonScreen extends CubitScreen { onFocusChange: (hasFocus) => hasFocus ? bloc(context).onFocus() : bloc(context).onUnfocus(), onKeyEvent: (focusNode, event) { - if (event.logicalKey == LogicalKeyboardKey.enter && state.isFocused()) { - onPressed?.call(); + if (event.logicalKey == LogicalKeyboardKey.enter && state.isFocused) { + onPressed?.call(state.state); bloc(context).onClickUpOut(); return KeyEventResult.handled; } @@ -105,11 +104,11 @@ class FlatButtonScreen extends CubitScreen { bloc(context).onClickDown(); }, onTapUp: (details) { - onPressed?.call(); + onPressed?.call(state.state); bloc(context).onClickUpIn(); }, onTapCancel: () { - onPressed?.call(); + onPressed?.call(state.state); bloc(context).onClickUpOut(); }, child: Material( 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 new file mode 100644 index 00000000..04a44790 --- /dev/null +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.dart @@ -0,0 +1,86 @@ +// 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' 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/selectable_button_cubit.dart'; +import 'package:wyatt_ui_kit/src/components/buttons/symbol_button/symbol_button_screen.dart'; +import 'package:wyatt_ui_kit/src/core/mixin/export_bloc_mixin.dart'; + +part 'symbol_button.g.dart'; + +@ComponentCopyWithExtension() +class SymbolButton extends SymbolButtonComponent + with $SymbolButtonCWMixin, ExportBloc { + SymbolButton({ + super.icon, + super.label, + super.disabledStyle, + super.normalStyle, + super.hoveredStyle, + super.focusedStyle, + super.tappedStyle, + super.selectedStyle, + super.mainAxisSize, + super.onPressed, + super.key, + }); + + final SelectableButtonCubit _cubit = SelectableButtonCubit(); + + @override + SelectableButtonCubit get bloc => _cubit; + + @override + SymbolButtonStyle? get disabledStyle => + super.disabledStyle as SymbolButtonStyle?; + + @override + SymbolButtonStyle? get normalStyle => super.normalStyle as SymbolButtonStyle?; + + @override + SymbolButtonStyle? get hoveredStyle => + super.hoveredStyle as SymbolButtonStyle?; + + @override + SymbolButtonStyle? get focusedStyle => + super.focusedStyle as SymbolButtonStyle?; + + @override + SymbolButtonStyle? get tappedStyle => super.tappedStyle as SymbolButtonStyle?; + + @override + SymbolButtonStyle? get selectedStyle => + super.selectedStyle as SymbolButtonStyle?; + + @override + Widget build(BuildContext context) => exportBloc( + child: SymbolButtonScreen( + icon: icon, + label: label, + disabledStyle: disabledStyle, + normalStyle: normalStyle, + hoveredStyle: hoveredStyle, + focusedStyle: focusedStyle, + tappedStyle: tappedStyle, + selectedStyle: selectedStyle, + onPressed: onPressed, + mainAxisSize: mainAxisSize, + 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 new file mode 100644 index 00000000..30476ddd --- /dev/null +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.g.dart @@ -0,0 +1,65 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'symbol_button.dart'; + +// ************************************************************************** +// ComponentCopyWithGenerator +// ************************************************************************** + +class $SymbolButtonCWProxyImpl implements $SymbolButtonComponentCWProxy { + const $SymbolButtonCWProxyImpl(this._value); + final SymbolButton _value; + @override + SymbolButton icon(Widget? icon) => this(icon: icon); + @override + SymbolButton disabledStyle(ButtonStyle? disabledStyle) => + this(disabledStyle: disabledStyle); + @override + SymbolButton normalStyle(ButtonStyle? normalStyle) => + this(normalStyle: normalStyle); + @override + SymbolButton hoveredStyle(ButtonStyle? hoveredStyle) => + this(hoveredStyle: hoveredStyle); + @override + SymbolButton focusedStyle(ButtonStyle? focusedStyle) => + this(focusedStyle: focusedStyle); + @override + SymbolButton tappedStyle(ButtonStyle? tappedStyle) => + this(tappedStyle: tappedStyle); + @override + SymbolButton selectedStyle(ButtonStyle? selectedStyle) => + this(selectedStyle: selectedStyle); + @override + SymbolButton onPressed(void Function(ControlState)? onPressed) => + this(onPressed: onPressed); + @override + SymbolButton key(Key? key) => this(key: key); + @override + SymbolButton call({ + Widget? icon, + ButtonStyle? disabledStyle, + ButtonStyle? normalStyle, + ButtonStyle? hoveredStyle, + ButtonStyle? focusedStyle, + ButtonStyle? tappedStyle, + ButtonStyle? selectedStyle, + void Function(ControlState)? onPressed, + Key? key, + }) => + SymbolButton( + icon: icon ?? _value.icon, + disabledStyle: disabledStyle ?? _value.disabledStyle, + normalStyle: normalStyle ?? _value.normalStyle, + hoveredStyle: hoveredStyle ?? _value.hoveredStyle, + focusedStyle: focusedStyle ?? _value.focusedStyle, + tappedStyle: tappedStyle ?? _value.tappedStyle, + selectedStyle: selectedStyle ?? _value.selectedStyle, + onPressed: onPressed ?? _value.onPressed, + key: key ?? _value.key, + ); +} + +mixin $SymbolButtonCWMixin on Component { + $SymbolButtonComponentCWProxy get copyWith => + $SymbolButtonCWProxyImpl(this as SymbolButton); +} 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 new file mode 100644 index 00000000..11f9050d --- /dev/null +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button_screen.dart @@ -0,0 +1,249 @@ +// 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' hide ButtonStyle; +import 'package:flutter/services.dart'; +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/cubit/selectable_button_cubit.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'; + +class SymbolButtonScreen + extends CubitScreen { + const SymbolButtonScreen({ + this.icon, + this.label, + this.disabledStyle, + this.normalStyle, + this.hoveredStyle, + this.focusedStyle, + this.tappedStyle, + this.selectedStyle, + this.onPressed, + this.mainAxisSize, + super.key, + }); + + final Widget? icon; + final TextWrapper? label; + + final SymbolButtonStyle? disabledStyle; + final SymbolButtonStyle? normalStyle; + final SymbolButtonStyle? hoveredStyle; + final SymbolButtonStyle? focusedStyle; + final SymbolButtonStyle? tappedStyle; + final SymbolButtonStyle? selectedStyle; + + final void Function(ControlState state)? onPressed; + + final MainAxisSize? mainAxisSize; + + @override + SelectableButtonCubit create(BuildContext context) => SelectableButtonCubit(); + + @override + Widget onBuild(BuildContext context, ButtonState state) { + // Set a default style + SymbolButtonStyle? style = normalStyle ?? const SymbolButtonStyle(); + + switch (state.state) { + case ControlState.disabled: + style = disabledStyle ?? style; + break; + case ControlState.hovered: + style = hoveredStyle ?? style; + break; + case ControlState.tapped: + style = tappedStyle ?? style; + break; + case ControlState.focused: + style = focusedStyle ?? style; + break; + case ControlState.normal: + break; + } + + if (state.isSelected) { + style = selectedStyle ?? style; + } + + return Focus( + onFocusChange: (hasFocus) => + hasFocus ? bloc(context).onFocus() : bloc(context).onUnfocus(), + onKeyEvent: (focusNode, event) { + if (event.logicalKey == LogicalKeyboardKey.enter && state.isFocused) { + onPressed?.call(state.state); + bloc(context).onClickUpOut(); + return KeyEventResult.handled; + } + return KeyEventResult.ignored; + }, + child: MouseRegion( + cursor: SystemMouseCursors.click, + onEnter: (event) { + bloc(context).onMouseEnter(); + }, + onExit: (event) { + bloc(context).onMouseLeave(); + }, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTapDown: (details) { + bloc(context).onClickDown(); + }, + onTapUp: (details) { + onPressed?.call(state.state); + bloc(context).onClickUpIn(); + }, + onTapCancel: () { + onPressed?.call(state.state); + bloc(context).onClickUpOut(); + }, + child: Row( + mainAxisSize: mainAxisSize ?? MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox.square( + dimension: 60, + child: AspectRatio( + aspectRatio: 1, + child: DecoratedBox( + decoration: BoxDecoration( + color: style.backgroundColors?.color ?? + context.colorScheme.primary, + // If no border color => no default value + border: (style.borderColors != null) + ? (style.borderColors?.isGradient ?? false) + ? GradientBoxBorder( + gradient: LinearGradient( + colors: style.borderColors!.colors, + ), + width: style.stroke ?? 2, + ) + : Border.all( + color: style.borderColors!.color, + width: style.stroke ?? 2, + ) + : null, + // if no gradient colors => no default value + gradient: (style.backgroundColors?.isGradient ?? false) + ? LinearGradient( + colors: style.backgroundColors!.colors, + ) + : null, + boxShadow: [ + if (style.shadow != null) ...[style.shadow!] + ], + borderRadius: BorderRadius.all( + Radius.circular(style.radius ?? 0), + ), + ), + child: ConstrainedBox( + constraints: const BoxConstraints( + minWidth: 50, + ), // min sizes for Material buttons + child: Padding( + padding: EdgeInsets.all(style.padding ?? 0), + child: Center( + // Choose color + // button.foreground.colors (gradient) ?? + // buttonStyle.foregroundColor.color ?? + // context.colorScheme.secondary + child: Builder( + builder: (context) { + final gradient = + (style?.foregroundColors?.isGradient ?? false) + ? LinearGradient( + colors: + style!.foregroundColors!.colors, + ) + : null; + final color = style?.foregroundColors?.color ?? + context.colorScheme.secondary; + if (gradient != null) { + return ShaderMask( + blendMode: BlendMode.srcIn, + shaderCallback: (bounds) => + gradient.createShader( + Rect.fromLTWH( + 0, + 0, + bounds.width, + bounds.height, + ), + ), + child: icon, + ); + } + return ColorFiltered( + colorFilter: + ColorFilter.mode(color, BlendMode.srcIn), + child: icon, + ); + }, + ), + ), + ), + ), + ), + ), + ), + // Choose color + // label.style.color ?? + // buttonStyle.foregroundColor.color ?? + // context.textTheme.titleLarge.color + // + // Choose gradient + // label.gradient ?? + // buttonStyle.foregroundColor.colors ?? + // null + if (label != null) ...[ + const Gap(10), + Builder( + builder: (context) { + final color = label?.style?.color ?? + style?.foregroundColors?.color ?? + context.textTheme.titleLarge?.color; + final buttonStyleGradient = + (style?.foregroundColors?.isGradient ?? false) + ? style?.foregroundColors?.colors + : null; + final gradient = label?.gradient ?? buttonStyleGradient; + + return Text( + label!.text, + style: (label!.style ?? context.textTheme.titleMedium) + ?.copyWith(color: color), + ).toGradient( + LinearGradientHelper.fromNullableColors( + gradient, + ), + ); + }, + ), + ], + ], + ), + ), + ), + ); + } +} diff --git a/packages/wyatt_ui_kit/lib/src/core/mixin/export_bloc_mixin.dart b/packages/wyatt_ui_kit/lib/src/core/mixin/export_bloc_mixin.dart new file mode 100644 index 00000000..f2ae41bf --- /dev/null +++ b/packages/wyatt_ui_kit/lib/src/core/mixin/export_bloc_mixin.dart @@ -0,0 +1,26 @@ +// 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/widgets.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +mixin ExportBloc> { + T get bloc; + Widget exportBloc({required Widget child}) => BlocProvider.value( + value: bloc, + child: child, + ); +} diff --git a/packages/wyatt_ui_kit/models/button_state_diagram.puml b/packages/wyatt_ui_kit/models/button_state_diagram.puml index adf43bdf..1f3ed7ef 100644 --- a/packages/wyatt_ui_kit/models/button_state_diagram.puml +++ b/packages/wyatt_ui_kit/models/button_state_diagram.puml @@ -8,6 +8,5 @@ tapped --> hovered : on mouse click up in (the cursor is in button) tapped --> normal : on mouse click up out (the cursor is out of button) normal --> focused : on focus focused --> normal : on unfocus -focused --> tapped : on space bar press down -tapped --> focused : on space bar press up +focused --> tapped : on enter press down @enduml \ No newline at end of file diff --git a/packages/wyatt_ui_kit/models/selectable_button_state_diagram.puml b/packages/wyatt_ui_kit/models/selectable_button_state_diagram.puml new file mode 100644 index 00000000..ca56770e --- /dev/null +++ b/packages/wyatt_ui_kit/models/selectable_button_state_diagram.puml @@ -0,0 +1,14 @@ +@startuml selectable_button_state_diagram +[*] --> normal +[*] --> disabled +normal --> hovered : on mouse enter +hovered --> normal : on mouse leave +hovered --> tapped : on mouse click down +tapped --> selected : on mouse click up and the previous state was not selected +selected --> tapped : on mouse click down +tapped --> hovered : on mouse click up in (the cursor is in button) +tapped --> normal : on mouse click up out (the cursor is out of button) +normal --> focused : on focus +focused --> normal : on unfocus +focused --> tapped : on enter press down +@enduml \ No newline at end of file