← Назад к вопросам

Что выберешь GetX или BLoC?

2.0 Middle🔥 301 комментариев
#State Management#Архитектура Flutter#Асинхронность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

GetX vs BLoC: Выбор State Management'а

Введение

Это классический вопрос в Flutter сообществе, и ответ не абсолютный — всё зависит от контекста. После 10+ лет я выбрал бы ни то, ни другое, а вместо этого — Provider или Riverpod. Но давайте разберёмся со обоими.

GetX: Обзор

Что это?

GetX — это инструмент all-in-one для state management, navigation, dependency injection и ещё кучи всего.

// GetX example
class CounterController extends GetxController {
  final count = 0.obs;
  
  void increment() => count++;
}

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Obx(() => Text('Count: ${Get.find<CounterController>().count}')),
      floatingActionButton: FloatingActionButton(
        onPressed: () => Get.find<CounterController>().increment(),
        child: Icon(Icons.add),
      ),
    );
  }
}

// main.dart
Get.put(CounterController()); // DI

Преимущества GetX ✅

  1. All-in-one решение

    • State management
    • Navigation
    • Dependency injection
    • Localization
    • Theme
    • Все в одном пакете
  2. Просто писать

    // Очень простой синтаксис
    final count = 0.obs;
    count++; // Автоматический update!
    
  3. Производительность

    • Реактивность встроена
    • Не требует setState()
  4. Navigation без context

    Get.to(NextPage());
    Get.back();
    
  5. Активное сообщество

    • Много плагинов
    • Много примеров

Недостатки GetX ❌

  1. Слишком магический (magic is the enemy of understanding)

    final count = 0.obs; // Как это работает под капотом?
    // Много неявных convention'ов
    
  2. Service Locator antipattern

    Get.find<CounterController>(); // Зависит от глобального состояния
    // Сложнее тестировать
    // Сложнее следить за зависимостями
    
  3. Жёсткая интеграция

    • Трудно использовать части отдельно
    • "All or nothing" подход
  4. Тестирование сложнее

    // Нужно правильно инициализировать GetX
    test('counter test', () async {
      Get.put(CounterController()); // Нужна setup
      final controller = Get.find<CounterController>();
      controller.increment();
      expect(controller.count.value, 1);
      Get.reset(); // Cleanup
    });
    
  5. Обновления могут сломать код

    • GetX разработчик довольно активный
    • API меняется иногда неожиданно
  6. Проблемы с деревом зависимостей

    • Трудно отследить что зависит от чего

BLoC: Обзор

Что это?

BLoC (Business Logic Component) — pattern для разделения бизнес-логики от UI.

// BLoC pattern
enum CounterEvent { increment }

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<CounterEvent>((event, emit) {
      switch (event) {
        case CounterEvent.increment:
          emit(state + 1);
      }
    });
  }
}

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<CounterBloc, int>(
      builder: (context, count) {
        return Scaffold(
          body: Text('Count: $count'),
          floatingActionButton: FloatingActionButton(
            onPressed: () => 
              context.read<CounterBloc>().add(CounterEvent.increment),
            child: Icon(Icons.add),
          ),
        );
      },
    );
  }
}

Преимущества BLoC ✅

  1. Чистая архитектура

    • Strict separation: Events → BLoC → States
    • Предсказуемый поток данных
  2. Легче тестировать

    test('counter bloc', () {
      final bloc = CounterBloc();
      expect(
        bloc.stream,
        emitsInOrder([0, 1, 2]),
      );
      bloc.add(CounterEvent.increment);
      bloc.add(CounterEvent.increment);
    });
    
  3. No magic

    • Явный поток: Event → Logic → State
    • Легко понять что происходит
  4. Инверсия зависимостей

    // BLoC не знает о UI
    // UI слушает BLoC
    // Легко тестировать отдельно
    
  5. Scalability

    • Большие приложения становятся управляемыми
    • Можно добавлять новые события без влияния на старые
  6. Ecosystem

    // Есть расширения
    - flutter_bloc (для UI)
    - bloc (сам пакет)
    - bloc_test (для тестирования)
    

