avoid_expanded_as_spacer
v0.4.0 Warning Fix Widget Replacement
Flags Expanded widgets that wrap an empty SizedBox or Container as their child. This pattern is equivalent to using the Spacer widget, which is purpose-built for this exact use case.
Why use this rule
Section titled “Why use this rule”Spacer exists for exactly this. Expanded(child: SizedBox()) builds two
widgets to do what one does, and the reader has to work out that the empty box
is deliberate rather than a leftover. The quick fix swaps the whole expression
for a Spacer, carrying flex across.
// Push the action button to the far end of the row.Row( children: [ const Text('Total'), const Expanded(child: SizedBox()), TextButton(onPressed: onPay, child: const Text('Pay')), ],);Row( children: [ const Text('Total'), const Spacer(), TextButton(onPressed: onPay, child: const Text('Pay')), ],);Examples
Section titled “Examples”An empty Container counts too
Section titled “An empty Container counts too”// Don'tExpanded(child: Container());
// Doconst Spacer();flex is carried across
Section titled “flex is carried across”Use it to split the leftover space unevenly:
// Don't — the gap after the title is twice the one before itRow( children: [ const Expanded(flex: 1, child: SizedBox()), const Text('Title'), const Expanded(flex: 2, child: SizedBox()), ],);
// DoRow( children: [ const Spacer(), const Text('Title'), const Spacer(flex: 2), ],);Spacer’s flex defaults to 1, so the first one can drop the argument.
A key on the empty child does not save it
Section titled “A key on the empty child does not save it”key is the one argument the rule tolerates on the child — a SizedBox that
carries only a key is still empty:
// Don'tconst Expanded(child: SizedBox(key: ValueKey('gap')));
// Doconst Spacer();The fix keeps a key written on the Expanded, and drops one written on
the child, since the child is what disappears:
// Don'tconst Expanded(key: ValueKey('gap'), child: SizedBox());
// Doconst Spacer(key: ValueKey('gap'));Known limitations
Section titled “Known limitations”The child has to be genuinely empty. A SizedBox with any argument other
than key is sizing something, and is left alone:
// Not reported — this is a fixed 24px gap, and `Spacer` cannot express itRow(children: [const Text('A'), const SizedBox(width: 24), const Text('B')]);
// Not reported — the box has a size, so the Expanded is not just a spacerExpanded(child: SizedBox(height: 40));For fixed gaps like the first one, see
prefer_spacing
and use_gap.
Flexible is not matched, only Expanded. Flexible(child: SizedBox())
uses FlexFit.loose, which takes no space at all — it is a different (and
usually mistaken) thing, not a spacer.
Spacer only works inside a Flex. Row, Column and Flex are the only
valid parents; the rule does not check that, so a stray
Expanded(child: SizedBox()) outside one will be reported and the rewrite will
throw at runtime. In practice Expanded has the same restriction, so such code
was already broken.
Configuration
Section titled “Configuration”This rule is in the recommended preset, so it is on with
preset: recommended or preset: opinionated. Add it to preset: core with
avoid_expanded_as_spacer: true.
To turn it off:
rules: avoid_expanded_as_spacer: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”prefer_spacing— Use the spacing argument on Row/Column instead of SizedBox spacers.use_gap— Use Gap widget instead of SizedBox for spacing in multi-child widgets.avoid_border_all— Use Border.fromBorderSide instead of Border.all for const support.avoid_incorrect_image_opacity— Use Image’s opacity parameter instead of wrapping in Opacity.