refactor(ui)!: remove bloc export in buttons to be state management solution agnostic (closes #147)

This commit is contained in:
Hugo Pointcheval 2023-02-23 16:59:56 +01:00
parent d6c9dfd8a7
commit 6c54689393
Signed by: hugo
GPG Key ID: 3AAC487E131E00BC
32 changed files with 673 additions and 342 deletions

View File

@ -14,6 +14,7 @@
// 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/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<dynamic>? 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<bool>? disabled;
}

View File

@ -36,6 +36,7 @@ abstract class FileSelectionButtonComponent extends ButtonComponent
super.selectedStyle,
super.invalidStyle,
super.onPressed,
super.disabled,
super.themeResolver,
super.key,
});

View File

@ -22,6 +22,7 @@ abstract class $FileSelectionButtonComponentCWProxy {
FileSelectionButtonComponent invalidStyle(ButtonStyle<dynamic>? invalidStyle);
FileSelectionButtonComponent onPressed(
void Function(ControlState)? onPressed);
FileSelectionButtonComponent disabled(ValueNotifier<bool>? disabled);
FileSelectionButtonComponent themeResolver(
ThemeResolver<dynamic, dynamic, dynamic>? themeResolver);
FileSelectionButtonComponent key(Key? key);
@ -38,6 +39,7 @@ abstract class $FileSelectionButtonComponentCWProxy {
ButtonStyle<dynamic>? selectedStyle,
ButtonStyle<dynamic>? invalidStyle,
void Function(ControlState)? onPressed,
ValueNotifier<bool>? disabled,
ThemeResolver<dynamic, dynamic, dynamic>? themeResolver,
Key? key,
});

View File

@ -34,6 +34,7 @@ abstract class FlatButtonComponent extends ButtonComponent
super.focusedStyle,
super.tappedStyle,
super.onPressed,
super.disabled,
super.themeResolver,
super.key,
});

View File

@ -17,6 +17,7 @@ abstract class $FlatButtonComponentCWProxy {
FlatButtonComponent focusedStyle(ButtonStyle<dynamic>? focusedStyle);
FlatButtonComponent tappedStyle(ButtonStyle<dynamic>? tappedStyle);
FlatButtonComponent onPressed(void Function(ControlState)? onPressed);
FlatButtonComponent disabled(ValueNotifier<bool>? disabled);
FlatButtonComponent themeResolver(
ThemeResolver<dynamic, dynamic, dynamic>? themeResolver);
FlatButtonComponent key(Key? key);
@ -31,6 +32,7 @@ abstract class $FlatButtonComponentCWProxy {
ButtonStyle<dynamic>? focusedStyle,
ButtonStyle<dynamic>? tappedStyle,
void Function(ControlState)? onPressed,
ValueNotifier<bool>? disabled,
ThemeResolver<dynamic, dynamic, dynamic>? themeResolver,
Key? key,
});

View File

@ -31,6 +31,7 @@ abstract class SimpleIconButtonComponent extends ButtonComponent
super.focusedStyle,
super.tappedStyle,
super.onPressed,
super.disabled,
super.themeResolver,
super.key,
});

View File

@ -14,6 +14,7 @@ abstract class $SimpleIconButtonComponentCWProxy {
SimpleIconButtonComponent focusedStyle(ButtonStyle<dynamic>? focusedStyle);
SimpleIconButtonComponent tappedStyle(ButtonStyle<dynamic>? tappedStyle);
SimpleIconButtonComponent onPressed(void Function(ControlState)? onPressed);
SimpleIconButtonComponent disabled(ValueNotifier<bool>? disabled);
SimpleIconButtonComponent themeResolver(
ThemeResolver<dynamic, dynamic, dynamic>? themeResolver);
SimpleIconButtonComponent key(Key? key);
@ -25,6 +26,7 @@ abstract class $SimpleIconButtonComponentCWProxy {
ButtonStyle<dynamic>? focusedStyle,
ButtonStyle<dynamic>? tappedStyle,
void Function(ControlState)? onPressed,
ValueNotifier<bool>? disabled,
ThemeResolver<dynamic, dynamic, dynamic>? themeResolver,
Key? key,
});

View File

@ -34,6 +34,7 @@ abstract class SymbolButtonComponent extends ButtonComponent
super.tappedStyle,
super.selectedStyle,
super.onPressed,
super.disabled,
super.themeResolver,
super.key,
});

