Chapter 02

Riverpod v1.0.0 (stable)の変更点(v0.14.0との比較)

村松龍之介
村松龍之介
2023.02.12に更新
このチャプターの目次

2021年11月6日、Riverpod 1.0.0 がリリースされました🎉

v1のリリースに伴い、本書では v1以降 をメインとして取り扱います。
v0.14.0 までのバージョンを使用されている場合は、 v0.14.0以前版 とタイトルに付くチャプターをご覧ください。
(今後は、間違いや誤字の訂正を除き、v1用のチャプターのみ更新していきます。)

変更履歴

  • 2021年11月6日: stable の変更内容を追記
    • AsyncValueを提供する全てのProviderで await を使用できるようになりました
  • 2021年10月xx日: dev.10, dev.11 の変更内容を追記
  • 2021年10月7日: dev.8, dev.9 の変更内容を追記

Riverpod: v1.0.0 を使用したリポジトリです。
https://github.com/altive/flutter_app_template


hooks_riverpod のみの変更点

flutter_riverpod を使っている方には影響ありません。

useProvider が削除されました

以前は、 HookWidgetuseProvider の組み合わせにより、各Providerを読み取ることができていました。
v1.0.0 で ref.xxx を使用した記法に統一されたことにより、 useProvider は廃止となります。

代わりに、 HookConsumerWidgetref.watch を使用しましょう。

従来の書き方

example.dart
class Example extends HookWidget {
  
  Widget build(BuildContext context) {
    final count = useProvider(counterProvider);
  }
}

v1以降

example.dart
class Example extends HookConsumerWidget {
  
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
  }
}

置換例

  1. HookWidget -> HookConsumerWidget
  2. Widget build(BuildContext context) -> Widget build(BuildContext context, WidgetRef ref)
  3. useProvider -> ref.watch

flutter_riverpod , hooks_riverpod 共通の変更点

ConsumerWidgetConsumerbuild , builder メソッド引数が変更

ScopedReader watchWidgetRef ref に変更されました。

従来の書き方

example.dart
class Example extends ConsumerWidget {
  
  Widget build(BuildContext context, ScopedReader watch) {
    final count = watch(counterProvider);
  }
}

v1以降

example.dart
class Example extends ConsumerWidget {
  
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
  }
}

ProviderListener が非推奨に。代わりに ref.listen を使いましょう

従来の書き方

example.dart
class Example extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return ProviderListener<int>(
      provider: counterProvider,
      onChange: (context, count) {
        // `counterProvider` の値が変更されたときに実行される処理
        print('The new count is $count');
      },
      child: Text('Example Widget...'),
  }
}

v1 以降

example.dart
final counterProvider = StateNotifierProvider<Counter, int>((ref) => Counter());

class Example extends ConsumerWidget {
  
  Widget build(BuildContext context, WidgetRef ref) {
    ref.listen<int>(counterProvider, (count) {
      // `counterProvider` の値が変更されたときに実行される処理
      print('The new count is $count');
    });
    return Text('Example Widget...');
  }
}

StatefulWidgetからWidgetRefを利用するための ConsumerStatefulWidgetConsumerState が追加されました

example.dart
class Example extends ConsumerStatefulWidget {
  const Example({Key? key}) : super(key: key);

  
  _ExampleState createState() => _ExampleState();
}

class _ExampleState extends ConsumerState<Example> {
  
  Widget build(BuildContext context) {
    ref.watch(counterProvider);
    return Text('Hello World');
  }
}

すべての watch 関数で select ( myProvider.select((value) => ...) ) が使用できるようになりました

example.dart
final userProvider = StateNotifierProvider<UserController, User>((ref) => UserController());

class Example extends ConsumerWidget {
  
  Widget build(BuildContext context, WidgetRef ref) {
    final age = ref.watch(userProvider.select((user) => user.age));
  }
}

Provider/FutureProvider/StreamProvider 内で ref.state を使うことで現在公開されている値を参照できるようになりました

