WCheckbox
A utility-first checkbox component that translates Tailwind-style classes into a fully customisable checkbox widget. It sidesteps the limitations of native Flutter checkboxes by using a composable architecture of WAnchor and WDiv.
On this page
checkbox/checkbox_basic
A live demo of this example follows.
checkbox_basic.dart
View source
Open demo
WCheckbox(
value: isChecked,
onChanged: (val) => setState(() => isChecked = val),
className: 'w-6 h-6 rounded-md border-gray-400 checked:bg-indigo-600',
)
Basic Usage#
The WCheckbox widget provides a highly flexible alternative to native Flutter checkboxes. It supports all Wind utility classes, allowing for complex styling like custom borders, focus rings, and state-based colours using the checked: prefix.
WCheckbox(
value: _value,
onChanged: (val) => setState(() => _value = val),
className: 'w-5 h-5 rounded border-2 border-slate-300 checked:bg-blue-500 checked:border-transparent',
)
Constructor#
const WCheckbox({
Key? key,
required bool value,
ValueChanged<bool>? onChanged,
String? className,
String? iconClassName,
bool disabled = false,
IconData? checkIcon,
Set<String>? states,
})
Props#
| Prop | Type | Default | Description |
|---|---|---|---|
value |
bool |
Required | Whether the checkbox is currently checked. |
onChanged |
ValueChanged<bool>? |
null |
Callback triggered when the checkbox state is toggled. null makes the checkbox display-only, exactly like disabled: true. See Accessibility. |
className |
String? |
null |
Wind utility classes for the checkbox container (dimensions, border, background). |
iconClassName |
String? |
null |
Utility classes applied to the check icon (e.g., 'text-white text-xs'). |
disabled |
bool |
false |
When true, prevents interaction and applies the disabled: prefix styles. |
checkIcon |
IconData? |
null |
Custom icon shown when value is true. When null, the widget renders Icons.check. |
states |
Set<String>? |
null |
Custom state identifiers merged with built-in checked and disabled states. |
Layout Modes#
Dimensions#
A checkbox is 20x20 by default, because its built-in classes start with w-5 h-5. Your className is appended after those defaults, so add width (w-{n}) and height (h-{n}) classes only to change the clickable area and visual size.
WCheckbox(
value: true,
className: 'w-4 h-4 rounded-sm', // Small checkbox
)
WCheckbox(
value: true,
className: 'w-8 h-8 rounded-full', // Large circular checkbox
)
Event Handling#
The onChanged callback is triggered when the user taps the checkbox. It provides the toggled boolean value.
WCheckbox(
value: isChecked,
onChanged: (bool newValue) {
setState(() {
isChecked = newValue;
});
},
)
Leaving onChanged null is how you render a display-only checkbox, such as a read-only summary row or a tile whose own tap drives the toggle.
Accessibility#
WCheckbox publishes two platform semantics nodes while it is interactive. The outer node carries the checked state but no tap action, so getByRole('checkbox') resolves and a screen reader announces whether the box is ticked. The inner node is the WAnchor button, and it carries the tap action. A display-only checkbox has no gesture, so only the outer node remains. The widget renders its check glyph as the only child, so it has no name of its own, and a WText sitting beside it in a row is a visible label rather than a semantic one: the nodes stay separate. Wrap the pair in MergeSemantics to publish a single node carrying both the text and the checked state.
// One node, named "Accept terms", reporting checked.
MergeSemantics(
child: WDiv(
className: 'flex flex-row items-center gap-3',
children: [
WCheckbox(
value: accepted,
onChanged: (val) => setState(() => accepted = val),
className: 'w-5 h-5 rounded border',
),
const WText(
'Accept terms',
className: 'text-slate-900 dark:text-white',
),
],
),
)
A null onChanged is read exactly like disabled: true. There is no callback to run, so the checkbox attaches no gesture, publishes no tap action, reports itself as not enabled, and activates the disabled: prefix. That last part is visible: a display-only checkbox styled disabled:opacity-50 renders dimmed.
// Interactive: tappable, announced as enabled.
WCheckbox(
value: isChecked,
onChanged: (val) => setState(() => isChecked = val),
className: 'w-5 h-5 rounded border',
)
// Display-only: no tap action, announced as unavailable, disabled: styles apply.
const WCheckbox(
value: true,
className: 'w-5 h-5 rounded border disabled:opacity-50',
)
WRadio and WSwitch read a null callback the same way, so the three controls behave alike.
State Variants#
WCheckbox uses the checked: prefix to apply styles when the value is true. It also supports standard interaction prefixes like hover:, focus:, and disabled:.
WCheckbox(
value: isChecked,
className: 'border-gray-300 checked:bg-blue-600 checked:border-transparent hover:border-blue-400 disabled:opacity-50',
)
Styling Examples#
Custom Icons and Colors#
You can completely change the look of the checkbox by providing a custom icon and using background utilities.
WCheckbox(
value: true,
checkIcon: Icons.favorite,
className: 'w-6 h-6 bg-pink-100 border-pink-300 checked:bg-pink-500 checked:border-transparent',
iconClassName: 'text-white text-xs',
)
Validation States#
Using the states property allows you to apply conditional styling for errors or success states.
WCheckbox(
value: false,
states: {'error'},
className: 'border-gray-300 error:border-red-500 error:ring-2 error:ring-red-100',
)
All Supported Classes#
| Category | Classes |
|---|---|
| Sizing | w-{n}, h-{n}, min-w-{n}, aspect-square |
| Spacing | p-{n}, m-{n} |
| Appearance | rounded, border, bg-{color}, shadow |
| States | checked:, hover:, focus:, disabled:, error: |
| Effects | opacity-{n}, ring, duration-{n}, ease-{n} |
Customizing Theme#
The default checked colour can be controlled via the primary colour in your WindThemeData.
WindThemeData(
colors: {
'primary': Colors.indigo,
},
)
Related Documentation#
- WFormCheckbox - Form-integrated checkbox with label and validation.
- WAnchor - The base interactive component used by WCheckbox.
- WIcon - Documentation for the check icon styling.