Reader Stacks

How to Create a Checkbox in Flutter

Checkbox itself is a controlled widget with no internal state of its own — every value change has to be reflected back through setState(), or the checkbox silently reverts on the next rebuild.

Flutter's Checkbox widget is fully controlled — it has no internal state of its own, which means every value change must be explicitly reflected back through the parent widget's own state or the checkbox will appear stuck.

A basic checkbox

bool _isChecked = false;

Checkbox(
  value: _isChecked,
  onChanged: (value) {
    setState(() {
      _isChecked = value ?? false;
    });
  },
)

Without setState() inside onChanged, the checkbox visually reverts to its previous state on the next rebuild — since Checkbox is purely controlled by its value parameter, it has no memory of a click that wasn't also reflected in the parent's own state.

A checkbox with a label, using CheckboxListTile

CheckboxListTile(
  title: const Text('Subscribe to newsletter'),
  value: _isSubscribed,
  onChanged: (value) {
    setState(() {
      _isSubscribed = value ?? false;
    });
  },
)

CheckboxListTile combines a checkbox with a tappable label and proper spacing in one widget — genuinely more convenient than manually placing a Checkbox and Text side by side in a Row, and it also makes the entire row (not just the small checkbox itself) tappable.

A tristate checkbox (checked, unchecked, or indeterminate)

bool? _selectAll = false;

Checkbox(
  tristate: true,
  value: _selectAll,
  onChanged: (value) {
    setState(() {
      _selectAll = value;
    });
  },
)

tristate: true allows a third, indeterminate visual state (typically a dash rather than a check or empty box) — commonly used for a "select all" checkbox that should show this indeterminate state when only some, not all, items in a list are individually selected.

A list of checkboxes with a working "select all"

List _selected = List.filled(items.length, false);

bool? get _allSelected {
  if (_selected.every((s) => s)) return true;
  if (_selected.every((s) => !s)) return false;
  return null; // some selected — indeterminate
}

void _toggleAll(bool? value) {
  setState(() {
    _selected = List.filled(items.length, value ?? false);
  });
}

Computing the "select all" checkbox's own value from the individual items' state (rather than tracking it separately) keeps the two genuinely in sync — a manually tracked separate boolean can drift out of sync if items are checked or unchecked individually.

Customizing a checkbox's color and shape

Checkbox(
  value: _isChecked,
  onChanged: (value) => setState(() => _isChecked = value ?? false),
  activeColor: Colors.deepPurple,
  checkColor: Colors.white,
  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
)