Недостатки BLoC ❌

  1. Много boilerplate кода

    // Нужно написать:
    - enum Events
    - class State
    - event handler
    // Для простого "increment" это много
    
  2. Steep learning curve

    • Event sourcing
    • Stream'ы
    • Rx принципы
    • Новичкам сложно
  3. Navigation complexity

    • Не встроена
    • Нужна отдельная библиотека (navigator 2.0)
  4. DI все равно нужна

    // Нужно провайдить BLoC в дерево
    BlocProvider(
      create: (context) => CounterBloc(),
      child: CounterPage(),
    )
    
  5. Может быть overkill для простых экранов

    • Чтобы показать один текст, нужно BLoC?
    • Перекомплексфикация

Сравнительная таблица

КритерийGetXBLoC
Простота⭐⭐⭐⭐⭐⭐⭐⭐
Тестируемость⭐⭐⭐⭐⭐⭐⭐⭐
Boilerplate⭐⭐⭐⭐⭐⭐⭐
Learning curve⭐⭐⭐⭐⭐⭐
Scalability⭐⭐⭐⭐⭐⭐⭐⭐
OfficialityНе-officialCommunity standard
All-in-one
Magic⭐⭐⭐⭐

Мой выбор: Почему я выбираю Provider/Riverpod

// Riverpod example (мой выбор)
final countProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
  return CounterNotifier();
});

class CounterNotifier extends StateNotifier<int> {
  CounterNotifier() : super(0);
  
  void increment() => state++;
}

// UI
class CounterPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(countProvider);
    
    return Scaffold(
      body: Text('Count: $count'),
      floatingActionButton: FloatingActionButton(
        onPressed: () => ref.read(countProvider.notifier).increment(),
        child: Icon(Icons.add),
      ),
    );
  }
}

Почему Riverpod?

  1. Simple but powerful

    • Простой синтаксис (проще чем BLoC)
    • Но более явный чем GetX
  2. Functional approach

    • No boilerplate classes
    • Все делается функциями
  3. Easier testing

    test('counter provider', () {
      final container = ProviderContainer();
      expect(container.read(countProvider), 0);
      container.read(countProvider.notifier).increment();
      expect(container.read(countProvider), 1);
    });
    
  4. Good balance

    • Не слишком магический (как GetX)
    • Не слишком verbose (как BLoC)

Practical scenarios

Используй GetX если:

  • ✅ Быстрый MVP
  • ✅ Простое приложение
  • ✅ Полный контроль над project'ом
  • ✅ Нужна Navigation из коробки

Используй BLoC если:

  • ✅ Enterprise app
  • ✅ Много разработчиков
  • ✅ Стандартизированная архитектура важна
  • ✅ Тестирование в приоритете

Используй Provider/Riverpod если:

  • ✅ Balanced approach
  • ✅ Средний размер app
  • ✅ Хочешь простоты и мощи
  • ✅ Functional programming нравится

Код примера: простой счетчик

GetX:

// Меньше всего кода
final count = 0.obs;
count++;

BLoC:

// Много кода, но очень explicit
enum CounterEvent { increment }
class CounterBloc extends Bloc<CounterEvent, int> { ... }

Provider:

// Middle ground
final countProvider = StateNotifierProvider((ref) => CounterNotifier());
ref.read(countProvider.notifier).increment();

Итоги

GetX vs BLoC: Как выбирать?

  1. GetX если нужна скорость и простота (стартапы, MVP)
  2. BLoC если нужна масштабируемость и чистота (enterprise)
  3. Provider/Riverpod если нужен баланс (мой выбор для большинства проектов)

В конце концов, какой бы вы ни выбрали, главное — выбрать и stick with it. Переключение между подходами посередине проекта — это боль. Все три работают, выбор зависит от ваших приоритетов и опыта команды.

Лично я рекомендую Provider для большинства случаев — это золотая середина между простотой и правильностью.

Что выберешь GetX или BLoC? | PrepBro