Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Updating checkbox to match designs #83

Merged
merged 3 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions example/lib/pages/components/checkbox_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class CheckBoxExample extends StatefulWidget {
}

class _CheckBoxExampleState extends State<CheckBoxExample> {
bool? isChecked = true;
bool isChecked = true;
bool isEnabled = true;
bool useIndeterminate = false;

Expand Down Expand Up @@ -76,9 +76,9 @@ Row getCheckBoxRow({required bool isEnabled, bool isSharp = true}) {
onChanged: isEnabled ? (value) => {} : null,
),
ZetaCheckbox(
value: null,
rounded: !isSharp,
onChanged: isEnabled ? (value) => {} : null,
value: false,
)
]);
}
10 changes: 7 additions & 3 deletions example/widgetbook/pages/components/checkbox_widgetbook.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import '../../test/test_components.dart';
import '../../utils/utils.dart';

Widget checkboxUseCase(BuildContext context) {
bool? b = true;
bool b = true;

return WidgetbookTestWidget(
widget: StatefulBuilder(
builder: (context, setState) {
dynamic onChanged =
context.knobs.boolean(label: 'Enabled', initialValue: true) ? (b2) => setState(() => b = b2) : null;
ValueChanged<bool>? onChanged = context.knobs.boolean(
label: 'Enabled',
initialValue: true,
)
? (b2) => setState(() => b = b2)
: null;
return ZetaCheckbox(
value: b,
onChanged: onChanged,
Expand Down
93 changes: 48 additions & 45 deletions lib/src/components/checkbox/checkbox.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,24 @@ import '../../../zeta_flutter.dart';
/// Zeta Checkbox.
///
/// Checkboxes allow users to select one or more items from a set. Checkboxes can turn an option on or off.
///
/// The checkbox itself does not maintain any state. Instead, when the state of
/// the checkbox changes, the widget calls the [onChanged] callback.
/// Widgets that use a checkbox should listen for the [onChanged] callback and
/// rebuild the checkbox with a new [value] to update the visual appearance of
/// the checkbox.
class ZetaCheckbox extends FormField<bool> {
/// Constructs a [ZetaCheckbox].
ZetaCheckbox({
super.key,
this.value = false,
required this.value,
this.label,
this.onChanged,
this.rounded = true,
this.useIndeterminate = false,
super.validator,
super.autovalidateMode,
super.restorationId,
super.key,
}) : super(
initialValue: value,
enabled: onChanged != null,
Expand All @@ -25,13 +31,13 @@ class ZetaCheckbox extends FormField<bool> {
label: label,
onChanged: (changedValue) {
field.didChange(changedValue);
onChanged?.call(changedValue);
onChanged?.call(changedValue!);
},
rounded: rounded,
useIndeterminate: useIndeterminate,
value: value,
error: !field.isValid,
enabled: onChanged != null,
disabled: onChanged == null,
);
},
);
Expand All @@ -45,10 +51,10 @@ class ZetaCheckbox extends FormField<bool> {
final bool useIndeterminate;

/// Whether the checkbox is selected, unselected or null (indeterminate)
final bool? value;
final bool value;

/// Called when the value of the checkbox should change.
final ValueChanged<bool?>? onChanged;
final ValueChanged<bool>? onChanged;

/// The label displayed next to the checkbox
final String? label;
Expand All @@ -63,7 +69,7 @@ class ZetaCheckbox extends FormField<bool> {
..add(StringProperty('label', label))
..add(DiagnosticsProperty<bool>('useIndeterminate', useIndeterminate))
..add(DiagnosticsProperty<bool>('rounded', rounded))
..add(ObjectFlagProperty<ValueChanged<bool?>?>.has('onChanged', onChanged));
..add(ObjectFlagProperty<ValueChanged<bool>?>.has('onChanged', onChanged));
}
}

Expand All @@ -75,17 +81,17 @@ class ZetaCheckboxFormFieldState extends FormFieldState<bool> {

class _Checkbox extends StatefulWidget {
const _Checkbox({
this.value = false,
required this.onChanged,
this.disabled = false,
this.value = false,
this.label,
this.rounded = true,
this.useIndeterminate = false,
this.error = false,
required this.enabled,
});

/// Whether the checkbox is selected, unselected or null (indeterminate)
final bool? value;
final bool value;

/// Called when the value of the checkbox should change.
final ValueChanged<bool?> onChanged;
Expand All @@ -103,7 +109,7 @@ class _Checkbox extends StatefulWidget {

final bool error;

final bool enabled;
final bool disabled;

@override
State<_Checkbox> createState() => _CheckboxState();
Expand All @@ -116,45 +122,39 @@ class _Checkbox extends StatefulWidget {
..add(DiagnosticsProperty<bool>('rounded', rounded))
..add(DiagnosticsProperty<bool>('useIndeterminate', useIndeterminate))
..add(DiagnosticsProperty<bool>('error', error))
..add(DiagnosticsProperty<bool>('enabled', enabled))
..add(DiagnosticsProperty<bool>('enabled', disabled))
..add(ObjectFlagProperty<ValueChanged<bool?>>.has('onChanged', onChanged));
}
}

class _CheckboxState extends State<_Checkbox> {
bool? get _value => widget.useIndeterminate ? widget.value : (widget.value ?? false);

bool? get _updatedValue {
if (widget.useIndeterminate) {
if (widget.value == null) {
return false;
} else if (widget.value!) {
return null;
} else {
return true;
}
} else {
return !_value!;
}
}
bool get _checked => widget.value;

bool _isFocused = false;
bool _isHovered = false;

void _setHovered(bool isHovered) {
if (!widget.disabled) {
setState(() {
_isHovered = isHovered;
});
}
}

@override
Widget build(BuildContext context) {
return Semantics(
mixed: widget.useIndeterminate,
enabled: widget.enabled,
enabled: !widget.disabled,
thelukewalton marked this conversation as resolved.
Show resolved Hide resolved
child: MouseRegion(
cursor: widget.enabled ? SystemMouseCursors.click : SystemMouseCursors.forbidden,
onEnter: (event) => setState(() => _isHovered = true),
onExit: (event) => setState(() => _isHovered = false),
child: widget.enabled
cursor: !widget.disabled ? SystemMouseCursors.click : SystemMouseCursors.forbidden,
onEnter: (event) => _setHovered(true),
onExit: (event) => _setHovered(false),
child: !widget.disabled
? FocusableActionDetector(
onFocusChange: (bool focus) => setState(() => _isFocused = focus),
child: GestureDetector(
onTap: widget.enabled ? () => widget.onChanged.call(_updatedValue) : null,
onTap: !widget.disabled ? () => widget.onChanged.call(!_checked) : null,
child: _buildContent(context),
),
)
Expand All @@ -166,17 +166,17 @@ class _CheckboxState extends State<_Checkbox> {
Flex _buildContent(BuildContext context) {
final theme = Zeta.of(context);

final icon = _value != null && !_value!
final icon = !_checked
? const SizedBox.shrink()
: Icon(
_value != null
!widget.useIndeterminate
? widget.rounded
? ZetaIcons.check_mark_round
: ZetaIcons.check_mark_sharp
: widget.rounded
? ZetaIcons.remove_round
: ZetaIcons.remove_sharp,
color: widget.enabled ? theme.colors.white : theme.colors.textDisabled,
color: !widget.disabled ? theme.colors.white : theme.colors.iconDisabled,
size: ZetaSpacing.x3_5,
);

Expand All @@ -188,15 +188,15 @@ class _CheckboxState extends State<_Checkbox> {
duration: const Duration(milliseconds: 200),
decoration: BoxDecoration(
boxShadow: [
if (_isFocused && widget.enabled)
if (_isFocused && !widget.disabled)
BoxShadow(
spreadRadius: 2,
blurStyle: BlurStyle.solid,
color: theme.colors.blue.shade50,
),
],
color: _getBackground(theme),
border: widget.enabled ? Border.all(color: _getBorderColor(theme), width: ZetaSpacing.x0_5) : null,
border: Border.all(color: _getBorderColor(theme), width: ZetaSpacing.x0_5),
borderRadius: widget.rounded ? ZetaRadius.minimal : ZetaRadius.none,
),
width: ZetaSpacing.x5,
Expand All @@ -217,19 +217,22 @@ class _CheckboxState extends State<_Checkbox> {

Color _getBackground(Zeta theme) {
final ZetaColorSwatch color = widget.error ? theme.colors.error : theme.colors.primary;
if (!widget.enabled) return theme.colors.surfaceDisabled;
if (_value != null && !_value!) return theme.colors.surfacePrimary;
if (_isHovered) return color.hover;
if (widget.disabled) return theme.colors.surfaceDisabled;
if (!_checked) return theme.colors.surfacePrimary;
if (_isHovered) return theme.colors.borderHover;

return color;
}

Color _getBorderColor(Zeta theme) {
final ZetaColorSwatch color = widget.error ? theme.colors.error : theme.colors.cool;

if (_isHovered) return color.shade90;
if (_checked || widget.error || widget.disabled) {
return _getBackground(theme);
}
if (_isHovered) {
return theme.colors.cool.shade90;
}

return color;
return theme.colors.cool.shade70;
}

@override
Expand Down