Как оптимизировать использование setState в Flutter чтобы уменьшить количество перерисовок
Что проверяют
Понимаете ли вы, что именно перестраивает setState, и отличаете ли rebuild от перерисовки. Следом обычно просят показать, как найти лишние перестроения, и спрашивают, когда пора переходить на Provider, Riverpod или BLoC.
Как отвечать
setState помечает элемент этого State как грязный, и на следующем кадре заново вызывается его build() со всем поддеревом. Весь экран он не трогает, но всё ниже строится заново, если Flutter не может это пропустить. Сам rebuild недорог: layout и paint у RenderObject пересчитываются, только если свойства реально изменились. Дорого становится, когда в build() тяжёлая работа или огромное поддерево.
Приёмы:
- опустить состояние вниз: вынести меняющийся кусок в отдельный маленький
StatefulWidget, тогдаsetStateперестраивает только его; const-конструкторы: для того жеconst-экземпляра Flutter пропускает перестроение поддерева;- параметр
child: уAnimatedBuilder,ValueListenableBuilder,ListenableBuilderстатичную часть передают черезchild, и она не строится на каждом тике; - точечная подписка:
ValueNotifierсValueListenableBuilder,context.selectв Provider,ref.watch(provider.select(...))в Riverpod,buildWhenилиBlocSelectorв BLoC; - чистый
build(): сортировку, парсинг и форматирование делать при изменении данных, а не в каждом кадре; - без пустых вызовов: не звать
setState, если значение не изменилось, и проверятьmountedпослеawait.
Измерять, а не гадать: Flutter DevTools, вкладка Performance с отслеживанием перестроений виджетов, и профильная сборка вместо debug.
Где спотыкаются
Создают ValueNotifier полем в StatelessWidget. При каждом перестроении родителя он создаётся заново, значение теряется, а dispose() никто не вызывает. Место ему в State с освобождением в dispose().
Думают, что const внутри build() дочернего виджета спасает сам этот виджет. Работает const там, где виджет создаётся, то есть у родителя.
Путают перестроение с перерисовкой и расставляют RepaintBoundary везде. Он изолирует перерисовку, а не rebuild, и каждый лишний слой стоит памяти.