Настройка архитектуры Riverpod для Flutter-приложения
Riverpod — переосмысление Provider от того же автора (Remi Rousselet). Он устраняет главный недостаток Provider: зависимость от BuildContext. В Riverpod провайдеры объявляются глобально как константы, доступны из любой точки кода — включая не-виджетный код, тесты, сервисы. С выходом Riverpod 2.x и code generation через riverpod_annotation бойлерплейт сократился до минимума.
Современный Riverpod с @riverpod
@riverpod
UserRepository userRepository(UserRepositoryRef ref) {
return UserRepositoryImpl(ref.watch(httpClientProvider));
}
@riverpod
class ProfileNotifier extends _$ProfileNotifier {
@override
FutureOr<UserProfile> build(String userId) async {
return ref.watch(userRepositoryProvider).getProfile(userId);
}
Future<void> refresh() async {
ref.invalidateSelf();
await future;
}
}
Генератор riverpod_generator создаёт profileNotifierProvider(userId) из этого кода. AsyncNotifier автоматически управляет AsyncValue<UserProfile> — loading, data, error.
В виджете:
class ProfileScreen extends ConsumerWidget {
final String userId;
const ProfileScreen({required this.userId});
@override
Widget build(BuildContext context, WidgetRef ref) {
final profileAsync = ref.watch(profileNotifierProvider(userId));
return profileAsync.when(
loading: () => const CircularProgressIndicator(),
error: (err, _) => ErrorView(message: err.toString()),
data: (profile) => ProfileView(profile: profile),
);
}
}
Преимущество перед Provider: комбинирование провайдеров
Riverpod позволяет провайдерам зависеть друг от друга через ref.watch. Это работает вне дерева виджетов:
@riverpod
Future<List<Post>> filteredFeed(FilteredFeedRef ref) async {
final feed = await ref.watch(feedProvider.future);
final filters = ref.watch(contentFiltersProvider);
return feed.where((p) => filters.allows(p)).toList();
}
Если contentFiltersProvider изменился — filteredFeed автоматически пересчитается. В Provider это требует ProxyProvider с ручной инвалидацией.
Тестирование
test('ProfileNotifier returns profile', () async {
final container = ProviderContainer(overrides: [
userRepositoryProvider.overrideWithValue(MockUserRepository()),
]);
final notifier = container.read(profileNotifierProvider('user-1').notifier);
expect(
await container.read(profileNotifierProvider('user-1').future),
equals(tProfile),
);
container.dispose();
});
Тест не требует pumpWidget, Flutter SDK не нужен. Это делает тесты Riverpod самыми быстрыми среди всех Flutter state management решений.
Что настраиваем
ProviderScope в корне приложения. Настройка riverpod_annotation + build_runner. Слоистая структура: repository providers → notifier providers → widget. Примеры ref.invalidate, ref.listen, ref.onDispose для управления жизненным циклом.
Сроки
Настройка Riverpod-архитектуры с нуля: 2–3 дня. Миграция с Provider на Riverpod: 1 неделя для проекта до 20 экранов. Стоимость — индивидуально.







