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

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

Ответ

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