Наверняка, самым частоиспользуемым Flutter-разработчиками методом можно назвать метод
build() в Stateful- и Stateless-виджетах. Сегодня поделюсь с вами своим топ-5 лучших практик для его оптимизации.1. Использовать константные конструкторы везде, где можно.
При перестроении родительского виджета все дочерние виджеты, созданные как константные, перестраиваться не будут. С помощью const вы даете программе явно понять, что именно этот объект изменяться не должен.
class Example extends StatelessWidget {
const Example({super.key});
@override
Widget build(BuildContext context) {
return Column(
children: [
const Text('Example text'), // оптимально
Text('Example text not const'), // можно оптимизировать, добавив const
],
);
}
}
2. Кэшировать обращения к MediaQuery.of(context) и Theme.of(context)
Каждое обращение у этих классов к методам
of(context) под капотом подразумевает вызов dependOnInheritedWidgetOfExactType(). Этот метод производит поиск необходимых данных по всему дереву виджетов. Такая операция занимает некоторое время. Если таких операций было запущено несколько, то время увеличивается.Кроме этого, если от MediaQuery вам необходим, например, только размер экрана, лучше вместо стандартного
of(context) использовать sizeOf(context).
/// Пример неоптимизированного использования
class NotOptimizedExample extends StatelessWidget {
const NotOptimizedExample({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.height * 0.3,
margin: EdgeInsets.all(MediaQuery.of(context).size.width * 0.05),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
border: Border.all(color: Theme.of(context).dividerColor),
),
child: const Text('Hello'),
);
}
}
/// Пример оптимизированного использования
class OptimizedExample extends StatelessWidget {
const OptimizedExample({super.key});
@override
Widget build(BuildContext context) {
final sizeData = MediaQuery.sizeOf(context);
final themeData = Theme.of(context);
return Container(
width: sizeData.width * 0.8,
height: sizeData.height * 0.3,
margin: EdgeInsets.all(sizeData.width * 0.05),
decoration: BoxDecoration(
color: themeData.primaryColor,
border: Border.all(color: themeData.dividerColor),
),
child: const Text('Hello'),
);
}
}
3. Вызывать перестроение только тех участков, которые действительно должны измениться.
Здесь для примера рассмотрим Stateful-виджет NotOptimizedExample, State которого выглядит таким образом:
class _NotOptimizedExampleState extends State<NotOptimizedExample> {
int counter = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
PageHeader(), // Пересоздается
CounterView(),
CounterButton(
onTap: () => setState(() {
counter++;
}),
),
PageFooter(), // Пересоздается
],
);
}
}
В этом случае при тапе на кнопку нам нужно обновить только CounterView. Но подобной реализацией мы заставляем перерисовать все содержимое экрана.
Оптимальное решение здесь — ребилдить только CounterView.
4. Оптимизировать скроллящиеся объекты на экране.
Основная идея — осознанно подбирать виджеты прокручиваемых списков, опираясь на размеры списка, сложность вложенных объектов и потребности в механике прокрутки. Больше деталей вы можете узнать из поста Розы об оптимизации списков и моего поста о сравнении трех подходов создания прокручиваемых списков.
Пятый способ — в комментариях👇