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

В Flutter ненужные перестроения виджетов могут привести к проблемам с производительностью или непредвиденному поведению. Вот как можно избежать или минимизировать ненужные перестроения виджетов:

1. Используйте const Виджеты

  • Объявляйте виджеты как const, если их свойства не изменяются. Это говорит Flutter, что виджет неизменяемый, и его не нужно перестраивать при изменении состояния в других местах.
    const Text('Hello World');
    

2. Мемоизация с использованием StatefulWidget

  • Используйте StatefulWidget и кэшируйте результаты сложных вычислений в состоянии, чтобы предотвратить их повторный расчет при каждом перестроении виджета.
    class MyWidget extends StatefulWidget {
      @override
      _MyWidgetState createState() => _MyWidgetState();
    }
    
    class _MyWidgetState extends State<MyWidget> {
      String expensiveResult;
    
      @override
      void initState() {
        super.initState();
        expensiveResult = expensiveComputation();
      }
    
      @override
      Widget build(BuildContext context) {
        return Text(expensiveResult);
      }
    }
    

 

3. Используйте Selector в Provider или Consumer с ChangeNotifier

  • Если вы используете Provider для управления состоянием, оборачивайте виджеты в Selector или Consumer, чтобы перестраивать виджеты только при изменении определенной части состояния.
    Selector<MyModel, String>(
      selector: (_, model) => model.someProperty,
      builder: (_, value, __) {
        return Text(value);
      },
    );
    

4. Используйте ключи (Keys) для предотвращения ненужных перестроений

  • Если вы работаете с динамическими виджетами в списках или других структурах, убедитесь, что каждый виджет имеет правильный ключ для их различения.
    ListView.builder(
      itemBuilder: (context, index) {
        return ListTile(key: ValueKey(index), title: Text('Item $index'));
      },
    );
    

5. Избегайте тяжелой работы в build()

  • Метод build() должен быть быстрым и идемпотентным. Избегайте выполнения сложных вычислений или сетевых вызовов в методе build().
  • Вместо этого выполняйте эту работу в initState(), didChangeDependencies() или другим событийным образом (например, через FutureBuilder, StreamBuilder).

6. Используйте RepaintBoundary

  • RepaintBoundary можно использовать для оборачивания виджетов, у которых дорогостоящий рендеринг. Это предотвращает ненужную перерисовку дочерних виджетов.
    RepaintBoundary(
      child: SomeExpensiveWidget(),
    );
    

7. Используйте shouldRebuild в пользовательских виджетах

  • При использовании пользовательских объектов рендеринга или CustomMultiChildLayout переопределите метод shouldRebuild, чтобы вернуть false, если виджету не нужно перестраиваться.
    class MyCustomWidget extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return CustomMultiChildLayout(
          delegate: MyLayoutDelegate(),
          children: <Widget>[ /* ваши дети */ ],
        );
      }
    }
    
    class MyLayoutDelegate extends MultiChildLayoutDelegate {
      @override
      bool shouldRelayout(covariant MultiChildLayoutDelegate oldDelegate) {
        return false;  // Предотвращение ненужных перестроений
      }
    }
    

8. Используйте AutomaticKeepAlive в списках

  • При использовании виджетов в прокручиваемых контейнерах (например, ListView или PageView), используйте AutomaticKeepAlive, чтобы предотвратить перестроение виджетов, которые выходят за пределы экрана.
    class MyListItem extends StatefulWidget {
      @override
      _MyListItemState createState() => _MyListItemState();
    }
    
    class _MyListItemState extends State<MyListItem> with AutomaticKeepAliveClientMixin {
      @override
      bool get wantKeepAlive => true;
    
      @override
      Widget build(BuildContext context) {
        super.build(context);  // Важно: вызовите super.build при использовании AutomaticKeepAlive
        return Text('Item');
      }
    }
    

Применяя эти техники, вы можете минимизировать ненужные перестроения виджетов в вашем приложении на Flutter, что приведет к лучшей производительности и более плавному пользовательскому опыту.