Ответ
На уровне UI (Presentation Layer) я фокусируюсь на паттернах, которые управляют состоянием экрана и логикой отображения. Вот основные из них в моей практике:
-
MVVM (Model-View-ViewModel): Чаще всего я использую именно этот подход.
ViewModel(часто реализованный какChangeNotifierили с помощьюStateNotifierиз Riverpod) содержит состояние и логику дляView(виджета Flutter). Связь между ними осуществляется через слушатели или провайдеры.// ViewModel для экрана входа class LoginViewModel extends ChangeNotifier { String _email = ''; String _password = ''; bool _isLoading = false; // Геттеры для View bool get isValid => _email.isNotEmpty && _password.isNotEmpty; bool get isLoading => _isLoading; void updateEmail(String value) { _email = value; notifyListeners(); } Future<void> submit() async { _isLoading = true; notifyListeners(); // ... вызов use case или репозитория _isLoading = false; notifyListeners(); } } -
BLoC (Business Logic Component): Когда логика экрана становится сложной и включает множество событий, я выбираю BLoC. Он идеально подходит для экранов с формами, фильтрами, реальным временем. Я использую пакет
flutter_bloc, который предоставляет четкую структуруBloc,Event,Stateи виджетыBlocBuilder/BlocListenerдля реакции на изменения. -
Presenter/Controller (в рамках Clean Architecture): В проектах с Clean Architecture роль ViewModel часто играет
PresenterилиController. Его задача — принимать данные от Use Cases (из Domain слоя), преобразовывать их в модель, удобную для отображения (UiModel), и управлять состоянием загрузки/ошибок. View в этом случае становится максимально "тупым" и просто отрисовывает переданнуюUiModel. -
StatefulWidget как простейший паттерн: Для очень простых экранов без сложного состояния (например, статический экран "О приложении") можно обойтись встроенным
StatefulWidget. Однако для всего, что сложнее, я сразу применяю один из вышеперечисленных паттернов, чтобы обеспечить тестируемость и разделение ответственности.
Ответ 18+ 🔞
Слушай, а вот про UI-слой, это же вообще отдельная песня, ёпта. Ты представляешь, сколько там этих паттернов развелось, как будто на выставке собак — каждый кобель норовит свою родословную показать. Я тебе сейчас по полочкам разложу, на чём обычно строю, чтобы не было потом пиздопроебибны в коде.
MVVM (Model-View-ViewModel) — это мой, можно сказать, рабочий конёк. Ну то есть, не конёк, а вообще основа основ. Смысл в чём: ViewModel — это такая хитрая жопа, которая всю логику экрана в себе держит, а View (то есть виджет) просто тупо рисует то, что ему сказали. Связываю я их обычно через ChangeNotifier или, если проект посерьёзнее, через StateNotifier из Riverpod. Получается чисто, понятно, и тестировать можно.
// ViewModel для экрана входа
class LoginViewModel extends ChangeNotifier {
String _email = '';
String _password = '';
bool _isLoading = false;
// Геттеры для View
bool get isValid => _email.isNotEmpty && _password.isNotEmpty;
bool get isLoading => _isLoading;
void updateEmail(String value) {
_email = value;
notifyListeners(); // Крикнул виджету: "Э, бошка думай, данные обновились!"
}
Future<void> submit() async {
_isLoading = true;
notifyListeners();
// ... тут вызов use case или репозитория, где всё может накрыться медным тазом
_isLoading = false;
notifyListeners();
}
}
BLoC (Business Logic Component) — это уже когда MVVM начинает потеть и задыхаться. Беру его на вооружение, если на экране начинается настоящий ёперный театр: куча событий, сложные переходы состояний, фильтры в реальном времени. Пакет flutter_bloc — просто песня, там всё чётко: Bloc, Event, State. Сидишь и как дирижёр оркестром управляешь этим цирком через BlocBuilder и BlocListener. Удивление пиздец, насколько удобно.
Presenter/Controller (в рамках Clean Architecture) — а это уже для проектов, где все такие серьёзные, в очках и с архитектурными диаграммами. Тут Presenter — это такой переговорщик между бизнес-логикой (Use Cases) и тупым виджетом. Его задача — взять сырые данные, отформатировать их в красивую UiModel (чтобы виджет не ломал голову) и ещё за всеми этими состояниями загрузки-ошибок следить. Виджет в этой схеме становится настолько тупым, что ему хоть вилкой в глаз, а он только картинки менять будет.
StatefulWidget как простейший паттерн — ну это, бля, как взять молоток и гвозди, чтобы собрать табуретку. Для экрана «О приложении», где одна картинка и два текста — ещё куда ни шло. Но чуть что посложнее — и ты уже сидишь с этим setState, который раскидан по всему виджету, и чувствуешь себя полным распиздяем. Поэтому я, как только вижу, что состояние хоть чуть-чуть выходит за рамки «включено/выключено», сразу хватаю MVVM или BLoC. И тестировать потом можно, и голова не болит, и доверия к коду — не ноль ебать, а вполне себе есть.