View File

@ -17,6 +17,7 @@ abstract class $SymbolButtonComponentCWProxy {
SymbolButtonComponent tappedStyle(ButtonStyle<dynamic>? tappedStyle);
SymbolButtonComponent selectedStyle(ButtonStyle<dynamic>? selectedStyle);
SymbolButtonComponent onPressed(void Function(ControlState)? onPressed);
SymbolButtonComponent disabled(ValueNotifier<bool>? disabled);
SymbolButtonComponent themeResolver(
ThemeResolver<dynamic, dynamic, dynamic>? themeResolver);
SymbolButtonComponent key(Key? key);
@ -31,6 +32,7 @@ abstract class $SymbolButtonComponentCWProxy {
ButtonStyle<dynamic>? tappedStyle,
ButtonStyle<dynamic>? selectedStyle,
void Function(ControlState)? onPressed,
ValueNotifier<bool>? disabled,
ThemeResolver<dynamic, dynamic, dynamic>? themeResolver,
Key? key,
});

View File

@ -15,14 +15,23 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
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<FileSelectionButtons> createState() => _FileSelectionButtonsState();
}
class _FileSelectionButtonsState extends State<FileSelectionButtons> {
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(
BlocProvider(
create: (context) => InvalidButtonCubit()..freeze(),
child: FileSelectionButton(
leading: _leading(),
title: const TextWrapper(
'Enabled',
),
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(
BlocProvider(
create: (context) => InvalidButtonCubit()..hover(),
child: FileSelectionButton(
leading: _leading(),
title: const TextWrapper(
'Hovered',
),
subTitle: const TextWrapper('Subtitle'),
)
..bloc.onMouseEnter()
..bloc.freeze(),
),
),
const Gap(20),
FileSelectionButton(
BlocProvider(
create: (context) => InvalidButtonCubit()..focus(),
child: FileSelectionButton(
leading: _leading(),
title: const TextWrapper(
'Focused',
),
subTitle: const TextWrapper('Subtitle'),
)
..bloc.onFocus()
..bloc.freeze(),
),
),
const Gap(20),
FileSelectionButton(
BlocProvider(
create: (context) => InvalidButtonCubit()..tap(),
child: FileSelectionButton(
leading: _leading(),
title: const TextWrapper(
'Tapped',
),
subTitle: const TextWrapper('Subtitle'),
)
..bloc.onClickDown()
..bloc.freeze(),
),
),
const Gap(20),
FileSelectionButton(
BlocProvider(
create: (context) => InvalidButtonCubit()..invalidate(),
child: FileSelectionButton(
leading: _leading(),
title: const TextWrapper(
'Invalid',
),
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),
],
);

View File

@ -15,13 +15,22 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
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<FlatButtons> createState() => _FlatButtonsState();
}
class _FlatButtonsState extends State<FlatButtons> {
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),
],

View File

@ -15,12 +15,21 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
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<SimpleIconButtons> createState() => _SimpleIconButtonsState();
}
class _SimpleIconButtonsState extends State<SimpleIconButtons> {
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(
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(
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(
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),
BlocProvider(
create: (context) => ButtonCubit()..tap(),
child: const SimpleIconButton(
icon: Icon(
Icons.storm_outlined,
size: 17,
),
),
),
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,
),
)
..bloc.onClickDown()
..bloc.freeze(),
const Gap(20),
disabled: _dynamic,
),
],
),
],
);
}

View File

