In Flutter, how do a Row's spacing parameter, Spacer and a SizedBox gap differ, and when should a Wrap replace the Row?
answer
- fixed gap versus flexible gap
- spacing arrived in 3.27
- only between children
- Spacer is Expanded with an empty child
- Wrap: runs instead of overflow
basics
~20 sRow.spacing puts a fixed gap between adjacent children; a SizedBox is a fixed gap you place yourself; Spacer is an Expanded that absorbs leftover space. Use Wrap when children should flow onto new lines instead of overflowing.
solid answer
~40 sSince Flutter 3.27, `Row` and `Column` take `spacing`: a fixed main-axis gap between adjacent children, never before the first or after the last, that adds to any gaps `mainAxisAlignment` creates and counts toward the children's total size, so it can contribute to overflow. A `SizedBox(width: 8)` does the same for one position only, useful when gaps differ. `Spacer` is different in kind: it is `Expanded(flex: flex, child: SizedBox.shrink())`, a flexible child that takes a share of the free space and pushes neighbours apart, so it leaves `mainAxisAlignment` nothing to distribute. When the children's total width can exceed the available width and they should flow onto more lines — chips, tags, allergen badges — use `Wrap` with `spacing` and `runSpacing`. `Wrap` has no flex, so `Expanded` inside it is an error.
code
dart · 15 linesimport 'package:flutter/material.dart';
Widget labelHeader() => const Row(
children: [
Text('Amount per serving'),
Spacer(), // flexible: takes all leftover width
Text('% Daily Value'),
],
);
Widget allergenBadges(List<String> allergens) => Wrap(
spacing: 8, // between badges in a run
runSpacing: 4, // between runs
children: [for (final String a in allergens) Chip(label: Text(a))],
);go deeper
Recall the three gap tools, that Spacer takes leftover space, and that Wrap moves children to a new line instead of overflowing.
Explain how spacing combines with mainAxisAlignment and counts toward size, and why Spacer is an Expanded under the hood.
Choose between Row and Wrap from the content's variability, and catch Spacer or Expanded misuse in unbounded or non-flex parents during review.
Standardise gap handling (spacing versus spacing-scale tokens and wrappers) across a component library so layouts stay consistent and migrations to new APIs are cheap.
## Three ways to make a gap Putting space between children of a `Row` or `Column` is so common that Flutter now has three tools with different behaviour. | Tool | Gap size | Where | Takes flex space? | Available since | |---|---|---|---|---| | `spacing:` on `Row`/`Column`/`Flex` | fixed | between every adjacent pair | no | 3.27 | | `SizedBox(width: …)` / `SizedBox(height: …)` | fixed | exactly where you put it | no | always | | `Spacer(flex: n)` | whatever is left | where you put it | yes | always | ## The spacing parameter `spacing` (default `0.0`, must not be negative) was added to `Flex`, and therefore `Row` and `Column`, in Flutter 3.27. Its rules, from the framework documentation: - It is applied **only between children** on the main axis, never before the first child or after the last. - It **combines with `mainAxisAlignment`**. With `spaceBetween` and 100 pixels of free space between two children and `spacing: 10`, there are at least 10 pixels between them plus the distributed free space. - It is **part of the size**. The flex algorithm starts its running total at `spacing * (childCount - 1)`, so a `Row` with ten children and `spacing: 12` needs 108 pixels of gaps. With `MainAxisSize.min` the Row is that much larger; in a tight space it can overflow. Before 3.27 the idiom was a `SizedBox` between each pair, often generated with a helper. `spacing` replaces that for uniform gaps; the framework itself migrated many `SizedBox` gaps to it in later releases. ## SizedBox gaps A `SizedBox(width: 8)` in a `Row` is simply an inflexible child with a fixed width and no content. Use it when: 1. Gaps differ between positions, for example a wider gap before the amount on a nutrition row. 2. You need one gap next to a conditional child without adding spacing everywhere. ## Spacer: a flexible gap `Spacer` builds `Expanded(flex: flex, child: const SizedBox.shrink())`, with `flex` defaulting to 1 and required to be positive. Consequences: - It consumes free space, so `mainAxisAlignment` has nothing left to distribute. - Several spacers split the free space by their flex factors: `Spacer(flex: 2)` gets twice the space of `Spacer()`. - It competes with `Expanded` and `Flexible` siblings for the same leftover space. - It is a flex child, so it fails in a Row whose width is unbounded, for example inside a horizontal scroll view. On a nutrition-label header, `Row(children: [Text('Amount per serving'), Spacer(), Text('% Daily Value')])` pins the two labels to opposite edges, the same effect as `spaceBetween` with two children. ## When Wrap replaces Row A `Row` lays everything on one line. If the children do not fit, it overflows. `Wrap` lays children along its `direction` (default `Axis.horizontal`) and starts a new **run** when the next child does not fit. Its main parameters: - `spacing` — gap between children within a run (default `0.0`). - `runSpacing` — gap between runs (default `0.0`). - `alignment` — how children are placed within a run (`WrapAlignment.start` by default; also `end`, `center`, `spaceBetween`, `spaceAround`, `spaceEvenly`). - `runAlignment` — how the runs are placed in the cross axis. - `crossAxisAlignment` — how children of different heights align within a run (`WrapCrossAlignment.start` by default). Use `Wrap` for chips, filter tags or allergen badges under a nutrition label: the count and label lengths vary, and flowing onto another line is better than truncating. Keep `Row` when the design is one line with a flexible child that should shrink, such as a name that ellipsises. `Wrap` does not support flex: its children are laid out at their natural size. An `Expanded` or `Flexible` directly inside it reports 'Incorrect use of ParentDataWidget.', because those widgets only work under a `Flex`.
- In Flutter, does Row(spacing: 16) with MainAxisAlignment.spaceBetween put 16 pixels at the ends?No. `spacing` is applied only between adjacent children, never before the first or after the last. With `spaceBetween`, the ends stay flush and each gap is the distributed free space plus 16. With `spaceAround` or `spaceEvenly`, the end gaps come from the free space alone.
- Why can adding spacing to a Flutter Row cause an overflow that was not there before?The flex algorithm counts `spacing * (childCount - 1)` as used main-axis space before sizing flexible children. A row that barely fit now needs that much more width, and if the inflexible children plus gaps exceed the Row's maximum, it overflows just as if you had added fixed `SizedBox` gaps.
saying these in an interview costs you the question
- Row spacing also adds space before the first and after the last child.
- Spacer and SizedBox(width: 8) behave the same way.
- Spacing is free and cannot contribute to an overflow.
- Expanded inside a Wrap fills the rest of the current run.
- mainAxisAlignment still distributes space when a Spacer is present.