Ответ
На уровне 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. Однако для всего, что сложнее, я сразу применяю один из вышеперечисленных паттернов, чтобы обеспечить тестируемость и разделение ответственности.