Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Дерево виджетов в Flutter
Widget Tree (дерево виджетов) — это иерархическая структура, в которой все UI элементы приложения организованы в виде дерева. Каждый виджет может иметь дочерние виджеты, создавая древовидную структуру от корня (MyApp) к листьям (Text, Image).
Структура дерева
MyApp
├── MaterialApp
│ ├── home: HomePage
│ │ ├── Scaffold
│ │ │ ├── appBar: AppBar
│ │ │ │ └── title: Text
│ │ │ ├── body: Column
│ │ │ │ ├── Row
│ │ │ │ │ ├── Icon
│ │ │ │ │ └── Text
│ │ │ │ └── ListView
│ │ │ │ └── ListTile (repeated)
│ │ │ └── floatingActionButton: FloatingActionButton
│ │ │ └── Icon
Элементы дерева
1. Widget — объявление UI (неизменяемо):
// Widget описывает КАК должен выглядеть UI
text = Text('Hello', style: TextStyle(fontSize: 18));
2. Element — экземпляр Widget в дереве:
// Element — это живой объект в дереве,
// которому живёт в памяти во время жизни приложения
3. RenderObject — отвечает за рисование:
// RenderObject содержит логику layout, paint и events
Трёхслойная архитектура
Widget Tree (неизменяемо)
↓
Element Tree (управляет жизненным циклом)
↓
Render Tree (отрисовка и layout)
// Пример преобразования
Widget: Text('Hello') ← Это просто описание
↓
Element: TextElement ← Это экземпляр в дереве
↓
Render: RenderParagraph ← Это что отрисовывается
Жизненный цикл элемента в дереве
1. mount() ← Element добавлен в дерево
2. build() ← Widget создаёт UI
3. update() ← Widget обновился
4. reassemble() ← Hot reload
5. deactivate() ← Удаление из дерева
6. unmount() ← Полное удаление из памяти
Как происходит построение дерева
Начальное построение:
void main() {
runApp(MyApp()); // Добавляет MyApp в дерево
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// build() вызывается для создания дочерних виджетов
return MaterialApp(
home: HomePage(),
);
}
}
Обновление дерева с setState:
class _CounterState extends State<Counter> {
int count = 0;
@override
Widget build(BuildContext context) {
return Column(children: [
Text('Count: $count'),
ElevatedButton(
onPressed: () {
setState(() => count++); // Триггер перестройки
},
child: Text('Increment'),
),
]);
}
}
// Что происходит при setState():
// 1. State отмечается как "грязное"
// 2. build() вызывается снова
// 3. Создаётся новый Widget Tree
// 4. Flutter сравнивает старое и новое дерево (diffing)
// 5. Обновляет только изменённые элементы
Reconciliation (сравнение деревьев)
Flutter использует эффективный алгоритм для обновления:
// Старое дерево
Column(
children: [Text('Count: 0')],
)
// Новое дерево
Column(
children: [Text('Count: 1')],
)
// Flutter видит:
// 1. Column остался → переиспользовать Element
// 2. Text изменился → обновить текст
// 3. Остальное не трогать
// Это намного эффективнее, чем пересоздавать всё
Ключи (Keys) для контроля дерева
Для сложных списков используются ключи:
// Без ключей: если переставить элементы, state может
// прилипнуть к неправильному элементу
Column(children: [
StatefulWidget1(),
StatefulWidget2(),
]);
// С ключами: Flutter знает какой Element куда идёт
Column(children: [
KeyedWidget(key: ValueKey(1), child: StatefulWidget1()),
KeyedWidget(key: ValueKey(2), child: StatefulWidget2()),
]);
// Для списков используйте:
ListView.builder(
itemBuilder: (context, index) => ListItem(
key: ValueKey(items[index].id), // ← Обязательно!
item: items[index],
),
)
InheritedWidget: передача данных по дереву
class MyTheme extends InheritedWidget {
final Color primaryColor;
const MyTheme({
required this.primaryColor,
required super.child,
});
static MyTheme of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyTheme>()!;
}
@override
bool updateShouldNotify(MyTheme oldWidget) {
return primaryColor != oldWidget.primaryColor;
}
}
// Использование:
final theme = MyTheme.of(context);
print(theme.primaryColor);
BuildContext: навигация по дереву
// BuildContext содержит информацию о позиции элемента в дереве
var parentScaffold = Scaffold.of(context); // Родитель Scaffold
var theme = Theme.of(context); // Ближайшая тема
var navigator = Navigator.of(context); // Navigator
// Используется для поиска элементов вверх по дереву
DevTools: инспекция дерева
flutter pub global run devtools
# Открывает DevTools со вкладкой Widget Inspector
Там можно:
- Видеть всё дерево
- Инспектировать свойства виджетов
- Поиск виджетов
- Анализ производительности
Производительность дерева
// Плохо: весь Column перестраивается
StatefulWidget buildCounter() {
return Column(
children: [
Counter(), // Перестраивается при setState
ExpensiveWidget(), // Перестраивается даже если не меняется!
AnotherExpensive(),
],
);
}
// Хорошо: изолировать setState в отдельный виджет
class Counter extends StatefulWidget { ... }
class MyPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
Counter(), // Только Counter перестраивается
ExpensiveWidget(), // Не перестраивается
AnotherExpensive(),
],
);
}
}
Практические советы
- Разделяйте виджеты — чем меньше части, тем эффективнее обновления
- Используйте const — const виджеты не перестраиваются
- Используйте ключи — для списков с динамическим контентом
- Профилируйте — DevTools покажет какие виджеты часто перестраиваются
- Знайте BuildContext — он привязан к позиции в дереве
Понимание дерева виджетов — это ключ к написанию эффективного и масштабируемого Flutter кода.