🐥

カウンターアプリから学ぶRiverpod入門(riverpod_generator)

に公開

概要

こんにちは。魚です。

本記事は、riverpodやriverpod_generatorについて勉強しているけど、結局riverpodってどんな仕組みで動いているの?なんだかいくつかの要素に分かれているみたいだけど、それってコードのどの部分にあたるの?という壁にぶつかった方のための記事です。

なんとな〜く知識はついてきたけど、点と点がバラバラなんだよな、、って人の、線を繋げる手助けになれば嬉しいです!

※イメージを掴むための記事なので、詳細な説明は省略しています。

Riverpodの構成要素

まず初めに、Riverpodは基本的に以下の3つの要素で成り立っています。

  • Provider
  • ConsumerWidget
  • ProviderScope

Providerとは

Providerアプリ内で共有するデータ(状態・値)を公開する窓口」 です。
通常は グローバルな final 変数 として宣言します。


ConsumerWidgetとは

Providerデータの倉庫(提供者) だとしたら、
ConsumerWidget は、 そのデータを受け取って UI に反映する担当者 です。


ProviderScopeとは

ProviderScope は、Riverpod の状態管理を機能させるために アプリ全体を囲むウィジェット です。

これにより、アプリ内のどのウィジェットからでも Provider にアクセスできるようになります。
通常はアプリのルート(MyApp など)を ProviderScope で囲んで使います。

カウンターアプリ完成コード

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

part 'main.g.dart';


class Counter extends _$Counter {
  
  int build() {
    return 0; 
  }

  void increment() {
    state++;
  }
}

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Riverpod Counter',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends ConsumerWidget {
  const MyHomePage({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    final counter = ref.read(counterProvider.notifier); 

    return Scaffold(
      appBar: AppBar(title: const Text('Riverpod Counter')),
      body: Center(
        child: Text(
          '$count',
          style: Theme.of(context).textTheme.headlineMedium,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => counter.increment(),
        child: const Icon(Icons.add),
      ),
    );
  }
}

環境構築

まず最初に環境構築をします。
ターミナル二以下をコピペしてパッケージのインストールを行います。

flutter pub add flutter_riverpod
flutter pub add riverpod_annotation
flutter pub add dev:riverpod_generator
flutter pub add dev:build_runner
flutter pub add dev:custom_lint
flutter pub add dev:riverpod_lint

次に、riverpod_generatorを扱えるように以下コマンドでbuild_runnerを実行します。

dart run build_runner watch

これで環境構築は完了です!


各要素の説明

Provider(アプリ内で共有するデータ(状態・値)を公開する窓口)


class Counter extends _$Counter {
  
  int build() {
    // この Provider が管理する状態の初期値を返す
    return 0; 
  }

  void increment() {
    //状態を更新するためのメソッドを書く
    state++;
  }
}

Counter:状態を管理するクラス。

_$Counter:Counterが_$Counterをextendsすることで、Providerとつながる仕組みが使えるようになる。

CounterProvider
・Provider本体
・build_runnerによってmain.g.dartに作成される。
・後ほどCounterProviderにアクセスすることで値を読み取ったり、更新したりする。


ConsumerWidget(UIに反映する担当者)

//状態によってUIを変更したいクラスにextendsする。
class MyHomePage extends ConsumerWidget {
  const MyHomePage({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider); // 状態の監視(最新の状態がcountに入る)
    final counter = ref.read(counterProvider.notifier); // 状態を変更するための準備

    return Scaffold(
      appBar: AppBar(title: const Text('Riverpod Counter')),
      body: Center(
        child: Text(
          '$count',
          style: Theme.of(context).textTheme.headlineMedium,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        //ounterクラス内で宣言したincrementメソッドを実行
        onPressed: () => counter.increment(),
        child: const Icon(Icons.add),
      ),
    );
  }
}

ConsumerWidget:状態によってUIを変更したいクラスにextendsする。

ref.watch(counterProvider) → 状態を監視(変更があると画面を再描画)
ref.read(counterProvider.notifier) → 状態を変更するための準備
counter.increment() → Counterクラス内で宣言したincrementメソッドを実行


ProviderScope

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

アプリ全体を囲んでRiverpodが使用できるようにする。


まとめ

いかがだったでしょうか?
今回の記事でイメージを掴めたら、あとは経験あるのみ!どんどんコーディングしてRiverpodへの知識を一緒に深めていきましょう!😊

Discussion