example.dart
final counterProvider = Provider<int>((ref) {
  ref.listen(otherProvider, (value) {
    ref.state++;
  });
  return 0;
});

StateProvider内で ref.controller にアクセスできるようになりました

example.dart
final counterProvider = StateProvider<int>((ref) {
  ref.listen(otherProvider, (value) {
    ref.controller.state++;
  });
  return 0;
});

ProviderReference.mounted が削除されました

onDispose を使用しましょう。

example.dart
final myProvider = Provider<int>((ref) {
  var mounted = true;
  ref.onDispose(() => mounted = false);
  return 0;
});

context.read が廃止、 ref.read を使いましょう

context を使ったProviderへのアクセスが廃止されました。
代わりに build メソッドの引数である WidgetRef ref を使用しましょう。

example.dart
- onPressed: context.read(myControllerProvider.notifier).save,
+ onPressed: ref.read(myControllerProvider.notifier).save,

context.refresh が廃止、 ref.refresh を使いましょう

example.dart
RefreshIndicator(
  onRefresh: () async => ref.refresh(myProvider),
  child: ListView(
    ...
  ),
);

StateProviderを読み込んだ時の戻り値がStateNotifierProviderと統一されました。

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

// int(状態)を返す
final counter1 = ref.watch(counterProvider);
// StateController<int> を返す
final counter2 = ref.watch(counterProvider.notifier);
// StateController<int> を返す
final counter3 = ref.watch(counterProvider.state);
// int(状態)を返す
final counter4 = ref.watch(counterProvider.state).state;

マイグレーション

// 以下のように書いていた場合は…
ref.read(counterProvider).state;
// 以下のようにマイグレーション可能だが
ref.read(counterProvider.state).state;
// こちらの方がより良さそう
ref.read(counterProvider);

StateController.update が追加され、以前の状態を使った更新が簡単になりました。

final myProvider = StateController((ref) => 0);
ref.read(myProvider).update((state) => state + 1);

ScopedProvider が廃止され、全てのProviderで、範囲を絞った override ができるようになった

https://twitter.com/riscait/status/1409304512221700099

従来は、 ProviderScope を使った各Providerの値の上書きはルートの ProviderScope で行うか、ScopedProvider を対象にしてしか使うことができませんでした。

今回の修正で、全ての XxxProviderScopedProvider と同様に、任意の箇所での ProviderScope/overrides による値の上書きが可能になりました。

置換例

ScopedProviderをProviderに書き換えるだけでOKです。

ScopedProvider<int>((ref) => throw UnimplementedError())Provider<int>((ref) => throw UnimplementedError())

Future/StreamProvider (AsyncValue) についての変更点 (dev.8~)

  • FutureProviderの戻り値の型が Future<T> ではなく FutureOr<T> になりました。
  • when メソッドの loadingerror にて、前 (previous) の値を使用できるようになりました。

Widget build(context, ref, WidgetRef ref) {
  return ref.watch(userProvider).when(
    error: (error, stacktrace, _) => Text('error: $error'),
    data: (user) => Text('Hello ${user.name}!'),
    loading: (previous) {
      if (previous is AsyncData<User>) {
        return Text('loading ... (previous: ${previous.value.name})'});
      }
      return CircularProgressIndicator();
    }
  );
}

AsyncValueを出力する全てのProviderで await を使用できるようになりました(stable~)

(以前は FutureProvider / StreamProvider に制限されていました)

class MyAsyncStateNotifier extends StateNotifier<AsyncValue<MyState>> {
  MyAsyncStateNotifier(): super(AsyncValue.loading()) {
    // 必要なデータを取得し、完了後 `state` を更新する
  }
}

final myAsyncStateNotifierProvider = StateNotifierProvider<MyAsyncStateNotifier, AsyncValue<MyState>>((ref) {
  return MyAsyncStateNotifier();
});

