Ответ
Я работал с несколькими архитектурами управления состоянием во Flutter, которые можно условно разделить по уровню сложности и охвату:
-
setState(встроенный):- Для чего: Идеален для простого, локального состояния внутри одного
StatefulWidget. - Ограничения: Не масштабируется для сложных приложений, так как приводит к перерисовке всего виджета и неудобен для передачи состояния вглубь дерева.
class _MyCounterState extends State<MyCounter> { int _count = 0; void _increment() { setState(() { _count++; }); // Перестраивает только этот виджет } }
- Для чего: Идеален для простого, локального состояния внутри одного
-
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}'), ) -
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'), )
- Для чего: Для сложных приложений, где важно четкое разделение слоев, предсказуемость состояния и тестируемость. Использует потоки (
-
Riverpod: Рассматриваю как эволюциюProviderс улучшенной безопасностью типов (null-safety из коробки), тестируемостью и независимостью от контекста виджетов.
Мой выбор: Для новых проектов средней и высокой сложности я предпочитаю Bloc/Cubit из-за отличной документации, предсказуемости и инструментов (bloc_concurrency, bloc_test). Для более простых задач или быстрых прототипов — Provider или Riverpod.
Ответ 18+ 🔞
Блин, слушай, я тут с этими архитектурами во Flutter как обезьяна с гранатой намучился, пока не разобрался, что к чему. Сейчас тебе по полочкам разложу, чтобы ты не наступал на те же грабли, на которые я в своё время наступил со всего размаху.
1. setState — классика жанра, но для детского сада.
Ну, ёпта, все с него начинают. Захотел кнопочку, которая цифру меняет — вперёд. Но это как велосипед с квадратными колёсами: пока приложение — одна кнопка, всё ок. Как только логики становится больше, чем у мартышлюшки, начинается пиздец. Он перерисовывает весь виджет целиком, даже если тебе надо обновить одну надпись. Представь, ты меняешь лампочку, а у тебя весь дом заново строят. Овердохуища лишней работы.
class _MyCounterState extends State<MyCounter> {
int _count = 0;
void _increment() {
setState(() { _count++; }); // Бля, перерисует весь виджет, будто он заново родился.
}
}
2. Provider + ChangeNotifier — рабочий инструмент, который Google в своё время пиарил.
Тут уже начинается нормальное разделение: логика в одном месте, а интерфейс в другом. ChangeNotifier — это такая модель, которая кричит всем: «Эй, я изменился!». А Provider — это почтальон, который разносит эту новость по всему дереву виджетов. Удобно, но иногда чувствуешь, что пишешь дохуя boilerplate-кода. И если неаккуратно использовать, можно такую кашу с notifyListeners() замутить, что сам потом будешь охуевать.
// Модель (тут живёт логика)
class CounterModel with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // Кричит всем: «Бля, я обновился!»
}
}
// А в UI просто слушаем
Consumer<CounterModel>(
builder: (context, model, child) => Text('${model.count}'), // Всё прозрачно.
)
3. Bloc / Cubit — это уже для серьёзных пацанов.
Если твоё приложение сложнее, чем список дел, и ты хочешь, чтобы всё было предсказуемо и тестируемо — это твой выбор. Тут чёткая схема: событие зашло, логика отработала, состояние вышло. Cubit — это как облегчённый Bloc, без лишних телодвижений. Да, поначалу кажется, что это ёперный театр с этими событиями и состояниями, но когда привыкнешь — обратной дороги нет. Плюс там отличные инструменты для обработки асинхронности и тестов.
// 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 из коробки и без привязки к контексту виджетов. Многие его хвалят, но я, честно говоря, пока не вник настолько, чтобы с пеной у рта его рекомендовать. Выглядит солидно, но доверия ебать ноль, пока сам не попробуешь в бою.
Итог мой личный, на основе шишек и синяков:
Если делаешь прототип или маленькое приложение — Provider тебе в помощь, не заморачивайся.
Если проект растёт, как на дрожжах, и ты не хочешь через месяц орать «какого хуя ничего не работает» — бери Bloc/Cubit. Документация у них — огонь, сообщество большое, и когда разберёшься, чувствуешь себя повелителем состояний. А setState оставь для учебных примеров, иначе потом будешь как тот самый распиздяй, который пытается построить небоскрёб из кубиков.