← Назад к вопросам
Как работает build() метод во Flutter?
1.0 Junior🔥 272 комментариев
#Flutter виджеты
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает build() метод во Flutter
Что такое build()?
build() — это метод, который описывает как выглядит виджет в данный момент. Это функция, которая возвращает дерево виджетов.
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
child: Text("Hello"),
);
}
}
build() вызывается в следующих ситуациях:
- Первый раз — при создании виджета
- Когда родитель пересчитывается — если родитель вызвал setState()
- Когда InheritedWidget изменился — если виджет использует его
- Когда зависимость изменилась — в Riverpod/Provider
Жизненный цикл StatelessWidget
class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget();
@override
Widget build(BuildContext context) {
print("Building StatelessWidget");
return Scaffold(
appBar: AppBar(title: Text("Title")),
body: Center(child: Text("Content")),
);
}
}
// StatelessWidget:
// 1. Конструктор вызывается
// 2. build() вызывается один раз
// 3. Виджет отображается
// 4. Больше build() не вызывается (пока родитель не пересчитается)
Жизненный цикл StatefulWidget
class MyStatefulWidget extends StatefulWidget {
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int counter = 0;
@override
void initState() {
super.initState();
print("initState called"); // Один раз при создании
}
@override
void didUpdateWidget(MyStatefulWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print("didUpdateWidget called"); // Когда параметры изменились
}
@override
void deactivate() {
super.deactivate();
print("deactivate called"); // Перед удалением из дерева
}
@override
void dispose() {
super.dispose();
print("dispose called"); // Финальная очистка ресурсов
}
@override
Widget build(BuildContext context) {
print("build called");
return Column(
children: [
Text("Count: $counter"),
ElevatedButton(
onPressed: () {
setState(() {
counter++; // Это вызовет build() ещё раз
});
},
child: Text("Increment"),
),
],
);
}
}
// Порядок вызовов:
// 1. createState()
// 2. initState() — один раз
// 3. build() — при необходимости
// 4. setState() → build() — повторно
// 5. didUpdateWidget() — если параметры изменились
// 6. deactivate() — перед удалением
// 7. dispose() — финальная очистка
Как работает setState()
class Counter extends StatefulWidget {
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int count = 0;
@override
Widget build(BuildContext context) {
print("build() #${count + 1}");
return Column(
children: [
Text("Count: $count"),
ElevatedButton(
onPressed: () {
// setState() помечает State как "грязное" и планирует rebuild
setState(() {
count++; // Изменить данные
});
// После setState():
// 1. markNeedsBuild() помечает State как грязное
// 2. Фреймворк планирует rebuild
// 3. На следующем фрейме вызывается build()
},
child: Text("Increment"),
),
],
);
}
}
// Внутри setState():
// setState(() { count++; });
// ↓
// markNeedsBuild() вызывается
// ↓
// Фреймворк добавляет State в список для перестройки
// ↓
// На следующем фрейме:
// ↓
// build() вызывается с новыми данными
Как часто вызывается build()?
// ❌ ПЛОХО — build() пересчитывает весь список при каждом нажатии
class BadList extends StatefulWidget {
@override
State<BadList> createState() => _BadListState();
}
class _BadListState extends State<BadList> {
List<String> items = List.generate(100, (i) => "Item $i");
@override
Widget build(BuildContext context) {
print("Rebuilding entire list");
return ListView(
children: items.map((item) => ListTile(title: Text(item))).toList(),
);
}
}
// ✅ ХОРОШО — отдельный виджет для каждого элемента
class ListItem extends StatelessWidget {
final String title;
const ListItem(this.title);
@override
Widget build(BuildContext context) {
print("Building: $title");
return ListTile(title: Text(title));
}
}
class GoodList extends StatefulWidget {
@override
State<GoodList> createState() => _GoodListState();
}
class _GoodListState extends State<GoodList> {
List<String> items = List.generate(100, (i) => "Item $i");
@override
Widget build(BuildContext context) {
print("Building list container"); // Один раз
return ListView(
children: items.map((item) => ListItem(item)).toList(),
);
}
}
Оптимизация build() с const
// ❌ ПЛОХО — виджет пересчитывается каждый раз
class Bad extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(color: Colors.blue, height: 100),
Text("Static text"),
],
);
}
}
// ✅ ХОРОШО — используем const для неизменяемых виджетов
class Good extends StatelessWidget {
const Good();
@override
Widget build(BuildContext context) {
return const Column(
children: [
SizedBox(height: 100, child: ColoredBox(color: Colors.blue)),
Text("Static text"),
],
);
}
}
BuildContext
BuildContext — это объект, который содержит информацию о позиции виджета в дереве:
Widget build(BuildContext context) {
// context содержит:
// - Ссылку на виджет
// - Ссылку на State (если это StatefulWidget)
// - Информацию о родителях и соседях
// - Доступ к Theme, MediaQuery и т.д.
// Примеры использования:
final theme = Theme.of(context); // Получить тему
final mediaQuery = MediaQuery.of(context); // Размер экрана
final navigator = Navigator.of(context); // Навигация
final scaffold = Scaffold.of(context); // Доступ к Scaffold
return Container();
}
Когда НЕ вызывается build()?
// ❌ Это НЕ вызовет build()
int value = 0;
value++; // Просто изменили переменную
// ✅ Это ВЫЗОВЕТ build()
setState(() {
value++; // Указали, что нужно пересчитать
});
// ❌ В StatelessWidget НЕ будет пересчёта
class Static extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(DateTime.now().toString()); // Статичное значение
}
}
// ✅ В StatefulWidget БУДЕТ пересчёт
class Dynamic extends StatefulWidget {
@override
State<Dynamic> createState() => _DynamicState();
}
class _DynamicState extends State<Dynamic> {
late Timer timer;
@override
void initState() {
super.initState();
timer = Timer.periodic(Duration(seconds: 1), (_) {
setState(() {}); // Это вызовет build() каждую секунду
});
}
@override
void dispose() {
timer.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Text(DateTime.now().toString());
}
}
Performance: виджеты должны быть быстрыми
// ❌ МЕДЛЕННО — сложные вычисления в build()
Widget build(BuildContext context) {
final result = expensiveCalculation(); // Каждый раз!
return Text("$result");
}
// ✅ БЫСТРО — используйте useMemo эквивалент
Widget build(BuildContext context) {
return Text(cachedResult); // Один раз вычислено
}
// Или с useMemo (в Riverpod)
final resultProvider = Provider<int>((ref) {
return expensiveCalculation(); // Кэшируется
});
Best Practices
- build() должен быть чистой функцией — без побочных эффектов
- Не вызывайте Future/Stream в build() — используйте initState или Riverpod
- Используйте const для неизменяемых виджетов
- Разделяйте большой build() на маленькие виджеты
- Используйте setState() для локального состояния
- Используйте Provider/Riverpod для глобального состояния
- Помните про dispose() — для очистки ресурсов
Итог
build() — это основной метод во Flutter, который:
- Описывает внешний вид виджета
- Вызывается при создании и при изменении состояния
- Должен быть быстрым и чистым
- Является чистой функцией (одинаковый input → одинаковый output)