Какие подходы к управлению состоянием (State Management) во Flutter ты знаешь?

«Какие подходы к управлению состоянием (State Management) во Flutter ты знаешь?» — вопрос из категории State Management, который задают на 29% собеседований Flutter Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

  1. setState (встроенный):

    • Для чего: Идеален для простого, локального состояния внутри одного StatefulWidget.
    • Ограничения: Не масштабируется для сложных приложений, так как приводит к перерисовке всего виджета и неудобен для передачи состояния вглубь дерева.
      class _MyCounterState extends State<MyCounter> {
      int _count = 0;
      void _increment() {
      setState(() { _count++; }); // Перестраивает только этот виджет
      }
      }
  2. Provider + ChangeNotifier (рекомендованный Google):

    • Для чего: Отлично подходит для разделения бизнес-логики и UI, предоставляет состояние любым потомкам в дереве.
    • Суть: ChangeNotifier хранит состояние и уведомляет слушателей об изменениях. Provider делает этот ChangeNotifier доступным в дереве виджетов.
      
      // Модель (логика)
      class CounterModel with ChangeNotifier {
      int _count = 0;
      int get count => _count;
      void increment() {
      _count++;
      notifyListeners(); // Сообщает Provider об изменении
      }
      }

    // Потребление в UI Consumer( builder: (context, model, child) => Text('${model.count}'), )

  3. Bloc / Cubit (Business Logic Component):

    • Для чего: Для сложных приложений, где важно четкое разделение слоев, предсказуемость состояния и тестируемость. Использует потоки (Stream).
    • Суть: События (Events) на входе -> Bloc обрабатывает логику -> новые состояния (States) на выходе. Cubit — упрощенная версия.
      
      // Cubit
      class CounterCubit extends Cubit<int> {
      CounterCubit() : super(0); // Начальное состояние
      void increment() => emit(state + 1); // Новое состояние
      }

    // Использование в UI BlocBuilder<CounterCubit, int>( builder: (context, state) => Text('$state'), )

  4. Riverpod: Рассматриваю как эволюцию Provider с улучшенной безопасностью типов (null-safety из коробки), тестируемостью и независимостью от контекста виджетов.

Мой выбор: Для новых проектов средней и высокой сложности я предпочитаю Bloc/Cubit из-за отличной документации, предсказуемости и инструментов (bloc_concurrency, bloc_test). Для более простых задач или быстрых прототипов — Provider или Riverpod.