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

Какой state manager выберешь для написания приложения с нуля?

2.7 Senior🔥 261 комментариев
#State Management#Архитектура Flutter

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

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

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

Выбор State Manager для нового приложения

Выбор правильного state manager — критически важное решение, которое влияет на архитектуру и масштабируемость приложения. Давайте разберемся, какой выбрать для проекта с нуля.

Мой выбор: Riverpod

Для нового приложения я выберу Riverpod, вот почему:

Преимущества Riverpod

1. Лучшая производительность и оптимизации:

final userProvider = FutureProvider<User>((ref) async {
  return fetchUser();
});

final userGreetingProvider = Provider<String>((ref) {
  final user = ref.watch(userProvider);
  return user.when(
    data: (u) => 'Hello, ${u.name}!',
    loading: () => 'Loading...',
    error: (err, st) => 'Error',
  );
});

2. Встроенная поддержка async операций:

final postsProvider = FutureProvider<List<Post>>((ref) async {
  final response = await dio.get('/posts');
  return (response.data as List)
      .map((p) => Post.fromJson(p))
      .toList();
});

final userPostsProvider = FutureProvider<List<Post>>((ref) async {
  final user = await ref.watch(userProvider.future);
  final response = await dio.get('/users/${user.id}/posts');
  return (response.data as List)
      .map((p) => Post.fromJson(p))
      .toList();
});

3. Функциональный подход без классов-нотификаторов:

final counterProvider = StateProvider<int>((ref) => 0);

final doubledProvider = Provider<int>((ref) {
  final count = ref.watch(counterProvider);
  return count * 2;
});

4. Отличная поддержка для тестирования:

test('Counter increments', () async {
  final container = ProviderContainer();
  expect(container.read(counterProvider), 0);
  container.read(counterProvider.notifier).state = 1;
  expect(container.read(counterProvider), 1);
});

5. Code generation с Riverpod Generator:

@riverpod
Future<User> user(UserRef ref, String userId) async {
  final response = await dio.get('/users/$userId');
  return User.fromJson(response.data);
}

Недостатки Riverpod

  • Кривая обучения круче, чем у Provider
  • Требует понимания функциональных концепций
  • Синтаксис иногда многословный

Альтернативы и когда их выбирать

1. Provider — для простых проектов

class UserNotifier extends StateNotifier<User?> {
  UserNotifier() : super(null);
  
  Future<void> fetchUser(String id) async {
    state = await userRepository.getUser(id);
  }
}

final userProvider = StateNotifierProvider<UserNotifier, User?>(
  (ref) => UserNotifier(),
);

Когда выбрать:

  • Прототип или MVP
  • Небольшая команда
  • Простая архитектура
  • Хотим минимализм

Плюсы: проще в изучении, меньше boilerplate, хорошая документация

Минусы: меньше оптимизаций, сложнее с DI

2. GetX — для быстрого прототипирования

class CounterController extends GetxController {
  var count = 0.obs;
  void increment() => count++;
}

class CounterView extends GetView<CounterController> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Obx(() => Text('${controller.count}')),
      ),
    );
  }
}

Когда выбрать: быстрое прототипирование, стартапы, простые приложения

Плюсы: очень просто, реактивность из коробки, routing и DI включены

Минусы: сложнее на больших проектах, дополнительная магия

3. Bloc Pattern — для enterprise приложений

class UserBloc extends Bloc<UserEvent, UserState> {
  UserBloc(this._userRepository) : super(UserInitial()) {
    on<FetchUserEvent>(_onFetchUser);
  }
  
  final UserRepository _userRepository;
  
  Future<void> _onFetchUser(
    FetchUserEvent event,
    Emitter<UserState> emit,
  ) async {
    emit(UserLoading());
    try {
      final user = await _userRepository.getUser(event.id);
      emit(UserLoaded(user));
    } catch (e) {
      emit(UserError(e.toString()));
    }
  }
}

Когда выбрать: большие team приложения, высокие требования к testability

Плюсы: отличная архитектура, легко тестировать, масштабируется

Минусы: много boilerplate, кривая обучения

4. MobX — для реактивных приложений

class CounterStore = _CounterStoreBase with _$CounterStore;

abstract class _CounterStoreBase with Store {
  @observable
  int count = 0;
  
  @action
  void increment() => count++;
  
  @computed
  bool get isEven => count % 2 == 0;
}

Когда выбрать: нужна четкая реактивность, JavaScript background

Матрица выбора

Размер проектаПрототипProduction
Простое (до 10 экранов)GetXProvider
Среднее (10-50 экранов)ProviderRiverpod
Большое (более 50 экранов)RiverpodBloc
EnterpriseBlocBloc

Рекомендуемая архитектура с Riverpod

class User {
  final String id;
  final String name;
  User({required this.id, required this.name});
}

class UserRepository {
  Future<User> getUser(String id) async {
    final response = await dio.get('/users/$id');
    return User.fromJson(response.data);
  }
}

final userRepositoryProvider = Provider((ref) => UserRepository());

final userProvider = FutureProvider.family<User, String>(
  (ref, userId) async {
    final repo = ref.watch(userRepositoryProvider);
    return repo.getUser(userId);
  },
);

class UserScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final userAsync = ref.watch(userProvider('123'));
    
    return userAsync.when(
      data: (user) => Text('Hello, ${user.name}'),
      loading: () => CircularProgressIndicator(),
      error: (err, st) => Text('Error: $err'),
    );
  }
}

Final Recommendation

Для нового приложения:

Если начинаете с нуля: Riverpod — лучший выбор на долгосрочку

Если нужен быстрый результат: Provider или GetX — простая кривая обучения

Если большой проект или team: Bloc Pattern — надежная архитектура

Если React/JavaScript background: MobX — привычные концепции

Мой выбор: Riverpod потому что лучшая производительность, функциональный подход, отличная документация, легко переходить с Provider, future-proof решение.