final myFutureProvider = FutureProvider((ref) async {
  // `.future` で Future を受け取り、 `await` で非同期処理の完了を待つことができる。
  final myState = await ref.watch(myAsyncStateNotifierProvider.future)
});

AsyncValue についてのその他の変更点

  • dev.8~
    • AsyncValue.copyWith が削除されました。
    • AsyncValue.error(_, stacktrace) が位置引数ではなく名前付き引数になりました。
    • AsyncValue.data が非推奨になりました。 AsyncValue.valueAsyncValue.asData を使用しましょう。
    • AsyncData , AsyncError , AsyncLoading が拡張可能になりました。
    • AsyncValue.whenOrNull を追加。 whenOrElse に似ているものの、 orElse パラメータの代わりに null を返します。
    • AsyncValue.value を追加。ローディングやエラーの状態を処理せずに値を読み取ることが可能です。
    • AsyncErrorconst でインスタンス化できるようになりました。
  • stable~
    • AsyncValue.data が廃止されました。 AsyncValue.value を使用しましょう。

その他の変更点

  • 最小要求 Dart SDKバージョンが 2.14.0 になりました。
  • ChangeNotifierProvider が nullableな ChangeNotifier をサポートするようになりました。
  • dev.1~
    • ProviderObserver.mayHaveChanged が削除されました。
    • ProviderReference は廃止され、ProviderRefBase が優先されます
    • ProviderContainer.debugProviderValues および ProviderContainer.debugProviderElementsが削除され、代わりに、ProviderContainer.getAllProviderElements を使用できるようになりました。
    • ref.container.refresh を行う代わりに ref.refresh を使ってプロバイダーを更新できるようになった
    • ref.onDispose は、プロバイダーの依存関係が変更されたことが分かった時点ですぐに dispose 関数を呼び出すようになりました
    • 各Providerは依存関係の1つが変更されリスナーがいる場合に、状態を再計算するために次の読み込みまで待つ必要がなくなりました。
    • ProviderContainer.pump が追加されました。各Providerがリスナーに通知するか破棄されるまで簡単に await するために使用できるユーティリティです。
    • familyautoDispose を使用したとき、状態に一貫性がない問題を修正。
    • ProviderObserver.didUpdateProvider は、以前の値と新しい値の両方を受け取るようになりました
    • Family.overrideWithProvider は、プロバイダーの作成が必要になりました
  • dev.8~
    • すべての overrideWithProvider メソッドが削除されました。代わりに overrideWithValue を使用しましょう。
    • すべてのProviderに、 dependencies という名前のパラメータが追加されました。
      そのProviderが依存する Providerfamily のリストを定義することができます。
    final myProvider = Provider(
      (ref) => ref.watch(otherProvider),
      dependencies: [otherProvider],
    );
    
    otherProvider が上書きされたときに myProvider を上書きするようにRiverpodに伝わります。
    • select の中で ref.watchref.read が使えなくなりました。
    // `select` 内では使用できない🙅‍♀️
    myProvider.select((value) => ref.watch(something)); 
    
  • stable
    • StreamProvider.last が廃止されました。 StreamProvider.future を代わりに使用します。
    • StreamProvider.future , StreamProvider.stream , FutureProvider.future において…
      • StreamProvider が値を出力する前に自身が再構築された場合、 StreamProvider.future は代わりに新しいstreamの "最初の値" が返されます。
      • FutureProvider で非同期処理が完了する前に自身が再構築された場合、 FutureProvider.future は代わりに新しい Future の結果が返されます。
    • 値がない限り、他のProviderと任意のProviderをオーバーライドできるようになりました。
      例えば、 StreamProvider<Model>Provider<AsyncValue<Model>> でオーバーライドできます。

参考リンク

https://pub.dev/packages/riverpod/changelog
https://pub.dev/packages/flutter_riverpod/changelog
https://pub.dev/packages/hooks_riverpod/changelog

https://github.com/rrousselGit/river_pod/pull/462/files

https://github.com/rrousselGit/river_pod/issues/531