Chapter 28無料公開

[v1-dev] riverpod v1.0.0 で変わること

村松龍之介
村松龍之介
2021.09.13に更新

この章は riverpod v1.0.0-dev の公開を受けて随時アップデート中です。
v1.0.0 の安定板が公開後に全チャプターを更新予定です。

RiverpodではCLIマイグレーションツールが提供される予定です。
v1.0.0への更新は、変更量が多いためマイグレーションツールを使った移行をお勧めします。


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);
  }
}

v.1.0.0 以降

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);
  }
}

v.1.0.0 以降

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

ProviderListener が非推奨になったため、代わりに ref.listen を使いましょう

再構築させずに 各Provider の値の変更を購読する記法が変更されました。

従来の書き方

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...'),
  }
}

v.1.0.0 以降

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内で ref.state を使うことで現在公開されている値を参照できるようになりました

  • Provider
  • FutureProvider
  • StreamProvider
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 を使いましょう

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(
    ...
  ),
);

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())

その他の変更

  • 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は、プロバイダーの作成が必要になりました

参考リンク

https://pub.dev/packages/hooks_riverpod/versions/1.0.0-dev.7/changelog

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

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