Всё есть виджет: дерево виджетов

В Flutter кнопка, текст, отступ и даже само приложение — всё это виджеты, вложенные друг в друга деревом.

Суть: виджет — это описание части интерфейса. Виджеты вкладываются друг в друга, образуя дерево. Flutter обходит это дерево и рисует пиксели на экране.

Композиция вместо настройки — это смена мышления, которая поначалу удивляет приходящих из вёрстки. В вебе вы бы дописали свойство к элементу; во Flutter вы оборачиваете виджет в другой виджет, который добавляет нужное поведение. Нужен отступ — Padding, нужно выравнивание — Center, нужен жест — GestureDetector. Каждый виджет делает одну вещь хорошо, а сложный интерфейс рождается из их вложенности. Эту идею стоит принять как можно раньше.

Главная ментальная модель Flutter укладывается в одну фразу: everything is a widget. Текст — виджет Text. Кнопка — виджет. Отступ вокруг кнопки — отдельный виджет Padding. Колонка, выстраивающая элементы вертикально, — виджет Column. Даже выравнивание по центру — это виджет Center. Вы не настраиваете свойства, как в вёрстке; вы оборачиваете один виджет другим.

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Text('Привет, Flutter!'),
        ),
      ),
    );
  }
}

Обратите внимание на вложенность: MaterialApp содержит Scaffold, тот — Center, а CenterText. Это и есть дерево виджетов в коде.

Как дерево работает под капотом

Каждый виджет — это всего лишь лёгкое описание того, как должен выглядеть кусок интерфейса. Сам по себе он ничего не рисует. Flutter обходит дерево сверху вниз, и на основе виджетов строит два других дерева: Element tree (что сейчас на экране) и Render tree (как это нарисовать в пикселях). Виджеты дёшевы и пересоздаются часто; тяжёлые элементы и render-объекты Flutter переиспользует.

  MaterialApp                  <- корень приложения
     |
   Scaffold                    <- каркас экрана (фон, AppBar, body)
     |
   Center                      <- выравнивание по центру
     |
   Text('Привет, Flutter!')    <- лист дерева, виден пользователю

  Чтение сверху вниз = вложенность в коде:
  Center( child: Text(...) )

Поскольку виджеты — это просто классы с конструкторами (что вы уже изучили!), создание интерфейса сводится к вызову конструкторов с именованными параметрами. Свойство child хранит вложенный виджет, а children — список виджетов.

# Модель дерева виджетов: вложенные узлы и обход для "отрисовки"
def widget(kind, child=None, children=None, text=None):
    return {'kind': kind, 'child': child, 'children': children or [], 'text': text}

tree = widget('Center', child=widget('Text', text='Привет, Flutter!'))

def render(node, depth=0):
    label = node['kind'] + (f": {node['text']}" if node['text'] else '')
    print('  ' * depth + label)
    if node['child']:
        render(node['child'], depth + 1)
    for c in node['children']:
        render(c, depth + 1)

render(tree)   # печатает дерево с отступами, как обход Flutter

Частые ошибки

  • Искать «настройки» виджета вместо обёртки. Чтобы добавить отступ, не ищите свойство — оберните виджет в Padding.
  • Забыть const у неизменяемых виджетов — теряется оптимизация перестроения.
  • Путать child и children. Первый — один вложенный виджет, второй — список.

Best practices

  • Думайте деревом: рисуя экран, мысленно стройте иерархию обёрток сверху вниз.
  • Ставьте const перед виджетами, которые не зависят от меняющихся данных.
  • Разбивайте большие деревья на отдельные классы-виджеты — об этом следующий урок.

Полезно знать про инструмент Flutter Inspector в среде разработки: он показывает то самое дерево виджетов вживую, позволяя кликнуть по элементу на экране и увидеть, из каких обёрток он состоит. Когда вёрстка ведёт себя неожиданно, инспектор почти всегда быстрее объясняет причину, чем чтение кода. Привыкайте сверяться с деревом — оно и есть истинная структура вашего интерфейса.

Итог: Flutter — это композиция виджетов в дерево. Усвойте «всё есть виджет» и привычку оборачивать, а не настраивать — на этом стоит весь фреймворк. Дальше разберём, какими бывают виджеты.

Проверьте себя
1. Что значит принцип «всё есть виджет» во Flutter?
AТолько кнопки являются виджетами
BЛюбая часть интерфейса — текст, отступ, выравнивание — это виджет
CВиджет — это файл проекта
DВиджеты рисуются на HTML
2. Как во Flutter добавить отступ вокруг текста?
AНастроить свойство margin у Text
BОбернуть Text в виджет Padding
CДобавить пробелы в строку
DИспользовать CSS