Flutter入門

【5分でわかる】RiverpodとProviderをわかりやすく解説 | Flutter入門


ども、木村です。本業はデータサイエンティスト、副業でアプリ開発をしています。

本記事は、Riverpodの使い方と各種Providerを解説します。

想定読者

これからRiverpodを使いたい方

今回のソースコードは以下で公開されています。

https://github.com/kimuson/riverpod_demo

Riverpodとは?

RiverpodはFlutterにおける状態管理をサポートするパッケージです。

Riverpodが登場する前はProviderというパッケージで状態管理をすることが主流でした。

RiverpodはProviderと開発者が同じであり、Providerの上位互換/改良版と言えるような状態管理パッケージになります。

ポイント

Providerの作者がRiverpodを作りました!

Riverpodを使うメリット

まず、RiverpodはProviderの弱みを補うような形で作られています。そのため、Riverpodでは以下のようなProviderの弱みを解消しています。

  • ステートの参照許可のないwidgetからアクセスをするとProviderNotFoundExceptionのエラーが発生する
  • 同じ型のProviderを利用できない
    など…

Riverpodは上記の問題を解消し、以下のようなメリット/特徴を持っています。

  • ProviderNotFoundExceptionが起こらない!
  • 同じ型で、複数の Providerを使える!
  • Providerをグローバルに利用できる!
    など

私自身、個人制作しているアプリでProviderからRiverpodにリプレイスを行いましたが、グローバルにProviderを定義できるのでソースコードが非常にスッキリしました。

ポイント

Riverpodは従来のProviderパッケージの弱みを解消します!

Riverpodの基本的な使い方

まず、FlutterのデフォルトアプリであるカウンターアプリをRiverpodにリファクタリングしながら使い方を学んでいきたいと思います。

デフォルトアプリとの差分だけみたい方は以下を参考ください。

https://github.com/kimuson/riverpod_demo/commit/f96c3d24025aff882df306cc80007c7a24783173

Riverpod のインストール

Riverpodのインストールを行います。Riverpodのパッケージは用途に合わせて3種類の中から選択します。

パッケージ名用途
flutter_riverpodFlutterでRiverpodを使うための基本機能が提供される
hooks_riverpodflutter_hooksとRiverpodの両方を使用する想定で作られている
riverpodDartパッケージ(Dartのみで動くため,Flutterに対応していない)

今回はFlutterでかつ、Riverpodの最も基本的な部分を取り扱いますので「flutter_riverpod」を使用します。

インストールはpubspec.yamlに以下の記載を追記してください。

dependencies:
  flutter:
    sdk: flutter
  flutter_riverpod: ^1.0.3 #追記

Providerの作成

providers.dartを新規で作成します。

ここには状態管理したい値を定義していきます。つまりProviderを実装します。

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

final CounterProvider = ChangeNotifierProvider((ref) => Counter());

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

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

重要なポイント

final CounterProvider = ChangeNotifierProvider((ref) => Counter());

上記がProviderで実装しています。

Riverpodには複数種類のProviderが用意されており、今回はその中の一つである「ChangeNotifierProvider」を使用します。

ChangeNotifierProviderでは、状態管理している値に変更があればその情報を参照しているwidgetに通知を飛ばし、UIを再描画してくれます。

ちなみに今回はCounter()クラスをProviderとして保存しています。

class Counter extends ChangeNotifier {}

Counter()クラスでは、ユーザーがボタンを押した回数を記録する_count変数と、ユーザーがボタンを押したときに_count変数に+1を行うincrement()メソッドを実装しています。

ChangeNotifierを継承し、increment()メソッドの中でnotifyListeners()を実行することで値の変更をUIに伝え、再描画をwidgetに促すことができます。

Providerの呼び出しと参照

main.dartを以下のように変更してください。

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

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

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

    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo Home Page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              counterProvider.count.toString(),
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          counterProvider.increment();
        },
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

重要なポイント

runApp(ProviderScope(child: const MyApp()));

ProviderScope()で親widgetを囲うことで、それ以降の子widgetでProviderを呼び出すことができるようになります。

 var counterProvider = ref.watch(CounterProvider);

ref.watch(<プロバイダー名>)を指定することで、providerを呼び出すことができます。

ちなみにこの.watch()が非常に優れもので、他のwidgetでCounterProviderの内容が変更されたらその変更を検知して、影響のある他のwidgetにも同様の変更を加え整合性を持つことができます。

ちなみに.watch()だけでなく、.read()を使うとProviderの変更はされず最初に呼び出したProviderの状態を使うことになります。

Text(
  counterProvider.count.toString(),
  style: Theme.of(context).textTheme.headline4,
 ),

counterProviderのcountにアクセスをしたい場合はcounterProvider.countのように記載すればアクセスが可能です。

またcounterProviderのincrement()メソッドにアクセスしたい時も同様にcounterProvider.increment()と記載すれば処理を実行できます。

まとめ:Riverpodを使って開発を効率的に!

Riverpodを使うことでコードの可読性や保守性が非常に良くなります。

私も初めて使った時あまりにもシンプルにコードを書けるようになったので感動しました。

Riverpodを使って一段階レベルの高いFlutterエンジニアに挑戦してみてはいかがでしょうか?

今回のソースコードは以下で公開されています。

https://github.com/kimuson/riverpod_demo

最新のFlutterの勉強方法 まとめ

Flutterを入門から実践レベルまで一通り学習できる方法をまとめました。

Flutterの勉強方法を知る

Flutterを動画で学習する

Flutterを書籍で学習する