← Назад к вопросам

Как работает 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() вызывается в следующих ситуациях:

  1. Первый раз — при создании виджета
  2. Когда родитель пересчитывается — если родитель вызвал setState()
  3. Когда InheritedWidget изменился — если виджет использует его
  4. Когда зависимость изменилась — в 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)
Как работает build() метод во Flutter? | PrepBro