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

Ответ 18+ 🔞

Ну вот, сидишь такой, выбираешь себе паттерн для Flutter-проекта, а там их, бля, овердохуища разных. Голова кругом идёт. Но если по чесноку, то всё зависит от того, насколько ты хочешь себе жизнь усложнить.

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

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

    // БЛоК для счётчика, чтоб ты понимал масштаб
    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): Это когда ты уже совсем ебанулся и решил построить космический корабль. Мы так для одного монстра делали, чтобы бизнес-логика жила отдельно ото всего, как монах в пещере. В качестве главного по таблеткам взяли Riverpod — штука мощная, от контекста не зависит, и тестировать её — одно удовольствие.

    // Провайдер в Riverpod — выглядит страшно, но работает как швейцарские часы
    final userRepositoryProvider = Provider<UserRepository>((ref) {
      return UserRepositoryImpl(ref.watch(apiClientProvider));
    });
    
    final currentUserProvider = StateNotifierProvider<UserNotifier, User?>((ref) {
      return UserNotifier(ref.watch(userRepositoryProvider));
    });
  • MVVM: Ну а это классика, которую можно на коленке собрать из того же ChangeNotifier. ViewModel крутит логикой, View рисует картинки. Всё интуитивно, никакой ебалы с потоками, если не хочешь.

Так что мой совет, чувак: не гонись сразу за сложным. Начни с Provider'а, чтобы быстро вьехать и что-то сделать. А если почувствуешь, что проект разбухает и ты уже сам от себя охуеваешь от количества кода — вот тогда уже смотри в сторону BLoC или той самой чистой архитектуры с Riverpod. Чтобы потом не пришлось всё переписывать с криками «ёб твою мать!».