Какие архитектурные паттерны ты применял в проектах на Flutter?

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

Ответ

В своих Flutter-проектах я использовал несколько паттернов, выбор которых зависит от масштаба и сложности приложения:

  • Provider + ChangeNotifier: Это был мой основной выбор для средних по сложности коммерческих проектов. Он идеально сочетает простоту и мощность, будучи официально рекомендованным командой Flutter. Я создавал модели, расширяющие ChangeNotifier, и использовал Consumer или Provider.of для реактивного обновления UI.

    // Типичная модель в таком подходе
    class AuthService extends ChangeNotifier {
      User? _currentUser;
      User? get currentUser => _currentUser;
    
      Future<void> login(String email, String password) async {
        // ... логика запроса к API
        _currentUser = fetchedUser;
        notifyListeners(); // UI, слушающий эту модель, перестроится
      }
    }
  • BLoC (с библиотекой flutter_bloc): Применял для проектов со сложной бизнес-логикой, где важно чёткое разделение ответственности и тестируемость. Паттерн основан на потоках (Stream) и концепциях Event -> State.

    // Простой BLoC для счётчика
    class CounterBloc extends Bloc<CounterEvent, int> {
      CounterBloc() : super(0) {
        on<Increment>((event, emit) => emit(state + 1));
        on<Decrement>((event, emit) => emit(state - 1));
      }
    }
    // В UI: BlocBuilder<CounterBloc, int>(builder: (context, count) => Text('$count'))
  • Clean Architecture (с Riverpod): Для одного крупного долгосрочного проекта мы выбрали Clean Architecture, чтобы изолировать бизнес-правила (Domain) от деталей реализации (Data, Presentation). В качестве state-менеджера использовали Riverpod, так как он предоставляет отличную типобезопасность, тестируемость и не зависит от BuildContext.

    // Provider в Riverpod (уровень Data/Presentation)
    final userRepositoryProvider = Provider<UserRepository>((ref) {
      return UserRepositoryImpl(ref.watch(apiClientProvider));
    });
    
    final currentUserProvider = StateNotifierProvider<UserNotifier, User?>((ref) {
      return UserNotifier(ref.watch(userRepositoryProvider));
    });
  • MVVM: Часто реализуется "из коробки" с ChangeNotifier (как ViewModel) и ValueListenableBuilder. Это интуитивно понятный паттерн для разделения UI и логики представления.

Мой общий принцип: начинать с Provider для скорости разработки, а для более сложных сценариев переходить на BLoC или Clean Architecture с Riverpod, чтобы обеспечить долгосрочную поддерживаемость кода.