Как минимизировать ненужные перерисовки виджетов?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Минимизация ненужных перерисовок во 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()
- ✅ Профилируй регулярно
Главный принцип: чем ближе к листьям дерева виджетов находится изменяемое состояние, тем меньше виджетов пересоберётся.