← Назад к вопросам
Что такое Riverpod и чем он отличается от Provider?
2.0 Middle🔥 202 комментариев
#Dart#State Management
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Riverpod vs Provider
Что такое Riverpod?
Riverpod — это современное, типобезопасное решение для управления состоянием во Flutter. Это эволюция Provider, созданная тем же автором (Romain Rastel).
Riverpod = **River** (река) + **Pod** (коробка) — контейнер для потока данных.
Основные отличия Riverpod от Provider
| Характеристика | Provider | Riverpod |
|---|---|---|
| Типизация | Слабая | Полная типизация |
| Зависимости | Через InheritedWidget | Через параметры |
| Тестирование | Требует MultiProvider | Встроено в архитектуру |
| API | InheritedWidget | Функциональный подход |
| Производительность | Хорошая | Отличная |
| Кривая обучения | Средняя | Крутая |
| Комбинирование | Сложнее | Просто через параметры |
Provider — начало
import "package:provider/provider.dart";
class Counter extends ChangeNotifier {
int count = 0;
void increment() {
count++;
notifyListeners();
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => Counter(),
child: MyApp(),
),
);
}
// Использование
class CounterWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<Counter>(
builder: (context, counter, child) {
return Text("Count: ${counter.count}");
},
);
}
}
Riverpod — современный подход
import "package:flutter_riverpod/flutter_riverpod.dart";
// 1. Создать Provider (как глобальная переменная)
final counterProvider = StateNotifierProvider<Counter, int>((ref) {
return Counter();
});
class Counter extends StateNotifier<int> {
Counter() : super(0);
void increment() {
state++;
}
}
void main() {
runApp(
ProviderScope(
child: MyApp(),
),
);
}
// 2. Использование
class CounterWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Column(
children: [
Text("Count: $count"),
ElevatedButton(
onPressed: () {
ref.read(counterProvider.notifier).increment();
},
child: Text("Increment"),
),
],
);
}
}
Типы Providers в Riverpod
// 1. Provider — для статических значений
final apiUrlProvider = Provider<String>((ref) {
return "https://api.example.com";
});
// 2. StateProvider — для простого состояния
final selectedIndexProvider = StateProvider<int>((ref) {
return 0;
});
// 3. StateNotifierProvider — для сложной логики
final counterProvider = StateNotifierProvider<Counter, int>((ref) {
return Counter();
});
// 4. FutureProvider — для асинхронных операций
final userProvider = FutureProvider<User>((ref) async {
final response = await http.get(
Uri.parse("https://api.example.com/user"),
);
return User.fromJson(jsonDecode(response.body));
});
// 5. StreamProvider — для потока данных
final messagesProvider = StreamProvider<Message>((ref) async* {
final controller = StreamController<Message>();
// Emit значения
yield Message("Hello");
yield Message("World");
});
// 6. Computed Provider — производные значения
final doubleCountProvider = Provider<int>((ref) {
final count = ref.watch(counterProvider);
return count * 2;
});
Зависимости между Providers
// Простая цепочка
final userIdProvider = StateProvider<int>((ref) => 1);
final userProvider = FutureProvider<User>((ref) async {
final userId = ref.watch(userIdProvider);
final response = await http.get(
Uri.parse("https://api.example.com/users/$userId"),
);
return User.fromJson(jsonDecode(response.body));
});
// Множественные зависимости
final filteredUsersProvider = FutureProvider<List<User>>((ref) async {
final users = await ref.watch(usersProvider.future);
final searchQuery = ref.watch(searchQueryProvider);
return users
.where((user) => user.name.contains(searchQuery))
.toList();
});
Обработка FutureProvider
class UserWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final userAsync = ref.watch(userProvider);
return userAsync.when(
loading: () => CircularProgressIndicator(),
error: (error, stackTrace) => Text("Error: $error"),
data: (user) => Text("User: ${user.name}"),
);
}
}
Тестирование с Riverpod
import "package:flutter_test/flutter_test.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
void main() {
group("Counter Provider Tests", () {
test("counter increments", () {
final container = ProviderContainer();
// Получить начальное состояние
expect(container.read(counterProvider), 0);
// Изменить состояние
container.read(counterProvider.notifier).increment();
// Проверить новое состояние
expect(container.read(counterProvider), 1);
});
test("UserProvider fetches user", () async {
final container = ProviderContainer();
// Дождаться Future
final user = await container.read(userProvider.future);
expect(user.name, "John");
});
});
}
StateNotifierProvider с логикой
class UserNotifier extends StateNotifier<User?> {
UserNotifier() : super(null);
Future<void> fetchUser(int id) async {
state = null; // Loading
try {
final response = await http.get(
Uri.parse("https://api.example.com/users/$id"),
);
state = User.fromJson(jsonDecode(response.body));
} catch (e) {
state = null;
rethrow;
}
}
void updateName(String newName) {
if (state != null) {
state = state!.copyWith(name: newName);
}
}
}
final userProvider = StateNotifierProvider<UserNotifier, User?>((ref) {
return UserNotifier();
});
// Использование
class UserScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(userProvider);
return Column(
children: [
if (user != null) Text(user.name),
ElevatedButton(
onPressed: () {
ref.read(userProvider.notifier).fetchUser(1);
},
child: Text("Load User"),
),
],
);
}
}
Riverpod vs Provider: реальный пример
// ❌ PROVIDER — сложнее
class UserRepository {
Future<User> fetchUser(int id) async {
// ...
}
}
final userRepositoryProvider = Provider((ref) => UserRepository());
final userProvider = FutureProvider.autoDispose
.family<User, int>((ref, id) async {
final repo = ref.watch(userRepositoryProvider);
return repo.fetchUser(id);
});
// ✅ RIVERPOD — проще
final userProvider = FutureProvider.autoDispose
.family<User, int>((ref, id) async {
final response = await http.get(
Uri.parse("https://api.example.com/users/$id"),
);
return User.fromJson(jsonDecode(response.body));
});
Когда использовать?
Используйте Provider, если:
- Простое приложение
- Новичок во Flutter
- Не нужна сложная типизация
Используйте Riverpod, если:
- Масштабируемое приложение
- Важна типизация
- Нужна хорошая тестируемость
- Команда готова к крутой кривой обучения
Миграция с Provider на Riverpod
// Provider
final counterProvider = ChangeNotifierProvider(
(ref) => Counter(),
);
// Riverpod эквивалент
final counterProvider = StateNotifierProvider<Counter, int>(
(ref) => Counter(),
);
Best Practices
- Riverpod для новых проектов
- Provider если уже используется
- Комбинируйте простые providers
- Используйте .family для параметризированных providers
- Используйте .autoDispose для очистки ресурсов
Итог
Riverpod — это эволюция Provider с лучшей типизацией, тестируемостью и функциональным подходом. Для новых проектов рекомендуется использовать Riverpod.