diff --git a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/button_component.dart b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/button_component.dart index 57b543c2..b412b05f 100644 --- a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/button_component.dart +++ b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/button_component.dart @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +import 'package:flutter/foundation.dart'; import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; abstract class ButtonComponent extends Component { @@ -26,6 +27,7 @@ abstract class ButtonComponent extends Component { this.selectedStyle, this.invalidStyle, this.onPressed, + this.disabled, super.themeResolver, super.key, }); @@ -51,6 +53,12 @@ abstract class ButtonComponent extends Component { /// Style of this button in invalid state final ButtonStyle? invalidStyle; - /// Callback on button press + /// Callback on button press, + /// + /// [ControlState] is passed as argument and can be used to determine if the + /// button was pressed in disabled state or not. final void Function(ControlState state)? onPressed; + + /// Current state of this button + final ValueNotifier? disabled; } diff --git a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/file_selection_button_component.dart b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/file_selection_button_component.dart index b0e5866b..32639be6 100644 --- a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/file_selection_button_component.dart +++ b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/file_selection_button_component.dart @@ -36,6 +36,7 @@ abstract class FileSelectionButtonComponent extends ButtonComponent super.selectedStyle, super.invalidStyle, super.onPressed, + super.disabled, super.themeResolver, super.key, }); diff --git a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/file_selection_button_component.g.dart b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/file_selection_button_component.g.dart index aa5816fe..a22edea2 100644 --- a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/file_selection_button_component.g.dart +++ b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/file_selection_button_component.g.dart @@ -22,6 +22,7 @@ abstract class $FileSelectionButtonComponentCWProxy { FileSelectionButtonComponent invalidStyle(ButtonStyle? invalidStyle); FileSelectionButtonComponent onPressed( void Function(ControlState)? onPressed); + FileSelectionButtonComponent disabled(ValueNotifier? disabled); FileSelectionButtonComponent themeResolver( ThemeResolver? themeResolver); FileSelectionButtonComponent key(Key? key); @@ -38,6 +39,7 @@ abstract class $FileSelectionButtonComponentCWProxy { ButtonStyle? selectedStyle, ButtonStyle? invalidStyle, void Function(ControlState)? onPressed, + ValueNotifier? disabled, ThemeResolver? themeResolver, Key? key, }); diff --git a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/flat_button_component.dart b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/flat_button_component.dart index 83420a59..e10d134c 100644 --- a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/flat_button_component.dart +++ b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/flat_button_component.dart @@ -34,6 +34,7 @@ abstract class FlatButtonComponent extends ButtonComponent super.focusedStyle, super.tappedStyle, super.onPressed, + super.disabled, super.themeResolver, super.key, }); diff --git a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/flat_button_component.g.dart b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/flat_button_component.g.dart index fb87d810..25acefba 100644 --- a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/flat_button_component.g.dart +++ b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/flat_button_component.g.dart @@ -17,6 +17,7 @@ abstract class $FlatButtonComponentCWProxy { FlatButtonComponent focusedStyle(ButtonStyle? focusedStyle); FlatButtonComponent tappedStyle(ButtonStyle? tappedStyle); FlatButtonComponent onPressed(void Function(ControlState)? onPressed); + FlatButtonComponent disabled(ValueNotifier? disabled); FlatButtonComponent themeResolver( ThemeResolver? themeResolver); FlatButtonComponent key(Key? key); @@ -31,6 +32,7 @@ abstract class $FlatButtonComponentCWProxy { ButtonStyle? focusedStyle, ButtonStyle? tappedStyle, void Function(ControlState)? onPressed, + ValueNotifier? disabled, ThemeResolver? themeResolver, Key? key, }); diff --git a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/simple_icon_button_component.dart b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/simple_icon_button_component.dart index b9a3c746..c2260ac4 100644 --- a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/simple_icon_button_component.dart +++ b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/simple_icon_button_component.dart @@ -31,6 +31,7 @@ abstract class SimpleIconButtonComponent extends ButtonComponent super.focusedStyle, super.tappedStyle, super.onPressed, + super.disabled, super.themeResolver, super.key, }); diff --git a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/simple_icon_button_component.g.dart b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/simple_icon_button_component.g.dart index 9a4b83ac..fdbce6a8 100644 --- a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/simple_icon_button_component.g.dart +++ b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/simple_icon_button_component.g.dart @@ -14,6 +14,7 @@ abstract class $SimpleIconButtonComponentCWProxy { SimpleIconButtonComponent focusedStyle(ButtonStyle? focusedStyle); SimpleIconButtonComponent tappedStyle(ButtonStyle? tappedStyle); SimpleIconButtonComponent onPressed(void Function(ControlState)? onPressed); + SimpleIconButtonComponent disabled(ValueNotifier? disabled); SimpleIconButtonComponent themeResolver( ThemeResolver? themeResolver); SimpleIconButtonComponent key(Key? key); @@ -25,6 +26,7 @@ abstract class $SimpleIconButtonComponentCWProxy { ButtonStyle? focusedStyle, ButtonStyle? tappedStyle, void Function(ControlState)? onPressed, + ValueNotifier? disabled, ThemeResolver? themeResolver, Key? key, }); diff --git a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/symbol_button_component.dart b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/symbol_button_component.dart index a09255d0..3a34c60a 100644 --- a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/symbol_button_component.dart +++ b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/symbol_button_component.dart @@ -34,6 +34,7 @@ abstract class SymbolButtonComponent extends ButtonComponent super.tappedStyle, super.selectedStyle, super.onPressed, + super.disabled, super.themeResolver, super.key, }); diff --git a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/symbol_button_component.g.dart b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/symbol_button_component.g.dart index a97dced0..be5cbb51 100644 --- a/packages/wyatt_ui_components/lib/src/domain/entities/buttons/symbol_button_component.g.dart +++ b/packages/wyatt_ui_components/lib/src/domain/entities/buttons/symbol_button_component.g.dart @@ -17,6 +17,7 @@ abstract class $SymbolButtonComponentCWProxy { SymbolButtonComponent tappedStyle(ButtonStyle? tappedStyle); SymbolButtonComponent selectedStyle(ButtonStyle? selectedStyle); SymbolButtonComponent onPressed(void Function(ControlState)? onPressed); + SymbolButtonComponent disabled(ValueNotifier? disabled); SymbolButtonComponent themeResolver( ThemeResolver? themeResolver); SymbolButtonComponent key(Key? key); @@ -31,6 +32,7 @@ abstract class $SymbolButtonComponentCWProxy { ButtonStyle? tappedStyle, ButtonStyle? selectedStyle, void Function(ControlState)? onPressed, + ValueNotifier? disabled, ThemeResolver? themeResolver, Key? key, }); diff --git a/packages/wyatt_ui_kit/example/lib/buttons/file_selection_button/file_selection_buttons.dart b/packages/wyatt_ui_kit/example/lib/buttons/file_selection_button/file_selection_buttons.dart index 0f398c13..d8863a85 100644 --- a/packages/wyatt_ui_kit/example/lib/buttons/file_selection_button/file_selection_buttons.dart +++ b/packages/wyatt_ui_kit/example/lib/buttons/file_selection_button/file_selection_buttons.dart @@ -15,14 +15,23 @@ // along with this program. If not, see . import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gap/gap.dart'; import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; import 'package:wyatt_ui_kit/wyatt_ui_kit.dart'; import 'package:wyatt_ui_kit_example/theme/constants.dart'; -class FileSelectionButtons extends StatelessWidget { +class FileSelectionButtons extends StatefulWidget { const FileSelectionButtons({super.key}); + @override + State createState() => _FileSelectionButtonsState(); +} + +class _FileSelectionButtonsState extends State { + final _disabled = ValueNotifier(true); + final _dynamic = ValueNotifier(false); + Widget _leading() => const DecoratedBox( decoration: BoxDecoration( color: Constants.grey2, @@ -48,6 +57,7 @@ class FileSelectionButtons extends StatelessWidget { style: Theme.of(context).textTheme.titleMedium, ), const Gap(20), + // Default FileSelectionButton( leading: _leading(), title: const TextWrapper( @@ -56,65 +66,99 @@ class FileSelectionButtons extends StatelessWidget { subTitle: const TextWrapper('Taille max: 20 Mo'), ), const Gap(20), - FileSelectionButton( - leading: _leading(), - title: const TextWrapper( - 'Enabled', + BlocProvider( + create: (context) => InvalidButtonCubit()..freeze(), + child: FileSelectionButton( + leading: _leading(), + title: const TextWrapper( + 'Enabled', + ), + subTitle: const TextWrapper('Subtitle'), ), - subTitle: const TextWrapper('Subtitle'), - ) - ..bloc.enable() - ..bloc.freeze(), + ), const Gap(20), + // Using a ValueNotifier to disable the button FileSelectionButton( leading: _leading(), title: const TextWrapper( 'Disabled', ), subTitle: const TextWrapper('Subtitle'), - ) - ..bloc.disable() - ..bloc.freeze(), + disabled: _disabled, + ), const Gap(20), - FileSelectionButton( - leading: _leading(), - title: const TextWrapper( - 'Hovered', + BlocProvider( + create: (context) => InvalidButtonCubit()..hover(), + child: FileSelectionButton( + leading: _leading(), + title: const TextWrapper( + 'Hovered', + ), + subTitle: const TextWrapper('Subtitle'), ), - subTitle: const TextWrapper('Subtitle'), - ) - ..bloc.onMouseEnter() - ..bloc.freeze(), + ), const Gap(20), - FileSelectionButton( - leading: _leading(), - title: const TextWrapper( - 'Focused', + BlocProvider( + create: (context) => InvalidButtonCubit()..focus(), + child: FileSelectionButton( + leading: _leading(), + title: const TextWrapper( + 'Focused', + ), + subTitle: const TextWrapper('Subtitle'), ), - subTitle: const TextWrapper('Subtitle'), - ) - ..bloc.onFocus() - ..bloc.freeze(), + ), const Gap(20), - FileSelectionButton( - leading: _leading(), - title: const TextWrapper( - 'Tapped', + BlocProvider( + create: (context) => InvalidButtonCubit()..tap(), + child: FileSelectionButton( + leading: _leading(), + title: const TextWrapper( + 'Tapped', + ), + subTitle: const TextWrapper('Subtitle'), ), - subTitle: const TextWrapper('Subtitle'), - ) - ..bloc.onClickDown() - ..bloc.freeze(), + ), const Gap(20), - FileSelectionButton( - leading: _leading(), - title: const TextWrapper( - 'Invalid', + BlocProvider( + create: (context) => InvalidButtonCubit()..invalidate(), + child: FileSelectionButton( + leading: _leading(), + title: const TextWrapper( + 'Invalid', + ), + subTitle: const TextWrapper('Subtitle'), ), - subTitle: const TextWrapper('Subtitle'), - ) - ..bloc.invalidate() - ..bloc.freeze(), + ), + const Gap(20), + // Dynamic, disabled with ValueNotifier and invalid with BlocProvider. + // Keep the state when the button is disabled. + Column( + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Text('Enabled'), + Checkbox( + value: !_dynamic.value, + onChanged: (value) { + setState(() { + _dynamic.value = !_dynamic.value; + }); + }, + ), + ], + ), + FileSelectionButton( + leading: _leading(), + title: const TextWrapper( + 'Dynamic', + ), + subTitle: const TextWrapper('Subtitle'), + disabled: _dynamic, + ), + ], + ), const Gap(20), ], ); diff --git a/packages/wyatt_ui_kit/example/lib/buttons/flat_button/flat_buttons.dart b/packages/wyatt_ui_kit/example/lib/buttons/flat_button/flat_buttons.dart index 4c1a6b1d..e4b59a3c 100644 --- a/packages/wyatt_ui_kit/example/lib/buttons/flat_button/flat_buttons.dart +++ b/packages/wyatt_ui_kit/example/lib/buttons/flat_button/flat_buttons.dart @@ -15,13 +15,22 @@ // along with this program. If not, see . import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gap/gap.dart'; import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; import 'package:wyatt_ui_kit/wyatt_ui_kit.dart'; -class FlatButtons extends StatelessWidget { +class FlatButtons extends StatefulWidget { const FlatButtons({super.key}); + @override + State createState() => _FlatButtonsState(); +} + +class _FlatButtonsState extends State { + final _disabled = ValueNotifier(true); + final _dynamic = ValueNotifier(false); + @override Widget build(BuildContext context) => Column( children: [ @@ -30,56 +39,80 @@ class FlatButtons extends StatelessWidget { style: Theme.of(context).textTheme.titleMedium, ), const Gap(20), - Center( + // Default + const Center( /// You can overwrite global textstyle of the label with [label], /// but if you only want to override the color/gradient of the text /// in a particular case you can override the style that will /// be merge during the build. child: FlatButton( - label: const TextWrapper( + label: TextWrapper( 'Voir notre savoir faire', ), ), ), const Gap(20), Center( - child: FlatButton( - label: const TextWrapper('Enabled'), - ) - ..bloc.enable() - ..bloc.freeze(), + child: BlocProvider( + create: (context) => ButtonCubit()..freeze(), + child: const FlatButton( + label: TextWrapper('Enabled'), + ), + ), ), const Gap(20), + // Disabled with ValueNotifier Center( child: FlatButton( label: const TextWrapper('Disabled'), - ) - ..bloc.disable() - ..bloc.freeze(), + disabled: _disabled, + ), ), const Gap(20), Center( - child: FlatButton( - label: const TextWrapper('Hovered'), - ) - ..bloc.onMouseEnter() - ..bloc.freeze(), + child: BlocProvider( + create: (context) => ButtonCubit()..hover(), + child: const FlatButton( + label: TextWrapper('Hovered'), + ), + ), ), const Gap(20), Center( - child: FlatButton( - label: const TextWrapper('Focused'), - ) - ..bloc.onFocus() - ..bloc.freeze(), + child: BlocProvider( + create: (context) => ButtonCubit()..focus(), + child: const FlatButton( + label: TextWrapper('Focused'), + ), + ), ), const Gap(20), Center( - child: FlatButton( - label: const TextWrapper('Tapped'), - ) - ..bloc.onClickDown() - ..bloc.freeze(), + child: BlocProvider( + create: (context) => ButtonCubit()..tap(), + child: const FlatButton( + label: TextWrapper('Tapped'), + ), + ), + ), + const Gap(20), + // Dynamic, disabled with ValueNotifier and + // keep the state when the button is disabled + Row( + children: [ + Checkbox( + value: !_dynamic.value, + onChanged: (value) { + setState(() { + _dynamic.value = !_dynamic.value; + }); + }, + ), + FlatButton( + label: const TextWrapper('Dynamic'), + disabled: _dynamic, + ), + ], ), const Gap(20), ], diff --git a/packages/wyatt_ui_kit/example/lib/buttons/simple_icon_button/simple_icon_buttons.dart b/packages/wyatt_ui_kit/example/lib/buttons/simple_icon_button/simple_icon_buttons.dart index 1de93f70..44143a5c 100644 --- a/packages/wyatt_ui_kit/example/lib/buttons/simple_icon_button/simple_icon_buttons.dart +++ b/packages/wyatt_ui_kit/example/lib/buttons/simple_icon_button/simple_icon_buttons.dart @@ -15,12 +15,21 @@ // along with this program. If not, see . import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gap/gap.dart'; import 'package:wyatt_ui_kit/wyatt_ui_kit.dart'; -class SimpleIconButtons extends StatelessWidget { +class SimpleIconButtons extends StatefulWidget { const SimpleIconButtons({super.key}); + @override + State createState() => _SimpleIconButtonsState(); +} + +class _SimpleIconButtonsState extends State { + final _disabled = ValueNotifier(true); + final _dynamic = ValueNotifier(false); + @override Widget build(BuildContext context) => Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -30,8 +39,9 @@ class SimpleIconButtons extends StatelessWidget { style: Theme.of(context).textTheme.titleMedium, ), const Gap(20), - SimpleIconButton( - icon: const Icon( + // Default + const SimpleIconButton( + icon: Icon( Icons.storm_outlined, size: 17, ), @@ -42,71 +52,101 @@ class SimpleIconButtons extends StatelessWidget { style: Theme.of(context).textTheme.labelMedium, ), const Gap(10), - SimpleIconButton( - icon: const Icon( - Icons.storm_outlined, - size: 17, + BlocProvider( + create: (context) => ButtonCubit()..freeze(), + child: const SimpleIconButton( + icon: Icon( + Icons.storm_outlined, + size: 17, + ), ), - ) - ..bloc.enable() - ..bloc.freeze(), + ), const Gap(20), Text( 'Disabled', style: Theme.of(context).textTheme.labelMedium, ), const Gap(10), + // Disabled using ValueNotifier SimpleIconButton( icon: const Icon( Icons.storm_outlined, size: 17, ), - ) - ..bloc.disable() - ..bloc.freeze(), + disabled: _disabled, + ), const Gap(20), Text( 'Hovered', style: Theme.of(context).textTheme.labelMedium, ), const Gap(10), - SimpleIconButton( - icon: const Icon( - Icons.storm_outlined, - size: 17, + BlocProvider( + create: (context) => ButtonCubit()..hover(), + child: const SimpleIconButton( + icon: Icon( + Icons.storm_outlined, + size: 17, + ), ), - ) - ..bloc.onMouseEnter() - ..bloc.freeze(), + ), const Gap(20), Text( 'Focused', style: Theme.of(context).textTheme.labelMedium, ), const Gap(10), - SimpleIconButton( - icon: const Icon( - Icons.storm_outlined, - size: 17, + BlocProvider( + create: (context) => ButtonCubit()..focus(), + child: const SimpleIconButton( + icon: Icon( + Icons.storm_outlined, + size: 17, + ), ), - ) - ..bloc.onFocus() - ..bloc.freeze(), + ), const Gap(20), Text( 'Tapped', style: Theme.of(context).textTheme.labelMedium, ), const Gap(10), - SimpleIconButton( - icon: const Icon( - Icons.storm_outlined, - size: 17, + BlocProvider( + create: (context) => ButtonCubit()..tap(), + child: const SimpleIconButton( + icon: Icon( + Icons.storm_outlined, + size: 17, + ), ), - ) - ..bloc.onClickDown() - ..bloc.freeze(), + ), const Gap(20), + Text( + 'Dynamic', + style: Theme.of(context).textTheme.labelMedium, + ), + const Gap(10), + // Dynamic, disabled with ValueNotifier and + // keep the state when the button is disabled + Row( + children: [ + Checkbox( + value: !_dynamic.value, + onChanged: (value) { + setState(() { + _dynamic.value = !_dynamic.value; + }); + }, + ), + SimpleIconButton( + icon: const Icon( + Icons.storm_outlined, + size: 17, + ), + disabled: _dynamic, + ), + ], + ), ], ); } diff --git a/packages/wyatt_ui_kit/example/lib/buttons/symbol_button/symbol_buttons.dart b/packages/wyatt_ui_kit/example/lib/buttons/symbol_button/symbol_buttons.dart index 54aa9cb5..659ae233 100644 --- a/packages/wyatt_ui_kit/example/lib/buttons/symbol_button/symbol_buttons.dart +++ b/packages/wyatt_ui_kit/example/lib/buttons/symbol_button/symbol_buttons.dart @@ -15,14 +15,23 @@ // along with this program. If not, see . import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gap/gap.dart'; import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; import 'package:wyatt_ui_kit/wyatt_ui_kit.dart'; import 'package:wyatt_ui_kit_example/theme/constants.dart'; -class SymbolButtons extends StatelessWidget { +class SymbolButtons extends StatefulWidget { const SymbolButtons({super.key}); + @override + State createState() => _SymbolButtonsState(); +} + +class _SymbolButtonsState extends State { + final _disabled = ValueNotifier(true); + final _dynamic = ValueNotifier(false); + Icon _icon(BuildContext context) => Icon( Icons.android, size: 25, @@ -45,47 +54,70 @@ class SymbolButtons extends StatelessWidget { icon: _icon(context), ), const Gap(20), - SymbolButton( - label: const TextWrapper('Enabled'), - icon: _icon(context), - ) - ..bloc.enable() - ..bloc.freeze(), + BlocProvider( + create: (context) => SelectableButtonCubit()..freeze(), + child: SymbolButton( + label: const TextWrapper('Enabled'), + icon: _icon(context), + ), + ), const Gap(20), + // Disabled SymbolButton( label: const TextWrapper('Disabled'), icon: _icon(context), - ) - ..bloc.disable() - ..bloc.freeze(), + disabled: _disabled, + ), const Gap(20), - SymbolButton( - label: const TextWrapper('Hovered'), - icon: _icon(context), - ) - ..bloc.onMouseEnter() - ..bloc.freeze(), + BlocProvider( + create: (context) => SelectableButtonCubit()..hover(), + child: SymbolButton( + label: const TextWrapper('Hovered'), + icon: _icon(context), + ), + ), const Gap(20), - SymbolButton( - label: const TextWrapper('Focused'), - icon: _icon(context), - ) - ..bloc.onFocus() - ..bloc.freeze(), + BlocProvider( + create: (context) => SelectableButtonCubit()..focus(), + child: SymbolButton( + label: const TextWrapper('Focused'), + icon: _icon(context), + ), + ), const Gap(20), - SymbolButton( - label: const TextWrapper('Tapped'), - icon: _icon(context), - ) - ..bloc.onClickDown() - ..bloc.freeze(), + BlocProvider( + create: (context) => SelectableButtonCubit()..tap(), + child: SymbolButton( + label: const TextWrapper('Tapped'), + icon: _icon(context), + ), + ), const Gap(20), - SymbolButton( - label: const TextWrapper('Selected'), - icon: _icon(context), - ) - ..bloc.select() - ..bloc.freeze(), + BlocProvider( + create: (context) => SelectableButtonCubit()..select(), + child: SymbolButton( + label: const TextWrapper('Selected'), + icon: _icon(context), + ), + ), + const Gap(20), + Row( + children: [ + Checkbox( + value: !_dynamic.value, + onChanged: (value) { + setState(() { + _dynamic.value = !_dynamic.value; + }); + }, + ), + SymbolButton( + label: const TextWrapper('Dynamic'), + icon: _icon(context), + disabled: _dynamic, + ), + ], + ), const Gap(20), DecoratedBox( decoration: BoxDecoration( diff --git a/packages/wyatt_ui_kit/example/pubspec.yaml b/packages/wyatt_ui_kit/example/pubspec.yaml index d9953115..cf855e24 100644 --- a/packages/wyatt_ui_kit/example/pubspec.yaml +++ b/packages/wyatt_ui_kit/example/pubspec.yaml @@ -10,6 +10,7 @@ environment: dependencies: flutter: { sdk: flutter } + flutter_bloc: ^8.1.2 flutter_localizations: { sdk: flutter } gap: ^2.0.1 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 bf04a627..34d6fae2 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/buttons.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/buttons.dart @@ -14,6 +14,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +export './cubit/button_cubit.dart'; +export './cubit/invalid_button_cubit.dart'; +export './cubit/selectable_button_cubit.dart'; export './file_selection_button/file_selection_button.dart'; export './flat_button/flat_button.dart'; export './simple_icon_button/simple_icon_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 d317a181..accafce5 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 @@ -23,8 +23,19 @@ import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; part 'button_state.dart'; class ButtonCubit extends Cubit { - ButtonCubit() : super(const ButtonState.initial(ControlState.normal)); + ButtonCubit() : super(const ButtonState.initial(ControlState.normal)) { + _previousState = state.copyWith(); + } + /// The previous state of the button. + /// This is used to restore the state of the button when + /// the button is enabled again. + ButtonState? _previousState; + + /// When the mouse enters the button. The button will change + /// its state to [ControlState.hovered]. + /// + /// If the button is disabled or freezed, this method does nothing. FutureOr onMouseEnter() async { if (state.isDisabled || state.isFreezed) { return; @@ -32,6 +43,10 @@ class ButtonCubit extends Cubit { emit(state.copyWith(state: ControlState.hovered)); } + /// When the mouse leaves the button. The button will change + /// its state to [ControlState.normal]. + /// + /// If the button is disabled or freezed, this method does nothing. FutureOr onMouseLeave() async { if (state.isDisabled || state.isFreezed) { return; @@ -40,6 +55,10 @@ class ButtonCubit extends Cubit { emit(state.copyWith(state: ControlState.normal)); } + /// When the button is focused. The button will change + /// its state to [ControlState.focused]. + /// + /// If the button is disabled or freezed, this method does nothing. FutureOr onFocus() async { if (state.isDisabled || state.isFreezed) { return; @@ -47,6 +66,10 @@ class ButtonCubit extends Cubit { emit(state.copyWith(state: ControlState.focused)); } + /// When the button is unfocused. The button will change + /// its state to [ControlState.normal]. + /// + /// If the button is disabled or freezed, this method does nothing. FutureOr onUnfocus() async { if (state.isDisabled || state.isFreezed) { return; @@ -54,6 +77,10 @@ class ButtonCubit extends Cubit { emit(state.copyWith(state: ControlState.normal)); } + /// When the mouse is pressed on the button. The button will change + /// its state to [ControlState.tapped]. + /// + /// If the button is disabled or freezed, this method does nothing. FutureOr onClickDown() async { if (state.isDisabled || state.isFreezed) { return; @@ -61,39 +88,127 @@ class ButtonCubit extends Cubit { emit(state.copyWith(state: ControlState.tapped)); } + /// When the mouse is released on the button. The button will change + /// its state to [ControlState.hovered]. + /// + /// If the button is disabled or freezed, this method does nothing. FutureOr onClickUpIn() async { - if (state.isDisabled) { + if (state.isDisabled || state.isFreezed) { return; } emit(state.copyWith(state: ControlState.hovered)); } + /// When the mouse is released outside the button. The button will change + /// its state to [ControlState.normal]. + /// + /// If the button is disabled or freezed, this method does nothing. FutureOr onClickUpOut() async { - if (state.isDisabled) { + if (state.isDisabled || state.isFreezed) { return; } emit(state.copyWith(state: ControlState.normal)); } + /// Programmatically change the state or extra states of the button. + FutureOr setState({ + ControlState? state, + List? extraStates, + }) async { + emit(this.state.copyWith(state: state, extraStates: extraStates)); + } + + /// Disable the button. + /// + /// This will remove all extra states and set + /// the state to [ControlState.disabled] and add [ExtraState.freezed]. FutureOr disable() async { - if (state.isFreezed) { - return; - } - emit(state.copyWith(state: ControlState.disabled)); + /// Save the previous state. + /// copyWith() is used to make sure the previous state is not + /// affected by any changes to the current state. + _previousState = state.copyWith(); + + emit( + const ButtonState( + state: ControlState.disabled, + extraStates: [ExtraState.freezed], + ), + ); } + /// Enable the button. + /// + /// This will restore the previous state of the button. FutureOr enable() async { - if (state.isFreezed) { + if (_previousState == null) { return; } - emit(state.copyWith(state: ControlState.normal)); + + emit(_previousState!); + _previousState = null; } + /// Set the button to hovered state and freeze it. + /// + /// Use this method to programmatically set the button to hovered state. + FutureOr hover() async { + emit( + state.copyWith( + state: ControlState.hovered, + extraStates: state.extraStates + [ExtraState.freezed], + ), + ); + } + + /// Set the button to focused state and freeze it. + /// + /// Use this method to programmatically set the button to focused state. + FutureOr focus() async { + emit( + state.copyWith( + state: ControlState.focused, + extraStates: state.extraStates + [ExtraState.freezed], + ), + ); + } + + /// Set the button to tapped state and freeze it. + /// + /// Use this method to programmatically set the button to tapped state. + FutureOr tap() async { + emit( + state.copyWith( + state: ControlState.tapped, + extraStates: state.extraStates + [ExtraState.freezed], + ), + ); + } + + /// Freeze the button in its current state. + /// + /// Shortcut for [setState] with extra state [ExtraState.freezed]. + /// + /// Use this method to programmatically freeze the button. FutureOr freeze() async { - emit(state.copyWith(freezed: true)); + emit( + state.copyWith( + extraStates: state.extraStates + [ExtraState.freezed], + ), + ); } + /// Unfreeze the button. + /// + /// Shortcut for [setState] without extra state [ExtraState.freezed]. + /// + /// Use this method to programmatically unfreeze the button. FutureOr unfreeze() async { - emit(state.copyWith(freezed: false)); + emit( + state.copyWith( + extraStates: state.extraStates + .where((element) => element != ExtraState.freezed) + .toList(), + ), + ); } } 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 index 1d2e5350..2abdd360 100644 --- 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 @@ -18,54 +18,41 @@ 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({required this.state, required this.extraStates}); - const ButtonState.initial(this.state) - : selected = false, - invalid = false, - freezed = false; + const ButtonState.initial(this.state) : extraStates = const []; + /// The control state of the button 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; + /// Not in control state, because a button state can be + /// a control state + extra state + /// e.g : + /// - ControlState.hover + [ExtraState.invalid] + /// - ControlState.tapped + [ExtraState.invalid + ExtraState.freezed] + final List extraStates; 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; + bool get isSelected => extraStates.contains(ExtraState.selected); + bool get isInvalid => extraStates.contains(ExtraState.invalid); + bool get isFreezed => extraStates.contains(ExtraState.freezed); @override - List get props => [state, selected, invalid, freezed]; + List get props => [state, extraStates]; @override bool? get stringify => true; ButtonState copyWith({ ControlState? state, - bool? selected, - bool? invalid, - bool? freezed, + List? extraStates, }) => ButtonState( state: state ?? this.state, - selected: selected ?? this.selected, - invalid: invalid ?? this.invalid, - freezed: freezed ?? this.freezed, + extraStates: extraStates ?? this.extraStates, ); } diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/cubit/invalid_button_cubit.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/cubit/invalid_button_cubit.dart index 30626192..4d2d1263 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/cubit/invalid_button_cubit.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/cubit/invalid_button_cubit.dart @@ -20,49 +20,29 @@ import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; import 'package:wyatt_ui_kit/src/components/buttons/cubit/button_cubit.dart'; class InvalidButtonCubit extends ButtonCubit { - @override - FutureOr onClickUpIn() async { - if (state.isDisabled || state.isFreezed) { - return; - } - - emit( - state.copyWith( - state: ControlState.hovered, - ), - ); - } - - @override - FutureOr onClickUpOut() async { - if (state.isDisabled || state.isFreezed) { - return; - } - - emit( - state.copyWith( - state: ControlState.normal, - ), - ); - } - + /// When the button is invalid. The button will add the + /// [ExtraState.invalid] to its extra states. + /// + /// Use [validate] to remove the [ExtraState.invalid] from the extra states. + /// + /// Use this method to invalidate the button programmatically. FutureOr invalidate() async { - if (state.isDisabled || state.isFreezed) { - return; - } - - emit( - state.copyWith(invalid: true), - ); + emit(state.copyWith(extraStates: state.extraStates + [ExtraState.invalid])); } - FutureOr fix() async { - if (state.isDisabled || state.isFreezed) { - return; - } - + /// When the button is valid. The button will remove the + /// [ExtraState.invalid] from its extra states. + /// + /// Use [invalidate] to add the [ExtraState.invalid] to the extra states. + /// + /// Use this method to validate the button programmatically. + FutureOr validate() async { emit( - state.copyWith(invalid: false), + state.copyWith( + extraStates: state.extraStates + .where((element) => element != ExtraState.invalid) + .toList(), + ), ); } } 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 index 9b747ad4..affaea42 100644 --- 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 @@ -20,14 +20,32 @@ 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 { + ButtonState _toggle(ButtonState state) { + if (state.isSelected) { + return state.copyWith( + extraStates: state.extraStates + .where((element) => element != ExtraState.selected) + .toList(), + ); + } else { + return state.copyWith( + extraStates: state.extraStates + [ExtraState.selected], + ); + } + } + + /// When the mouse is released on the button, the button will + /// add the [ExtraState.selected] if it is selected, or remove + /// the [ExtraState.selected] if it is not selected. + /// + /// If the button is disabled or freezed, this method does nothing. @override FutureOr onClickUpIn() async { if (state.isDisabled || state.isFreezed) { return; } - emit( - state.copyWith(state: ControlState.hovered, selected: !state.selected), - ); + + emit(_toggle(state).copyWith(state: ControlState.hovered)); } @override @@ -35,26 +53,31 @@ class SelectableButtonCubit extends ButtonCubit { if (state.isDisabled || state.isFreezed) { return; } - emit(state.copyWith(state: ControlState.normal, selected: !state.selected)); + + emit(_toggle(state).copyWith(state: ControlState.normal)); } + /// When the button is selected. The button will add the + /// [ExtraState.selected] to its extra states. + /// + /// Use this method to select the button programmatically. FutureOr select() async { - if (state.isDisabled || state.isFreezed) { - return; - } - emit( - state.copyWith(selected: true), + state.copyWith(extraStates: state.extraStates + [ExtraState.selected]), ); } + /// When the button is unselected. The button will remove the + /// [ExtraState.selected] from its extra states. + /// + /// Use this method to unselect the button programmatically. FutureOr unselect() async { - if (state.isDisabled || state.isFreezed) { - return; - } - emit( - state.copyWith(selected: false), + state.copyWith( + extraStates: state.extraStates + .where((element) => element != ExtraState.selected) + .toList(), + ), ); } } diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/exportable_bloc.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/cubit/state_listener.dart similarity index 55% rename from packages/wyatt_ui_kit/lib/src/components/buttons/exportable_bloc.dart rename to packages/wyatt_ui_kit/lib/src/components/buttons/cubit/state_listener.dart index 0f9fbd73..53754e74 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/exportable_bloc.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/cubit/state_listener.dart @@ -14,23 +14,22 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter/foundation.dart'; +import 'package:wyatt_ui_kit/src/components/buttons/cubit/button_cubit.dart'; -class ExportableBloc> - extends StatelessWidget { - const ExportableBloc({ - required this.bloc, - required this.child, - super.key, - }); - - final T bloc; - final Widget child; - - @override - Widget build(BuildContext context) => BlocProvider.value( - value: bloc, - child: child, - ); +/// A class that listens to the state of a [ValueNotifier] and +/// changes the state of a [ButtonCubit] accordingly. +abstract class StateListener { + /// Listens to the state of the [ValueNotifier] and + /// changes the state of the [ButtonCubit] accordingly. + static void listen( + ValueNotifier? state, + B bloc, + ) { + if (state!.value) { + bloc.disable(); + } else { + bloc.enable(); + } + } } diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button.dart index 1c038237..06353a4e 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button.dart @@ -17,8 +17,6 @@ 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/invalid_button_cubit.dart'; -import 'package:wyatt_ui_kit/src/components/buttons/exportable_bloc.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'; @@ -27,7 +25,7 @@ part 'file_selection_button.g.dart'; @ComponentCopyWithExtension() class FileSelectionButton extends FileSelectionButtonComponent with $FileSelectionButtonCWMixin { - FileSelectionButton({ + const FileSelectionButton({ super.leading, super.title, super.subTitle, @@ -39,15 +37,12 @@ class FileSelectionButton extends FileSelectionButtonComponent super.selectedStyle, super.invalidStyle, super.onPressed, + super.disabled, super.mainAxisSize, super.themeResolver, super.key, }); - final InvalidButtonCubit _cubit = InvalidButtonCubit(); - - InvalidButtonCubit get bloc => _cubit; - @override FileSelectionButtonStyle? get disabledStyle => super.disabledStyle as FileSelectionButtonStyle?; @@ -81,23 +76,21 @@ class FileSelectionButton extends FileSelectionButtonComponent super.themeResolver as FileSelectionButtonThemeResolver?; @override - Widget build(BuildContext context) => ExportableBloc( - bloc: _cubit, - child: FileSelectionButtonScreen( - leading: leading, - title: title, - subTitle: subTitle, - disabledStyle: disabledStyle, - normalStyle: normalStyle, - hoveredStyle: hoveredStyle, - focusedStyle: focusedStyle, - tappedStyle: tappedStyle, - selectedStyle: selectedStyle, - invalidStyle: invalidStyle, - onPressed: onPressed, - mainAxisSize: mainAxisSize, - themeResolver: themeResolver, - key: key, - ), + Widget build(BuildContext context) => FileSelectionButtonScreen( + leading: leading, + title: title, + subTitle: subTitle, + disabledStyle: disabledStyle, + normalStyle: normalStyle, + hoveredStyle: hoveredStyle, + focusedStyle: focusedStyle, + tappedStyle: tappedStyle, + selectedStyle: selectedStyle, + invalidStyle: invalidStyle, + onPressed: onPressed, + disabled: disabled, + mainAxisSize: mainAxisSize, + themeResolver: themeResolver, + key: key, ); } diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button.g.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button.g.dart index 45b7eca4..3e151c7b 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button.g.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button.g.dart @@ -45,6 +45,9 @@ class $FileSelectionButtonCWProxyImpl FileSelectionButton onPressed(void Function(ControlState)? onPressed) => this(onPressed: onPressed); @override + FileSelectionButton disabled(ValueNotifier? disabled) => + this(disabled: disabled); + @override FileSelectionButton themeResolver( ThemeResolver? themeResolver) => this(themeResolver: themeResolver); @@ -64,6 +67,7 @@ class $FileSelectionButtonCWProxyImpl ButtonStyle? selectedStyle, ButtonStyle? invalidStyle, void Function(ControlState)? onPressed, + ValueNotifier? disabled, ThemeResolver? themeResolver, Key? key, }) => @@ -79,6 +83,7 @@ class $FileSelectionButtonCWProxyImpl selectedStyle: selectedStyle ?? _value.selectedStyle, invalidStyle: invalidStyle ?? _value.invalidStyle, onPressed: onPressed ?? _value.onPressed, + disabled: disabled ?? _value.disabled, mainAxisSize: mainAxisSize ?? _value.mainAxisSize, themeResolver: themeResolver ?? _value.themeResolver, key: key ?? _value.key, diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button_screen.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button_screen.dart index 36495ec9..a8080da9 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button_screen.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/file_selection_button/file_selection_button_screen.dart @@ -21,6 +21,7 @@ import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; import 'package:wyatt_ui_kit/src/components/buttons/cubit/button_cubit.dart'; import 'package:wyatt_ui_kit/src/components/buttons/cubit/invalid_button_cubit.dart'; +import 'package:wyatt_ui_kit/src/components/buttons/cubit/state_listener.dart'; import 'package:wyatt_ui_kit/src/components/buttons/file_selection_button/dotter_border_child.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'; @@ -39,6 +40,7 @@ class FileSelectionButtonScreen this.selectedStyle, this.invalidStyle, this.onPressed, + this.disabled, this.mainAxisSize, this.themeResolver, super.key, @@ -58,11 +60,26 @@ class FileSelectionButtonScreen final FileSelectionButtonStyle? invalidStyle; final void Function(ControlState state)? onPressed; + final ValueNotifier? disabled; final FileSelectionButtonThemeResolver? themeResolver; @override InvalidButtonCubit create(BuildContext context) => InvalidButtonCubit(); + @override + InvalidButtonCubit init(BuildContext context, InvalidButtonCubit bloc) { + disabled?.addListener( + () => StateListener.listen(disabled, bloc), + ); + + /// Set the initial state depending on the disabled value + /// after adding the listener. + if (disabled?.value ?? false) { + bloc.disable(); + } + return bloc; + } + /// Negotiate the theme to get a complete style. FileSelectionButtonStyle _resolve(BuildContext context, ButtonState state) { final FileSelectionButtonThemeResolver resolver = themeResolver ?? 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 df67bb11..ebcfec92 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,8 +17,6 @@ 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/exportable_bloc.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'; @@ -26,7 +24,7 @@ part 'flat_button.g.dart'; @ComponentCopyWithExtension() class FlatButton extends FlatButtonComponent with $FlatButtonCWMixin { - FlatButton({ + const FlatButton({ super.prefix, super.suffix, super.label, @@ -36,15 +34,12 @@ class FlatButton extends FlatButtonComponent with $FlatButtonCWMixin { super.focusedStyle, super.tappedStyle, super.onPressed, + super.disabled, super.mainAxisSize, super.themeResolver, super.key, }); - final ButtonCubit _cubit = ButtonCubit(); - - ButtonCubit get bloc => _cubit; - @override FlatButtonStyle? get disabledStyle => super.disabledStyle as FlatButtonStyle?; @@ -65,21 +60,19 @@ class FlatButton extends FlatButtonComponent with $FlatButtonCWMixin { super.themeResolver as FlatButtonThemeResolver?; @override - Widget build(BuildContext context) => ExportableBloc( - bloc: _cubit, - child: FlatButtonScreen( - prefix: prefix, - suffix: suffix, - label: label, - disabledStyle: disabledStyle, - normalStyle: normalStyle, - hoveredStyle: hoveredStyle, - focusedStyle: focusedStyle, - tappedStyle: tappedStyle, - onPressed: onPressed, - mainAxisSize: mainAxisSize, - themeResolver: themeResolver, - key: key, - ), + Widget build(BuildContext context) => FlatButtonScreen( + prefix: prefix, + suffix: suffix, + label: label, + disabledStyle: disabledStyle, + normalStyle: normalStyle, + hoveredStyle: hoveredStyle, + focusedStyle: focusedStyle, + tappedStyle: tappedStyle, + onPressed: onPressed, + disabled: disabled, + mainAxisSize: mainAxisSize, + themeResolver: themeResolver, + key: key, ); } diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button.g.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button.g.dart index 2fb990d5..8f28dd65 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 @@ -37,6 +37,9 @@ class $FlatButtonCWProxyImpl implements $FlatButtonComponentCWProxy { FlatButton onPressed(void Function(ControlState)? onPressed) => this(onPressed: onPressed); @override + FlatButton disabled(ValueNotifier? disabled) => + this(disabled: disabled); + @override FlatButton themeResolver( ThemeResolver? themeResolver) => this(themeResolver: themeResolver); @@ -54,6 +57,7 @@ class $FlatButtonCWProxyImpl implements $FlatButtonComponentCWProxy { ButtonStyle? focusedStyle, ButtonStyle? tappedStyle, void Function(ControlState)? onPressed, + ValueNotifier? disabled, ThemeResolver? themeResolver, Key? key, }) => @@ -67,6 +71,7 @@ class $FlatButtonCWProxyImpl implements $FlatButtonComponentCWProxy { focusedStyle: focusedStyle ?? _value.focusedStyle, tappedStyle: tappedStyle ?? _value.tappedStyle, onPressed: onPressed ?? _value.onPressed, + disabled: disabled ?? _value.disabled, mainAxisSize: mainAxisSize ?? _value.mainAxisSize, themeResolver: themeResolver ?? _value.themeResolver, key: key ?? _value.key, diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button_screen.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button_screen.dart index 07df2a77..f16b626c 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button_screen.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/flat_button/flat_button_screen.dart @@ -20,6 +20,7 @@ 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/state_listener.dart'; import 'package:wyatt_ui_kit/src/components/buttons/flat_button/flat_button_theme_resolver.dart'; import 'package:wyatt_ui_kit/src/components/gradients/gradient_box_border.dart'; import 'package:wyatt_ui_kit/src/components/gradients/gradient_text.dart'; @@ -35,6 +36,7 @@ class FlatButtonScreen extends CubitScreen { this.focusedStyle, this.tappedStyle, this.onPressed, + this.disabled, this.mainAxisSize, this.themeResolver, super.key, @@ -52,11 +54,23 @@ class FlatButtonScreen extends CubitScreen { final FlatButtonStyle? tappedStyle; final void Function(ControlState state)? onPressed; + final ValueNotifier? disabled; final FlatButtonThemeResolver? themeResolver; @override ButtonCubit create(BuildContext context) => ButtonCubit(); + @override + ButtonCubit init(BuildContext context, ButtonCubit bloc) { + disabled?.addListener(() => StateListener.listen(disabled, bloc)); + /// Set the initial state depending on the disabled value + /// after adding the listener. + if (disabled?.value ?? false) { + bloc.disable(); + } + return bloc; + } + /// Negotiate the theme to get a complete style. FlatButtonStyle _resolve(BuildContext context, ControlState state) { final FlatButtonThemeResolver resolver = themeResolver ?? diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button.dart index 9bd074b1..6d2be5d2 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button.dart @@ -17,8 +17,6 @@ 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/exportable_bloc.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'; @@ -27,7 +25,7 @@ part 'simple_icon_button.g.dart'; @ComponentCopyWithExtension() class SimpleIconButton extends SimpleIconButtonComponent with $SimpleIconButtonCWMixin { - SimpleIconButton({ + const SimpleIconButton({ super.icon, super.disabledStyle, super.normalStyle, @@ -35,14 +33,11 @@ class SimpleIconButton extends SimpleIconButtonComponent super.focusedStyle, super.tappedStyle, super.onPressed, + super.disabled, super.themeResolver, super.key, }); - final ButtonCubit _cubit = ButtonCubit(); - - ButtonCubit get bloc => _cubit; - @override SimpleIconButtonStyle? get disabledStyle => super.disabledStyle as SimpleIconButtonStyle?; @@ -68,18 +63,16 @@ class SimpleIconButton extends SimpleIconButtonComponent super.themeResolver as SimpleIconButtonThemeResolver?; @override - Widget build(BuildContext context) => ExportableBloc( - bloc: _cubit, - child: SimpleIconButtonScreen( - icon: icon, - disabledStyle: disabledStyle, - normalStyle: normalStyle, - hoveredStyle: hoveredStyle, - focusedStyle: focusedStyle, - tappedStyle: tappedStyle, - onPressed: onPressed, - themeResolver: themeResolver, - key: key, - ), + Widget build(BuildContext context) => SimpleIconButtonScreen( + icon: icon, + disabledStyle: disabledStyle, + normalStyle: normalStyle, + hoveredStyle: hoveredStyle, + focusedStyle: focusedStyle, + tappedStyle: tappedStyle, + onPressed: onPressed, + disabled: disabled, + themeResolver: themeResolver, + key: key, ); } diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button.g.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button.g.dart index 79bfba6a..9ad48526 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button.g.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_button.g.dart @@ -31,6 +31,9 @@ class $SimpleIconButtonCWProxyImpl SimpleIconButton onPressed(void Function(ControlState)? onPressed) => this(onPressed: onPressed); @override + SimpleIconButton disabled(ValueNotifier? disabled) => + this(disabled: disabled); + @override SimpleIconButton themeResolver( ThemeResolver? themeResolver) => this(themeResolver: themeResolver); @@ -45,6 +48,7 @@ class $SimpleIconButtonCWProxyImpl ButtonStyle? focusedStyle, ButtonStyle? tappedStyle, void Function(ControlState)? onPressed, + ValueNotifier? disabled, ThemeResolver? themeResolver, Key? key, }) => @@ -56,6 +60,7 @@ class $SimpleIconButtonCWProxyImpl focusedStyle: focusedStyle ?? _value.focusedStyle, tappedStyle: tappedStyle ?? _value.tappedStyle, onPressed: onPressed ?? _value.onPressed, + disabled: disabled ?? _value.disabled, themeResolver: themeResolver ?? _value.themeResolver, key: key ?? _value.key, ); diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_screen.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_screen.dart index 4467cd54..0aa1687f 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_screen.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/simple_icon_button/simple_icon_screen.dart @@ -19,6 +19,7 @@ import 'package:flutter/services.dart'; import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; import 'package:wyatt_ui_kit/src/components/buttons/cubit/button_cubit.dart'; +import 'package:wyatt_ui_kit/src/components/buttons/cubit/state_listener.dart'; import 'package:wyatt_ui_kit/src/components/buttons/simple_icon_button/simple_icon_button_theme_resolver.dart'; import 'package:wyatt_ui_kit/src/components/gradients/gradient_box_border.dart'; import 'package:wyatt_ui_kit/src/components/gradients/gradient_icon.dart'; @@ -33,6 +34,7 @@ class SimpleIconButtonScreen extends CubitScreen { this.focusedStyle, this.tappedStyle, this.onPressed, + this.disabled, this.themeResolver, super.key, }); @@ -46,11 +48,24 @@ class SimpleIconButtonScreen extends CubitScreen { final SimpleIconButtonStyle? tappedStyle; final void Function(ControlState state)? onPressed; + final ValueNotifier? disabled; final SimpleIconButtonThemeResolver? themeResolver; @override ButtonCubit create(BuildContext context) => ButtonCubit(); + @override + ButtonCubit init(BuildContext context, ButtonCubit bloc) { + disabled?.addListener(() => StateListener.listen(disabled, bloc)); + + /// Set the initial state depending on the disabled value + /// after adding the listener. + if (disabled?.value ?? false) { + bloc.disable(); + } + return bloc; + } + /// Negotiate the theme to get a complete style. SimpleIconButtonStyle _resolve(BuildContext context, ControlState state) { final SimpleIconButtonThemeResolver resolver = themeResolver ?? diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.dart index 64436fa5..1f343e8a 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.dart @@ -17,17 +17,14 @@ 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/exportable_bloc.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'; part 'symbol_button.g.dart'; @ComponentCopyWithExtension() -class SymbolButton extends SymbolButtonComponent - with $SymbolButtonCWMixin{ - SymbolButton({ +class SymbolButton extends SymbolButtonComponent with $SymbolButtonCWMixin { + const SymbolButton({ super.icon, super.label, super.disabledStyle, @@ -39,13 +36,10 @@ class SymbolButton extends SymbolButtonComponent super.mainAxisSize, super.themeResolver, super.onPressed, + super.disabled, super.key, }); - final SelectableButtonCubit _cubit = SelectableButtonCubit(); - - SelectableButtonCubit get bloc => _cubit; - @override SymbolButtonStyle? get disabledStyle => super.disabledStyle as SymbolButtonStyle?; @@ -73,21 +67,19 @@ class SymbolButton extends SymbolButtonComponent super.themeResolver as SymbolButtonThemeResolver?; @override - Widget build(BuildContext context) => ExportableBloc( - bloc: _cubit, - child: SymbolButtonScreen( - icon: icon, - label: label, - disabledStyle: disabledStyle, - normalStyle: normalStyle, - hoveredStyle: hoveredStyle, - focusedStyle: focusedStyle, - tappedStyle: tappedStyle, - selectedStyle: selectedStyle, - onPressed: onPressed, - mainAxisSize: mainAxisSize, - themeResolver: themeResolver, - key: key, - ), + Widget build(BuildContext context) => SymbolButtonScreen( + icon: icon, + label: label, + disabledStyle: disabledStyle, + normalStyle: normalStyle, + hoveredStyle: hoveredStyle, + focusedStyle: focusedStyle, + tappedStyle: tappedStyle, + selectedStyle: selectedStyle, + onPressed: onPressed, + disabled: disabled, + mainAxisSize: mainAxisSize, + themeResolver: themeResolver, + key: key, ); } diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.g.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.g.dart index c7e06ab2..536fe3c4 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.g.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button.g.dart @@ -38,6 +38,9 @@ class $SymbolButtonCWProxyImpl implements $SymbolButtonComponentCWProxy { SymbolButton onPressed(void Function(ControlState)? onPressed) => this(onPressed: onPressed); @override + SymbolButton disabled(ValueNotifier? disabled) => + this(disabled: disabled); + @override SymbolButton themeResolver( ThemeResolver? themeResolver) => this(themeResolver: themeResolver); @@ -55,6 +58,7 @@ class $SymbolButtonCWProxyImpl implements $SymbolButtonComponentCWProxy { ButtonStyle? tappedStyle, ButtonStyle? selectedStyle, void Function(ControlState)? onPressed, + ValueNotifier? disabled, ThemeResolver? themeResolver, Key? key, }) => @@ -70,6 +74,7 @@ class $SymbolButtonCWProxyImpl implements $SymbolButtonComponentCWProxy { mainAxisSize: mainAxisSize ?? _value.mainAxisSize, themeResolver: themeResolver ?? _value.themeResolver, onPressed: onPressed ?? _value.onPressed, + disabled: disabled ?? _value.disabled, key: key ?? _value.key, ); } diff --git a/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button_screen.dart b/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button_screen.dart index e51505ec..468d4e1e 100644 --- a/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button_screen.dart +++ b/packages/wyatt_ui_kit/lib/src/components/buttons/symbol_button/symbol_button_screen.dart @@ -21,6 +21,7 @@ import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart'; import 'package:wyatt_ui_kit/src/components/buttons/cubit/button_cubit.dart'; import 'package:wyatt_ui_kit/src/components/buttons/cubit/selectable_button_cubit.dart'; +import 'package:wyatt_ui_kit/src/components/buttons/cubit/state_listener.dart'; import 'package:wyatt_ui_kit/src/components/buttons/symbol_button/symbol_button_theme_resolver.dart'; import 'package:wyatt_ui_kit/src/components/gradients/gradient_box_border.dart'; import 'package:wyatt_ui_kit/src/components/gradients/gradient_text.dart'; @@ -37,6 +38,7 @@ class SymbolButtonScreen this.tappedStyle, this.selectedStyle, this.onPressed, + this.disabled, this.mainAxisSize, this.themeResolver, super.key, @@ -54,11 +56,26 @@ class SymbolButtonScreen final SymbolButtonStyle? selectedStyle; final void Function(ControlState state)? onPressed; + final ValueNotifier? disabled; final SymbolButtonThemeResolver? themeResolver; @override SelectableButtonCubit create(BuildContext context) => SelectableButtonCubit(); + @override + SelectableButtonCubit init(BuildContext context, SelectableButtonCubit bloc) { + disabled?.addListener( + () => StateListener.listen(disabled, bloc), + ); + + /// Set the initial state depending on the disabled value + /// after adding the listener. + if (disabled?.value ?? false) { + bloc.disable(); + } + return bloc; + } + /// Negotiate the theme to get a complete style. SymbolButtonStyle _resolve(BuildContext context, ButtonState state) { final SymbolButtonThemeResolver resolver = themeResolver ??