Flutter開発入門47 hooks_riverpodでのStreamProvider、NotifierProvider、StateNotifierProvider、ChangeNotifierProviderの使い方

Flutter

はじめに

hooks_riverpodは、Flutterアプリの開発において、強力で柔軟な状態管理を提供します。特に、非同期処理を扱うためのStreamProvider、状態を管理するためのNotifierProviderStateNotifierProvider、および従来のChangeNotifierに基づいたChangeNotifierProviderは、それぞれの場面で最適なソリューションを提供します。

この記事では、StreamProvider、NotifierProvider、StateNotifierProvider、ChangeNotifierProviderの使い方について詳しく解説します。Flutter開発を効率化し、状態管理をよりシンプルにするための実践的な知識を提供します。


hooks_riverpodとは?

hooks_riverpodは、Riverpodの状態管理機能とFlutter Hooksのフック機能を統合したライブラリです。Riverpodによる強力な状態管理に、Flutter Hooksを組み合わせることで、ウィジェットのライフサイクルに応じた簡潔な状態管理を実現します。

この記事では、特に以下のプロバイダについて詳しく見ていきます。

  • StreamProvider:非同期データのストリームを管理。
  • NotifierProviderNotifierクラスをベースにした状態管理。
  • StateNotifierProviderStateNotifierを使って状態を管理する。
  • ChangeNotifierProvider:従来のChangeNotifierベースの状態管理。

hooks_riverpodが導入が済んでいない方は以下記事を確認してください。


StreamProviderの使い方

StreamProviderは、非同期でデータのストリームを提供する際に使用します。たとえば、リアルタイムに更新されるデータや、ストリームAPIのデータを扱う場合に便利です。

例:StreamProviderでのリアルタイムカウント

final countStreamProvider = StreamProvider<int>((ref) {
  return Stream.periodic(const Duration(seconds: 1), (count) => count);
});

class HomeScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final asyncValue = ref.watch(countStreamProvider);

    return Scaffold(
      appBar: AppBar(title: const Text('StreamProvider Example')),
      body: Center(
        child: asyncValue.when(
          data: (count) => Text('Count: $count'),
          loading: () => CircularProgressIndicator(),
          error: (err, stack) => Text('Error: $err'),
        ),
      ),
    );
  }
}

解説

  • StreamProvider:非同期ストリームをプロバイダとして提供し、UIにリアルタイムで反映できます。
  • Stream.periodic:一定の間隔でデータを発行するストリームの生成方法です。
  • asyncValue.whenStreamProviderの状態に応じて、データ・ローディング・エラーのそれぞれを表示します。

NotifierProviderの使い方

NotifierProviderは、Notifierクラスに基づいたプロバイダで、Notifierを使って状態を管理し、それに応じてUIをリビルドする際に使用します。Notifierは、状態を持たせ、notifyListenersで更新するシンプルな仕組みです。

例:NotifierProviderでカウンターを管理

class CounterNotifier extends Notifier<int> {
  @override
  int build() => 0;

  void increment() => state++;
}

final counterProvider = NotifierProvider<CounterNotifier, int>(() {
  return CounterNotifier();
});

class HomeScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);

    return Scaffold(
      appBar: AppBar(title: const Text('NotifierProvider Example')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Count: $count', style: const TextStyle(fontSize: 40)),
            ElevatedButton(
              onPressed: () => ref.read(counterProvider.notifier).increment(),
              child: const Text('Increment'),
            ),
          ],
        ),
      ),
    );
  }
}

解説

  • Notifier:シンプルな状態管理クラスで、状態を管理し、変更があった際にリビルドをトリガーします。
  • NotifierProviderNotifierをプロバイダとして使用し、状態を管理します。
  • ref.watch(counterProvider):カウンターの現在の値を監視し、UIに反映します。
  • ref.read(counterProvider.notifier).increment()Notifierのメソッドを使って状態を変更します。

StateNotifierProviderの使い方

StateNotifierProviderは、StateNotifierクラスを使って、状態を細かく管理したいときに便利です。StateNotifierは、状態を変更するためのメソッドを定義でき、状態の変更を安全に管理できます。

例:StateNotifierProviderでカウンターを管理

class CounterStateNotifier extends StateNotifier<int> {
  CounterStateNotifier() : super(0);

  void increment() => state++;
}

final counterStateNotifierProvider =
    StateNotifierProvider<CounterStateNotifier, int>((ref) {
  return CounterStateNotifier();
});

class HomeScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterStateNotifierProvider);

    return Scaffold(
      appBar: AppBar(title: const Text('StateNotifierProvider Example')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Count: $count', style: const TextStyle(fontSize: 40)),
            ElevatedButton(
              onPressed: () =>
                  ref.read(counterStateNotifierProvider.notifier).increment(),
              child: const Text('Increment'),
            ),
          ],
        ),
      ),
    );
  }
}

解説

  • StateNotifier:状態を管理するためのクラスで、複雑なロジックにも対応できます。
  • StateNotifierProviderStateNotifierをプロバイダとして使用し、状態の管理を行います。
  • state:現在の状態を保持するプロパティで、状態が更新されるとリビルドがトリガーされます。

ChangeNotifierProviderの使い方

ChangeNotifierProviderは、従来のChangeNotifierベースの状態管理を使いたい場合に有効です。ChangeNotifierは、notifyListenersメソッドを使って状態の更新を通知します。

例:ChangeNotifierProviderでカウンターを管理

class CounterChangeNotifier extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

final counterChangeNotifierProvider =
    ChangeNotifierProvider<CounterChangeNotifier>((ref) {
  return CounterChangeNotifier();
});

class HomeScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final counter = ref.watch(counterChangeNotifierProvider);

    return Scaffold(
      appBar: AppBar(title: const Text('ChangeNotifierProvider Example')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Count: ${counter.count}',
                style: const TextStyle(fontSize: 40)),
            ElevatedButton(
              onPressed: () => counter.increment(),
              child: const Text('Increment'),
            ),
          ],
        ),
      ),
    );
  }
}

解説

  • ChangeNotifier:リスナーに状態の変更を通知するためのクラスで、従来のFlutterの状態管理でよく使われます。
  • ChangeNotifierProviderChangeNotifierをプロバイダとして使用し、notifyListenersでUIをリビルドします。

まとめ

Flutterでの状態管理において、hooks_riverpodは強力なツールです。今回紹介したStreamProviderNotifierProviderStateNotifierProvider、およびChangeNotifierProviderを適切に使い分けることで、アプリケーションの規模に関わらず、効率的に状態を管理できます。それぞれのプロバイダは特定のユースケースに最適化されており、状態の再利用や非同期データの管理に役立ちます。

コメント