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

Что такое 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

ХарактеристикаProviderRiverpod
ТипизацияСлабаяПолная типизация
ЗависимостиЧерез InheritedWidgetЧерез параметры
ТестированиеТребует MultiProviderВстроено в архитектуру
APIInheritedWidgetФункциональный подход
ПроизводительностьХорошаяОтличная
Кривая обученияСредняяКрутая
КомбинированиеСложнееПросто через параметры

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.