Как избежать лишних вызовов build во Flutter
Что проверяют
Понимаете ли вы, что сам вызов build дешёвый, а дорого обходится перестройка большого поддерева без причины. Интервьюеру важно, умеете ли вы сузить область перестройки, а не «запретить» build. Следом обычно просят показать, как найти лишние перестройки в DevTools.
Как отвечать
Начните с механики: build вызывается, когда виджет помечен грязным (setState, изменилась зависимость от InheritedWidget) или когда перестроился родитель и передал новый экземпляр. Отсюда приёмы:
constконструкторы: константный экземпляр канонизирован, Flutter видит при сравнении тот же объект и пропускает поддерево;- дробление на классы:
setStateдержите как можно ниже по дереву. Отдельный класс виджета перестраивается сам по себе, а метод-хелпер_buildHeader()всегда перестраивается вместе с родителем; - параметр
childу билдеров:AnimatedBuilder,ValueListenableBuilderиConsumerпринимают готовыйchild, который не пересоздаётся на каждом кадре; - точечная подписка:
context.selectв Provider,ref.watch(provider.select(...))в Riverpod,buildWhenиBlocSelectorво flutter_bloc. Виджет перестраивается только при изменении нужного поля; - узкие зависимости:
MediaQuery.sizeOf(context)вместоMediaQuery.of(context), чтобы не реагировать на клавиатуру и прочие изменения; - без побочных эффектов в build:
FutureдляFutureBuilderсоздают вinitState, иначе каждая перестройка заново шлёт запрос.
Результат проверяют в DevTools на вкладке Performance, там есть отслеживание сборок виджетов и статистика перестроек, а в debug-сборке помогает debugPrintRebuildDirtyWidgets. Замеры скорости делают в profile-режиме.
Где спотыкаются
Считают, что StatelessWidget не перестраивается. Перестраивается, когда перестроился родитель или изменилась его зависимость от контекста.
Советуют «переопределить shouldRebuild» у своего виджета. У StatelessWidget такого метода нет, похожие хуки есть у InheritedWidget (updateShouldNotify), CustomPainter (shouldRepaint) и делегатов слайверов.
Путают build и отрисовку. RepaintBoundary сокращает перерисовку, а не число вызовов build.