[Flutter]状態管理:Riverpod の使い方

Flutterには、依存性注入を行うための機能が多数提供されており、それらの中には、Providerパッケージも含まれています。 Providerは、Flutterにおける依存性注入のためのパッケージで、簡単にデータを渡すことができます。しかし、大規模なアプリケーションを開発する場合、Providerのデータ管理が複雑になります。そこで、Riverpodというパッケージが登場しました。

Riverpodは、Flutterにおける依存性注入をより強力に行えるようにするパッケージです。Riverpodは、Providerパッケージをベースにしており、より強力なスコープ管理機能や、簡単に切り替えられるテストモードなど、多数の機能を提供しています。ここでは、Riverpodの詳細な使い方を紹介します。

目次

Riverpodの導入

Riverpodを使用するには、flutter_riverpodパッケージを追加する必要があります。pubspec.yamlファイルに以下の依存関係を追加してください。

dependencies:
  flutter_riverpod: ^2.3.6

Riverpodを使用するには、FlutterのウィジェットツリーのルートであるMaterialAppウィジェット内で、ProviderScopeを使用してRiverpodを初期化する必要があります。

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(
    ProviderScope(
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My App',
      home: MyHomePage(),
    );
  }
}

Riverpodの使い方

Riverpodでは、Providerと呼ばれる機能を使用して、データを提供します。Providerは、生成された値を格納し、必要に応じてリビルドをトリガーする値の変更を検出します。ここでは、簡単なカウンターアプリを作成し、Riverpodの基本的な使い方を説明します。

final counterProvider = StateNotifierProvider<Counter, int>((ref) {
  return Counter();
});

class Counter extends StateNotifier<int> {
  Counter() : super(0);
  void increment() => state++;
}

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

    return Scaffold(
      appBar: AppBar(
        title: Text('Counter App'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Count:',
            ),
            Text(
              '$count',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          ref.read(counterProvider.notifier).increment();
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

この例では、`counterProvider`という`StateNotifierProvider`を作成して、初期値として`0`を設定しています。`MyHomePage`ウィジェットは、`ConsumerWidget`を継承しており、`counterProvider`を監視して、値が変更された場合にウィジェットを再ビルドします。`ref.read()`メソッドを使用して、`counterProvider`を更新し、カウンターを増やすことができます。

複数のProviderScopeを管理する

Riverpodでは、`ProviderScope`を使用して、異なるスコープでデータを管理することができます。例えば、アプリのグローバルな状態を管理する`ProviderScope`と、画面単位で状態を管理する`ProviderScope`を作成することができます。`ProviderScope`により、データを分離することができます。

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(
    ProviderScope(
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My App',
      home: MyHomePage(),
    );
  }
}

class Counter extends StateNotifier<int> {
  Counter() : super(0);
  void increment() => state++;
}

final localcounterProvider = StateNotifierProvider<Counter, int>((ref) {
  return Counter();
});

final globalCounterProvider = StateNotifierProvider<Counter, int>((ref) {
  return Counter();
});

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

    return Text(
      '$count',
      style: Theme.of(context).textTheme.headlineLarge,
    );
  }
}

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

    return Text(
      '$count',
      style: Theme.of(context).textTheme.headlineMedium,
    );
  }
}

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

    return Scaffold(
      appBar: AppBar(
        title: Text('Counter App'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Global Count:',
            ),
            GlobalCounterWidget(),
            SizedBox(height: 20),
            ProviderScope(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Text(
                    'Local Count:',
                  ),
                  LocalCounterWidget(),
                ],
              ),
              overrides: [
                localcounterProvider.overrideWith((ref) => Counter())
              ],
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          ref.read(localcounterProvider.notifier).increment();
          ref.read(globalCounterProvider.notifier).increment();
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

この例では、globalCounterProviderというグローバルなStateNotifierProviderを作成し、グローバルな状態を管理します。GlobalCounterWidgetウィジェットでは、globalCounterProviderを監視して、その値を表示しています。

ProviderScopeを使用して、ローカルな状態を管理するために、localcounterProviderを定義し、LocalCounterWidgetウィジェットで監視します。ProviderScope内でlocalcounterProviderを再定義し、初期値を0に設定しています。これにより、グローバルなカウンターとローカルなカウンターが独立して存在することができます。

MyHomePageウィジェットは、2つのカウンターウィジェットを含んでいます。1つはグローバルなカウンターを監視し、もう1つはローカルなカウンターを監視します。ProviderScopeを使用して、ローカルなカウンターウィジェットにcounterProviderを渡し、グローバルなカウンターとは独立してカウントアップすることができます。

上記サンプルコードの場合、localcounterProvideroverride部分でCounter()を指定しています。そのため、ボタンを押下したら、globalは値がカウントアップしますが、localの値はカウントアップせずにゼロのままになります。

まとめ

Providerパターンの改良版であり、型安全性が高く、可読性の向上を提供します。また、テストを容易にするためにProviderScopeを使用して異なる状態を提供できます。Riverpodは、DI(依存性注入)やルーティングとの統合も容易で、アプリケーション全体の開発効率を向上させます。状態管理において柔軟性が求められる場合において、Riverpodは強力な選択肢となります。

Riverpod以外の状態管理方法については下記記事および、その記事内のリンクページの詳細な使い方を参照してみて下さい。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次