feat(ui_kit): add symbol button + enhance bloc control over state
This commit is contained in:
		
							parent
							
								
									aea71fa32c
								
							
						
					
					
						commit
						168d840b87
					
				| @ -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), | ||||
|         ], | ||||
|       ); | ||||
| } | ||||
|  | ||||
| @ -15,3 +15,4 @@ | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| export './flat_button/flat_button.dart'; | ||||
| export './symbol_button/symbol_button.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 <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| 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<ControlState> { | ||||
|   ButtonCubit() : super(ControlState.normal); | ||||
| part 'button_state.dart'; | ||||
| 
 | ||||
| class ButtonCubit extends Cubit<ButtonState> { | ||||
|   ButtonCubit() : super(const ButtonState.initial(ControlState.normal)); | ||||
| 
 | ||||
|   FutureOr<void> onMouseEnter() async { | ||||
|     if (state == ControlState.disabled) { | ||||
|     if (state.isDisabled || state.isFreezed) { | ||||
|       return; | ||||
|     } | ||||
|     emit(ControlState.hovered); | ||||
|     emit(state.copyWith(state: ControlState.hovered)); | ||||
|   } | ||||
| 
 | ||||
|   FutureOr<void> onMouseLeave() async { | ||||
|     if (state == ControlState.disabled) { | ||||
|     if (state.isDisabled || state.isFreezed) { | ||||
|       return; | ||||
|     } | ||||
|     emit(ControlState.normal); | ||||
| 
 | ||||
|     emit(state.copyWith(state: ControlState.normal)); | ||||
|   } | ||||
| 
 | ||||
|   FutureOr<void> onFocus() async { | ||||
|     if (state == ControlState.disabled) { | ||||
|     if (state.isDisabled || state.isFreezed) { | ||||
|       return; | ||||
|     } | ||||
|     emit(ControlState.focused); | ||||
|     emit(state.copyWith(state: ControlState.focused)); | ||||
|   } | ||||
| 
 | ||||
|   FutureOr<void> onUnfocus() async { | ||||
|     if (state == ControlState.disabled) { | ||||
|     if (state.isDisabled || state.isFreezed) { | ||||
|       return; | ||||
|     } | ||||
|     emit(ControlState.normal); | ||||
|     emit(state.copyWith(state: ControlState.normal)); | ||||
|   } | ||||
| 
 | ||||
|   FutureOr<void> onClickDown() async { | ||||
|     if (state == ControlState.disabled) { | ||||
|     if (state.isDisabled || state.isFreezed) { | ||||
|       return; | ||||
|     } | ||||
|     emit(ControlState.tapped); | ||||
|     emit(state.copyWith(state: ControlState.tapped)); | ||||
|   } | ||||
| 
 | ||||
|   FutureOr<void> onClickUpIn() async { | ||||
|     if (state == ControlState.disabled) { | ||||
|     if (state.isDisabled) { | ||||
|       return; | ||||
|     } | ||||
|     emit(ControlState.hovered); | ||||
|     emit(state.copyWith(state: ControlState.hovered)); | ||||
|   } | ||||
| 
 | ||||
|   FutureOr<void> onClickUpOut() async { | ||||
|     if (state == ControlState.disabled) { | ||||
|     if (state.isDisabled) { | ||||
|       return; | ||||
|     } | ||||
|     emit(ControlState.normal); | ||||
|     emit(state.copyWith(state: ControlState.normal)); | ||||
|   } | ||||
| 
 | ||||
|   FutureOr<void> disable() async { | ||||
|     emit(ControlState.disabled); | ||||
|     if (state.isFreezed) { | ||||
|       return; | ||||
|     } | ||||
|     emit(state.copyWith(state: ControlState.disabled)); | ||||
|   } | ||||
| 
 | ||||
|   FutureOr<void> enable() async { | ||||
|     emit(ControlState.normal); | ||||
|     if (state.isFreezed) { | ||||
|       return; | ||||
|     } | ||||
|     emit(state.copyWith(state: ControlState.normal)); | ||||
|   } | ||||
| 
 | ||||
|   FutureOr<void> freeze() async { | ||||
|     emit(state.copyWith(freezed: true)); | ||||
|   } | ||||
| 
 | ||||
|   FutureOr<void> unfreeze() async { | ||||
|     emit(state.copyWith(freezed: false)); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -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 <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| 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<Object?> 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, | ||||
|       ); | ||||
| } | ||||
| @ -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 <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| 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<void> onClickUpIn() async { | ||||
|     if (state.isDisabled || state.isFreezed) { | ||||
|       return; | ||||
|     } | ||||
|     emit( | ||||
|       state.copyWith(state: ControlState.hovered, selected: !state.selected), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   FutureOr<void> onClickUpOut() async { | ||||
|     if (state.isDisabled || state.isFreezed) { | ||||
|       return; | ||||
|     } | ||||
|     emit(state.copyWith(state: ControlState.normal, selected: !state.selected)); | ||||
|   } | ||||
| } | ||||
| @ -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<ButtonCubit> { | ||||
|   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, | ||||
|         ), | ||||
|       ); | ||||
| } | ||||
|  | ||||
| @ -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( | ||||
|  | ||||
| @ -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<ButtonCubit, ControlState> { | ||||
| class FlatButtonScreen extends CubitScreen<ButtonCubit, ButtonState> { | ||||
|   const FlatButtonScreen({ | ||||
|     this.prefix, | ||||
|     this.suffix, | ||||
| @ -50,7 +50,7 @@ class FlatButtonScreen extends CubitScreen<ButtonCubit, ControlState> { | ||||
|   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, ControlState> { | ||||
|   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<ButtonCubit, ControlState> { | ||||
|         style = focusedStyle ?? style; | ||||
|         break; | ||||
|       case ControlState.normal: | ||||
|       case ControlState.selected: | ||||
|       case ControlState.invalid: | ||||
|         // already done | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
| @ -85,8 +84,8 @@ class FlatButtonScreen extends CubitScreen<ButtonCubit, ControlState> { | ||||
|       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<ButtonCubit, ControlState> { | ||||
|             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( | ||||
|  | ||||
| @ -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 <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| 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<SelectableButtonCubit> { | ||||
|   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, | ||||
|         ), | ||||
|       ); | ||||
| } | ||||
| @ -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); | ||||
| } | ||||
| @ -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 <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| 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<SelectableButtonCubit, ButtonState> { | ||||
|   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, | ||||
|                       ), | ||||
|                     ); | ||||
|                   }, | ||||
|                 ), | ||||
|               ], | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -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 <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:flutter/widgets.dart'; | ||||
| import 'package:flutter_bloc/flutter_bloc.dart'; | ||||
| 
 | ||||
| mixin ExportBloc<T extends StateStreamableSource<Object?>> { | ||||
|   T get bloc; | ||||
|   Widget exportBloc({required Widget child}) => BlocProvider<T>.value( | ||||
|         value: bloc, | ||||
|         child: child, | ||||
|       ); | ||||
| } | ||||
| @ -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 | ||||
| @ -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 | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user