Skip to content

Commit

Permalink
fix: Updating checkbox to match designs (#83)
Browse files Browse the repository at this point in the history
* fix border

* updating indeterminate

* fixed disabled styling and widgetbook
  • Loading branch information
mikecoomber authored Jun 4, 2024
1 parent 890d267 commit 05a32ac
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 50 deletions.
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,
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

0 comments on commit 05a32ac

Please sign in to comment.