Есть ли у вас опыт работы с Riverpod во Flutter?

Ответ

Да, Riverpod стал моим основным выбором для управления состоянием в новых Flutter-проектах. Я рассматриваю его как эволюцию Provider, исправляющую его ключевые боли и предлагающую более мощную, безопасную и гибкую систему.

Ключевые преимущества, которые я ценю на практике:

  1. Компиляционно-безопасный: Ошибки, связанные с неправильным типом провайдера или его отсутствием, отлавливаются на этапе компиляции, а не во время выполнения.
  2. Не зависит от BuildContext: Провайдеры можно читать и слушать в любом месте Dart-кода (в бизнес-логике, сервисах), что сильно упрощает архитектуру.
  3. Гибкая система провайдеров: Разные типы для разных задач (Provider, StateProvider, StateNotifierProvider, FutureProvider, StreamProvider).
  4. Встроенные возможности: Автоматическое кэширование, отмена запросов у FutureProvider/StreamProvider, ref.watch для реактивности и ref.read для разовых действий.

Пример из реального проекта:

// 1. Провайдер для сервиса (не зависит от UI)
final apiClientProvider = Provider<ApiClient>((ref) {
  final dio = ref.read(dioProvider); // Читаем другой провайдер
  return ApiClient(dio);
});

// 2. StateNotifierProvider для сложной бизнес-логики
final authStateProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) {
  final api = ref.watch(apiClientProvider); // Реактивная зависимость
  return AuthNotifier(api);
});

// 3. FutureProvider для асинхронных данных с кэшированием
final userProfileProvider = FutureProvider<UserProfile>((ref) async {
  final userId = ref.watch(authStateProvider.select((s) => s.userId));
  final api = ref.read(apiClientProvider);
  return await api.fetchUserProfile(userId!);
});

Использование в виджете с ConsumerWidget или ConsumerStatefulWidget:

class ProfileScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // ref заменяет context для доступа к провайдерам
    final authState = ref.watch(authStateProvider);
    final userProfileAsync = ref.watch(userProfileProvider);

    return userProfileAsync.when(
      data: (profile) => Scaffold(
        appBar: AppBar(title: Text(profile.name)),
        body: ...,
      ),
      loading: () => Center(child: CircularProgressIndicator()),
      error: (err, stack) => Center(child: Text('Error: $err')),
    );
  }
}

Мой опыт: Я использовал Riverpod для построения масштабируемой архитектуры в коммерческом приложении. Возможность легко комбинировать провайдеры, создавать производные состояния с select и иметь всю бизнес-логику, полностью отделённую от UI-слоя, значительно повысило тестируемость и поддерживаемость кода.