Programming with AI
ENRU
11.3

Widgets

the tree, composition, and why a margin is a widget too

What it is

In Flutter everything is a widget. A button, text, a margin, alignment, the screen itself. An interface is assembled not out of properties, as in CSS, but out of widgets nested inside one another.

That is the main difference from everything earlier in the book, and the main source of bafflement when reading: the code looks like a deep staircase of brackets.

What it looks like in code

Scaffold(
  appBar: AppBar(title: const Text('Weather')),
  body: Padding(
    padding: const EdgeInsets.all(16),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(city.name, style: Theme.of(context).textTheme.titleLarge),
        const SizedBox(height: 8),
        Text(city.temp == null ? 'no data' : '${city.temp}°'),
        const Spacer(),
        ElevatedButton(
          onPressed: () => onRemove(city.name),
          child: const Text('Remove'),
        ),
      ],
    ),
  ),
)
Diagram from the book — to be redrawn for the web
The same thing as a tree. The margin and the gap are nodes just like the text and the button.

How to read it

Read by nesting, not by lines. The indentation in the code repeats the tree's structure, so the most useful thing while reading is to watch the indentation level.

child is one child, children is a list. Those two words let you reconstruct the whole structure: where it branches, where it is a chain.

Margins and gaps are widgets too. That is the main difference from CSS:

What you needWidget
a margin aroundPadding
a gap betweenSizedBox(height: 8)
take all the free spaceExpanded, Spacer
alignAlign, Center
in a column / in a rowColumn, Row
stacked on top of each otherStack

Which is why the tree is so deep: what would be two lines of properties in CSS is two levels of nesting here.

const in front of a widget. Not a formality: such a widget is created once and isn't recreated on a redraw. An AI often leaves it out, and the analyzer complains about that.

Theme.of(context) — take a value from the application's theme: a colour, a font, a size. The counterpart of CSS variables (article 8.3): better than writing sizes in as numbers.

Row, Column and overflow

The most frequent visual breakage in Flutter: the content doesn't fit, and a yellow-and-black stripe with an overflow message appears on the screen.

The cause is almost always the same: text longer than the screen was put into a Row. The cure is wrapping it in Expanded or Flexible — they say "take the space that's there, and no more".

What to tell the AI

Prompt
Explain this widget tree: what each part is responsible for.
One line per nesting level, no code.
Prompt
Pull the contents of Column out into a separate CityInfo
widget in lib/widgets/. Pass the parameters through the
constructor. Don't change the behaviour.
Prompt
With a long city name this Row overflows. Fix it with
Expanded, and explain why that rather than a fixed width.

What to look for

A ten-level tree in one file. It breaks into widgets.

No const where the widget doesn't depend on data. Needless recreation on every redraw.

Sizes as numbers instead of the theme. fontSize: 17 instead of Theme.of(context) — the same magic numbers as in article 8.3.

A Container for one thing. If you only need a margin, that's Padding; only alignment, Align. Container can do everything at once and therefore hides the intent.

Logic inside build. Network requests and computations in the build method: it is called on every redraw, which is to say often.

Fixed sizes for one screen. Phones differ; MediaQuery and Expanded exist precisely for that.

Cheat sheet

  • In Flutter everything is a widget, margins and gaps included.

  • child is one, children is a list; the tree follows from them.

  • Read by nesting, not by lines.

  • const in front of a widget saves a recreation on redraw.

  • Sizes and colours come from the theme, not from numbers.

  • A yellow-and-black stripe means overflow; the cure is Expanded.