Всё есть виджет: дерево виджетов
В 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, а Center — Text. Это и есть дерево виджетов в коде.
Как дерево работает под капотом
Каждый виджет — это всего лишь лёгкое описание того, как должен выглядеть кусок интерфейса. Сам по себе он ничего не рисует. 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 — это композиция виджетов в дерево. Усвойте «всё есть виджет» и привычку оборачивать, а не настраивать — на этом стоит весь фреймворк. Дальше разберём, какими бывают виджеты.