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

Как минимизировать ненужные перерисовки виджетов?

2.3 Middle🔥 181 комментариев
#Архитектура Flutter#Тестирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Минимизация ненужных перерисовок во Flutter

Перерисовка — одна из основных причин падения производительности. В Flutter есть несколько проверенных подходов для её минимизации.

1. Const конструкторы — первая линия защиты

// Плохо: создаёт новый объект каждый раз
Widget build(BuildContext context) {
  return Container(
    child: Text("Hello"),
  );
}

// Хорошо: const гарантирует повторное использование
const helloWidget = Text("Hello");

Widget build(BuildContext context) {
  return const SizedBox(
    child: helloWidget,
  );
}

Когда виджет в конструкторе marked as const, Flutter видит, что данные не изменились, и пропускает rebuild.

2. Правильное использование State

Проблема: setState() перестраивает всё дерево виджетов ниже:

class MyPage extends StatefulWidget {
  @override
  State<MyPage> createState() => _MyPageState();
}

class _MyPageState extends State<MyPage> {
  int counter = 0;
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Counter")), // ПЕРЕРИСУЕТСЯ!
      body: Center(
        child: Text(counter.toString()), // нужна только это
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => setState(() => counter++),
      ),
    );
  }
}

Решение: выноси State в отдельные виджеты (по сути, composition pattern):

class CounterDisplay extends StatefulWidget {
  @override
  State<CounterDisplay> createState() => _CounterDisplayState();
}

class _CounterDisplayState extends State<CounterDisplay> {
  int counter = 0;
  
  @override
  Widget build(BuildContext context) {
    return Text(counter.toString());
  }
}

class MyPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: const AppBar(title: Text("Counter")), // const, не перерисуется
      body: const Center(child: CounterDisplay()),
      floatingActionButton: FloatingActionButton(
        onPressed: () {}, // обновляем только CounterDisplay
      ),
    );
  }
}

3. InheritedWidget и Provider для управления состоянием

// Плохо: старый способ (PropDrilling)
MyWidget(value: value, onChanged: onChanged);

// Хорошо: используй InheritedWidget
class CounterProvider extends InheritedWidget {
  final int counter;
  final Function(int) onCounterChanged;
  
  const CounterProvider({
    required this.counter,
    required this.onCounterChanged,
    required super.child,
  });
  
  static CounterProvider of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<CounterProvider>()!;
  }
  
  @override
  bool updateShouldNotify(CounterProvider oldWidget) {
    return counter != oldWidget.counter; // только если counter изменился
  }
}

Provider пакет — обёртка над InheritedWidget с удобным API.

4. RepaintBoundary для изоляции перерисовок

// Дорогой виджет, который часто обновляется
RepaintBoundary(
  child: ExpensiveAnimationWidget(),
)

RepaintBoundary создаёт отдельный слой (layer), который Flutter может перерисовывать независимо.

5. shouldRebuild в InheritedWidget

@override
bool updateShouldNotify(OldProvider oldWidget) {
  // Вернёт false — зависимые виджеты НЕ пересоберутся
  return oldWidget.userId != userId;
}

Это критично для сложных состояний, где часть данных меняется, а часть нет.

6. Профилирование через DevTools

flutter run
# Открыть DevTools > Performance > Rasterizer
# Посмотреть, какие фреймы > 16ms (60fps) или > 8ms (120fps)

Чек-лист оптимизации

  • ✅ Используй const конструкторы везде, где возможно
  • ✅ Выноси изменяемое состояние в отдельные виджеты
  • ✅ Используй Provider или GetX для масштабируемого состояния
  • ✅ Оборачивай дорогие виджеты в RepaintBoundary
  • ✅ Правильно реализуй updateShouldNotify()
  • ✅ Избегай создания новых объектов в build()
  • ✅ Профилируй регулярно

Главный принцип: чем ближе к листьям дерева виджетов находится изменяемое состояние, тем меньше виджетов пересоберётся.