@ -15,14 +15,23 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
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<SymbolButtons> createState() => _SymbolButtonsState();
}
class _SymbolButtonsState extends State<SymbolButtons> {
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(
BlocProvider(
create: (context) => SelectableButtonCubit()..freeze(),
child: SymbolButton(
label: const TextWrapper('Enabled'),
icon: _icon(context),
)
..bloc.enable()
..bloc.freeze(),
),
),
const Gap(20),
// Disabled
SymbolButton(
label: const TextWrapper('Disabled'),
icon: _icon(context),
)
..bloc.disable()
..bloc.freeze(),
disabled: _disabled,
),
const Gap(20),
SymbolButton(
BlocProvider(
create: (context) => SelectableButtonCubit()..hover(),
child: SymbolButton(
label: const TextWrapper('Hovered'),
icon: _icon(context),
)
..bloc.onMouseEnter()
..bloc.freeze(),
),
),
const Gap(20),
SymbolButton(
BlocProvider(
create: (context) => SelectableButtonCubit()..focus(),
child: SymbolButton(
label: const TextWrapper('Focused'),
icon: _icon(context),
)
..bloc.onFocus()
..bloc.freeze(),
),
),
const Gap(20),
SymbolButton(
BlocProvider(
create: (context) => SelectableButtonCubit()..tap(),
child: SymbolButton(
label: const TextWrapper('Tapped'),
icon: _icon(context),
)
..bloc.onClickDown()
..bloc.freeze(),
),
),
const Gap(20),
SymbolButton(
BlocProvider(
create: (context) => SelectableButtonCubit()..select(),
child: SymbolButton(
label: const TextWrapper('Selected'),
icon: _icon(context),
)
..bloc.select()
..bloc.freeze(),
),
),
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(

View File

@ -10,6 +10,7 @@ environment:
dependencies:
flutter: { sdk: flutter }
flutter_bloc: ^8.1.2
flutter_localizations: { sdk: flutter }
gap: ^2.0.1

View File

@ -14,6 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
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';

View File

@ -23,8 +23,19 @@ import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
part 'button_state.dart';
class ButtonCubit extends Cubit<ButtonState> {
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<void> onMouseEnter() async {
if (state.isDisabled || state.isFreezed) {
return;
@ -32,6 +43,10 @@ class ButtonCubit extends Cubit<ButtonState> {
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<void> onMouseLeave() async {
if (state.isDisabled || state.isFreezed) {
return;
@ -40,6 +55,10 @@ class ButtonCubit extends Cubit<ButtonState> {
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<void> onFocus() async {
if (state.isDisabled || state.isFreezed) {
return;
@ -47,6 +66,10 @@ class ButtonCubit extends Cubit<ButtonState> {
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<void> onUnfocus() async {
if (state.isDisabled || state.isFreezed) {
return;
@ -54,6 +77,10 @@ class ButtonCubit extends Cubit<ButtonState> {
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<void> onClickDown() async {
if (state.isDisabled || state.isFreezed) {
return;
@ -61,39 +88,127 @@ class ButtonCubit extends Cubit<ButtonState> {
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<void> 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<void> 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<void> setState({
ControlState? state,
List<ExtraState>? 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<void> 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<void> 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<void> 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<void> 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<void> 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<void> 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<void> unfreeze() async {
emit(state.copyWith(freezed: false));
emit(
state.copyWith(
extraStates: state.extraStates
.where((element) => element != ExtraState.freezed)
.toList(),
),
);
}
}

View File

@ -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<ExtraState> 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<Object?> get props => [state, selected, invalid, freezed];
List<Object?> get props => [state, extraStates];
@override
bool? get stringify => true;
ButtonState copyWith({
ControlState? state,
bool? selected,
bool? invalid,
bool? freezed,
List<ExtraState>? extraStates,
}) =>
ButtonState(
state: state ?? this.state,
selected: selected ?? this.selected,
invalid: invalid ?? this.invalid,
freezed: freezed ?? this.freezed,
extraStates: extraStates ?? this.extraStates,
);
}

View File

@ -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<void> onClickUpIn() async {
if (state.isDisabled || state.isFreezed) {
return;
}
emit(
state.copyWith(
state: ControlState.hovered,
),
);
}
@override
FutureOr<void> 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<void> invalidate() async {
if (state.isDisabled || state.isFreezed) {
return;
emit(state.copyWith(extraStates: state.extraStates + [ExtraState.invalid]));
}
/// 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<void> validate() async {
emit(
state.copyWith(invalid: true),
);
}
FutureOr<void> fix() async {
if (state.isDisabled || state.isFreezed) {
return;
}
emit(
state.copyWith(invalid: false),
state.copyWith(
extraStates: state.extraStates
.where((element) => element != ExtraState.invalid)
.toList(),
),
);
}
}

View File

@ -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<void> 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<void> 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<void> unselect() async {
if (state.isDisabled || state.isFreezed) {
return;
}
emit(
state.copyWith(selected: false),
state.copyWith(
extraStates: state.extraStates
.where((element) => element != ExtraState.selected)
.toList(),
),
);
}
}

View File

@ -14,23 +14,22 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/foundation.dart';
import 'package:wyatt_ui_kit/src/components/buttons/cubit/button_cubit.dart';
class ExportableBloc<T extends StateStreamableSource<Object?>>
extends StatelessWidget {
const ExportableBloc({
required this.bloc,
required this.child,
super.key,
});
final T bloc;
final Widget child;
@override
Widget build(BuildContext context) => BlocProvider<T>.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<B extends ButtonCubit>(
ValueNotifier<bool>? state,
B bloc,
) {
if (state!.value) {
bloc.disable();
} else {
bloc.enable();
}
}
}

View File

@ -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,9 +76,7 @@ class FileSelectionButton extends FileSelectionButtonComponent
super.themeResolver as FileSelectionButtonThemeResolver?;
@override
Widget build(BuildContext context) => ExportableBloc(
bloc: _cubit,
child: FileSelectionButtonScreen(
Widget build(BuildContext context) => FileSelectionButtonScreen(
leading: leading,
title: title,
subTitle: subTitle,
@ -95,9 +88,9 @@ class FileSelectionButton extends FileSelectionButtonComponent
selectedStyle: selectedStyle,
invalidStyle: invalidStyle,
onPressed: onPressed,
disabled: disabled,
mainAxisSize: mainAxisSize,
themeResolver: themeResolver,
key: key,
),
);
}

View File

@ -45,6 +45,9 @@ class $FileSelectionButtonCWProxyImpl
FileSelectionButton onPressed(void Function(ControlState)? onPressed) =>
this(onPressed: onPressed);
@override
FileSelectionButton disabled(ValueNotifier<bool>? disabled) =>
this(disabled: disabled);
@override
FileSelectionButton themeResolver(
ThemeResolver<dynamic, dynamic, dynamic>? themeResolver) =>
this(themeResolver: themeResolver);
@ -64,6 +67,7 @@ class $FileSelectionButtonCWProxyImpl
ButtonStyle<dynamic>? selectedStyle,
ButtonStyle<dynamic>? invalidStyle,
void Function(ControlState)? onPressed,
ValueNotifier<bool>? disabled,
ThemeResolver<dynamic, dynamic, dynamic>? 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,

View File

@ -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<bool>? disabled;
final FileSelectionButtonThemeResolver? themeResolver;
@override
InvalidButtonCubit create(BuildContext context) => InvalidButtonCubit();
@override
InvalidButtonCubit init(BuildContext context, InvalidButtonCubit bloc) {
disabled?.addListener(
() => StateListener.listen<InvalidButtonCubit>(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 ??

View File

@ -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,9 +60,7 @@ class FlatButton extends FlatButtonComponent with $FlatButtonCWMixin {
super.themeResolver as FlatButtonThemeResolver?;
@override
Widget build(BuildContext context) => ExportableBloc(
bloc: _cubit,
child: FlatButtonScreen(
Widget build(BuildContext context) => FlatButtonScreen(
prefix: prefix,
suffix: suffix,
label: label,
@ -77,9 +70,9 @@ class FlatButton extends FlatButtonComponent with $FlatButtonCWMixin {
focusedStyle: focusedStyle,
tappedStyle: tappedStyle,
onPressed: onPressed,
disabled: disabled,
mainAxisSize: mainAxisSize,
themeResolver: themeResolver,
key: key,
),
);
}

View File

@ -37,6 +37,9 @@ class $FlatButtonCWProxyImpl implements $FlatButtonComponentCWProxy {
FlatButton onPressed(void Function(ControlState)? onPressed) =>
this(onPressed: onPressed);
@override
FlatButton disabled(ValueNotifier<bool>? disabled) =>
this(disabled: disabled);
@override
FlatButton themeResolver(
ThemeResolver<dynamic, dynamic, dynamic>? themeResolver) =>
this(themeResolver: themeResolver);
@ -54,6 +57,7 @@ class $FlatButtonCWProxyImpl implements $FlatButtonComponentCWProxy {
ButtonStyle<dynamic>? focusedStyle,
ButtonStyle<dynamic>? tappedStyle,
void Function(ControlState)? onPressed,
ValueNotifier<bool>? disabled,
ThemeResolver<dynamic, dynamic, dynamic>? 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,

View File

@ -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<ButtonCubit, ButtonState> {
this.focusedStyle,
this.tappedStyle,
this.onPressed,
this.disabled,
this.mainAxisSize,
this.themeResolver,
super.key,
@ -52,11 +54,23 @@ class FlatButtonScreen extends CubitScreen<ButtonCubit, ButtonState> {
final FlatButtonStyle? tappedStyle;
final void Function(ControlState state)? onPressed;
final ValueNotifier<bool>? 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 ??

View File

@ -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,9 +63,7 @@ class SimpleIconButton extends SimpleIconButtonComponent
super.themeResolver as SimpleIconButtonThemeResolver?;
@override
Widget build(BuildContext context) => ExportableBloc(
bloc: _cubit,
child: SimpleIconButtonScreen(
Widget build(BuildContext context) => SimpleIconButtonScreen(
icon: icon,
disabledStyle: disabledStyle,
normalStyle: normalStyle,
@ -78,8 +71,8 @@ class SimpleIconButton extends SimpleIconButtonComponent
focusedStyle: focusedStyle,
tappedStyle: tappedStyle,
onPressed: onPressed,
disabled: disabled,
themeResolver: themeResolver,
key: key,
),
);
}

View File

@ -31,6 +31,9 @@ class $SimpleIconButtonCWProxyImpl
SimpleIconButton onPressed(void Function(ControlState)? onPressed) =>
this(onPressed: onPressed);
@override
SimpleIconButton disabled(ValueNotifier<bool>? disabled) =>
this(disabled: disabled);
@override
SimpleIconButton themeResolver(
ThemeResolver<dynamic, dynamic, dynamic>? themeResolver) =>
this(themeResolver: themeResolver);
@ -45,6 +48,7 @@ class $SimpleIconButtonCWProxyImpl
ButtonStyle<dynamic>? focusedStyle,
ButtonStyle<dynamic>? tappedStyle,
void Function(ControlState)? onPressed,
ValueNotifier<bool>? disabled,
ThemeResolver<dynamic, dynamic, dynamic>? 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,
);

View File

@ -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<ButtonCubit, ButtonState> {
this.focusedStyle,
this.tappedStyle,
this.onPressed,
this.disabled,
this.themeResolver,
super.key,
});
@ -46,11 +48,24 @@ class SimpleIconButtonScreen extends CubitScreen<ButtonCubit, ButtonState> {
final SimpleIconButtonStyle? tappedStyle;
final void Function(ControlState state)? onPressed;
final ValueNotifier<bool>? 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 ??

View File

@ -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,9 +67,7 @@ class SymbolButton extends SymbolButtonComponent
super.themeResolver as SymbolButtonThemeResolver?;
@override
Widget build(BuildContext context) => ExportableBloc(
bloc: _cubit,
child: SymbolButtonScreen(
Widget build(BuildContext context) => SymbolButtonScreen(
icon: icon,
label: label,
disabledStyle: disabledStyle,
@ -85,9 +77,9 @@ class SymbolButton extends SymbolButtonComponent
tappedStyle: tappedStyle,
selectedStyle: selectedStyle,
onPressed: onPressed,
disabled: disabled,
mainAxisSize: mainAxisSize,
themeResolver: themeResolver,
key: key,
),
);
}

View File

@ -38,6 +38,9 @@ class $SymbolButtonCWProxyImpl implements $SymbolButtonComponentCWProxy {
SymbolButton onPressed(void Function(ControlState)? onPressed) =>
this(onPressed: onPressed);
@override
SymbolButton disabled(ValueNotifier<bool>? disabled) =>
this(disabled: disabled);
@override
SymbolButton themeResolver(
ThemeResolver<dynamic, dynamic, dynamic>? themeResolver) =>
this(themeResolver: themeResolver);
@ -55,6 +58,7 @@ class $SymbolButtonCWProxyImpl implements $SymbolButtonComponentCWProxy {
ButtonStyle<dynamic>? tappedStyle,
ButtonStyle<dynamic>? selectedStyle,
void Function(ControlState)? onPressed,
ValueNotifier<bool>? disabled,
ThemeResolver<dynamic, dynamic, dynamic>? 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,
);
}

View File

@ -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<bool>? disabled;
final SymbolButtonThemeResolver? themeResolver;
@override
SelectableButtonCubit create(BuildContext context) => SelectableButtonCubit();
@override
SelectableButtonCubit init(BuildContext context, SelectableButtonCubit bloc) {
disabled?.addListener(
() => StateListener.listen<SelectableButtonCubit>(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 ??