広告 Flutter Dart Unity アプリ開発

Flutter Riverpod 2.6.1 で動作確認済のサンプルソース

現時点でのLatest:2.6.1で動作するRiverpodのサンプルソースです。

Flutter Riverpodの要点

用語説明

ConsumerWidget: 外部の「状態(State)」を観察することが出来るWidget。ref経由でProviderの中にあるStateを覗くことができる。
原則としてref.watch()メソッドを使う。(特別な場合のみ、ref.listen() , ref.read()を使う)

State:状態。グローバル変数のようなもの。単純(int,String等)、複雑(Listやクラス等)、Future(単純・複雑)、Stream(単純・複雑)等の種類がある。

Provider : 変数等の「状態(State)」をラップしているもの。状態の型に合わせて選択する必要があるが、後で説明するジェネレータを使うと意識しなくてもOK

Notifier:「状態(State)」を変更する機能。後で説明するジェネレータを使うと、_$xxxNotifier と1つの名前で統一して書ける。

main()関数では、MyApp()関数をProviderScope()関数で囲む必要がある。

Ver2でのデータの型と、対応するNotifier(状態変更ツール)、Provider(状態ラップ)

ジェネレータを使用しない場合は、手作業で適切なプロバイダを選択する必要がある。

単純系:intやString等の1つの変数
複雑系:リストやクラスのような複雑なもの
Future:後から値が確定する型
Stream:値がどんどん飛んでくるような型

データの型Notifier(状態変更ツール)Provider(状態ラップ)
単純データ・複雑データNotifierNotifierProvider
Future,StreamAsyncNotifierAsyncNotifierProvider

riverpod_generator(build_runner) を使う場合は、_$xxxNotifierを1つ定義すると、後はジェネレータが適切な型のプロバイダを選択してソースをジェネレートしてくれる

パッケージのインストール

下記コマンドを、アプリのソースルートで実行する。(VSCodeのターミナルにコピペ)赤字は必須。(Hooksは別のページで説明)

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

上記コマンドを実行すると、pubspec.yamlは、以下のようになる。(2025/2時点)

name: my_app_name
environment:
  sdk: ">=3.0.0 <4.0.0"
  flutter: ">=3.0.0"

dependencies:
  flutter:
    sdk: flutter
  flutter_riverpod: ^2.6.1
  riverpod_annotation: ^2.6.1

dev_dependencies:
  build_runner:
  custom_lint:
  riverpod_generator: ^2.6.4
  riverpod_lint: ^2.6.4

Hello world

lib/main.dart 最小限のコード。赤字がRiverpodに関連している部分。この例では状態の変更(Notifier)は使っていない


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

part 'main.g.dart';

// 単純データ(String)の状態。「ref」が必要
@riverpod
String helloWorld(Ref ref) {
  return 'Hello world';
}

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

class MyApp extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final String value = ref.watch(helloWorldProvider);  //ConsumerWidgetがref.watchしている

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Example')),
        body: Center(
          child: Text(value),
        ),
      ),
    );
  }
}

下記赤字のコマンドを実行して、ジェネレータを稼働させる。
(watchだと、ソース修正後にも自動でジェネレータが動くけど、ターミナルが使えなくなるから、下記を都度実行するのがおすすめです。)

flutter pub run build_runner build  --delete-conflicting-outputs

ジェネレータ(build_runner)がつける、NotifierとProviderの名前のルール

Notifier: _$S1Notifier (先頭に_$をつけ、大文字で始まる)

Provider: s1NotifierProviderとなる。(先頭が小文字になり、最後にProviderが追加される)

文字列リスト状態をRiverpodで扱っているサンプル。(要ジェネレータ)

YouTuber ルビーDOGさんのRiverpod完全解説動画のソースを参考に、2025年2月時点の最新版のflutterで動くよう修正しています。

s1.dart   単純変数の例

//s1.dart

import 'package:riverpod_annotation/riverpod_annotation.dart';
part 's1.g.dart';

@riverpod
class S1Notifier extends _$S1Notifier {
  @override
  int build() {
    // 最初のデータ
    return 0;
  }

  // データを変更する関数
  void updateState() {
    // 変更前のデータ
    final oldState = state;
    // 変更後のデータ
    final newState = oldState + 1;
    // データを上書き
    state = newState;
  }
}

my_widge1.dart

// my_widget1.dart

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

import 's1.dart';

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // S1 watch
    final s1 = ref.watch(s1NotifierProvider);
    // S1 listen
    ref.listen(
      s1NotifierProvider,
      (oldState, newState) {
        // スナックバーを表示
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(
            content: Text('S1データが変更されました'),
          ),
        );
      },
    );
    // S1 テキスト
    final s1Text = Text('$s1');
    // S1 ボタン
    final s1Button = ElevatedButton(
      onPressed: () {
        // S1 ノティファイアを呼ぶ
        final notifier = ref.read(s1NotifierProvider.notifier);
        // S1 データを変更
        notifier.updateState();
      },
      child: const Text('+1'),
    );

    // 縦に並べる
    return Column(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        s1Text,
        s1Button,
      ],
    );
  }
}

s2.dart リスト・配列の例

import 'package:riverpod_annotation/riverpod_annotation.dart';
part 's2.g.dart';

@riverpod
class S2Notifier extends _$S2Notifier {
  @override
  List<String> build() {
    // 最初のデータ
    return ['A', 'B', 'C', 'D'];
  }

  // データを変更する関数
  void updateState() {
    // 変更前のデータ
    final oldState = state;
    // 変更後のデータ
    final newState = [...oldState, 'X'];
    // データを上書き
    state = newState;
  }
}

my_widget2.dart

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

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {  // ref を作成

    // S2 watchの例
    final s2 = ref.watch(s2NotifierProvider);  // buildの中ではref.watch
    // ListView
    final listView = ListView.builder(
      itemCount: s2.length,
      itemBuilder: (_, index) {
        // index番目の文字
        final text = Text(s2[index]);
        return Card(child: text);
      },
    );

    //S2 listenの例
    ref.listen(s2NotifierProvider, (oldState, newState) {
      // 関数を呼び出す。ここではスナックバーに値の変化を表示する。
      ScaffoldMessenger.of(
        context,
      ).showSnackBar(SnackBar(content: Text('$oldState → $newState')));
    });

    // ボタン
    final button = FloatingActionButton(
      onPressed: () {
        // S2 Notifier
        final notifier = ref.read(s2NotifierProvider.notifier); //onPressedの中ではref.read
        // データを変更
        notifier.updateState();   //notifierの関数を呼んで、状態を変化させる
      },
      child: const Icon(Icons.add),
    );

    // 縦に並べる
    return Scaffold(floatingActionButton: button, body: listView);
  }
}

main.dart

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

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(
        body: Column(
          mainAxisAliment: MainAxisAliment.center,
          children:[
            MyWidget1(),
            MyWidget2(),
         ],
        ),
      ),
    );
  }
}

データ型毎のState表記例

// 単純データ
@riverpod
String example(ExampleRef ref) {
  return 'foo';
}

// 複雑データ(List、クラス)
@riverpod
class Example extends _$Example {
  @override
  String build() {
    return 'foo';
  }

  // Add methods to mutate the state
}

//Future 単純型
@riverpod
Future<String> example(ExampleRef ref) async {
  return Future.value('foo');
}


//Stream 単純型
@riverpod
Stream<String> example(ExampleRef ref) async* {
  yield 'foo';
}

//Future 複雑型
@riverpod
class Example extends _$Example {
  @override
  Future<String> build() async {
    return Future.value('foo');
  }

  // Add methods to mutate the state
}

//Stream 複雑型
@riverpod
class Example extends _$Example {
  @override
  Stream<String> build() async* {
    yield 'foo';
  }

  // Add methods to mutate the state
}

ref.watch, ref.read, ref.listenの違い

ref.watch は、値の変化に応じてウィジェットやプロバイダを更新する。基本的にwatich使う

ref.listen は、任意の関数を呼び出したい時に使用する

ref.watchメソッドとref.listenメソッドは、 ElevatedButton の onPressed 内など、非同期的な場面で呼び出してはならない。
また initState を始め、State のライフサイクルメソッド内での使用も避けなければならない。
これらの場合は代わりに 下記のref.readメソッドを使用する。

ref.readメソッドは、単純に値を参照するのみ。ref.read はリアクティブではないため、可能な限り使用を避ける。
(watch や listen の使用では問題が生じる場合の回避策として存在している。)
ほとんどの場面では watch や listen の使用、特に watch の使用がベター。

AsyncValueについて

Future,Stream型では、データが存在しなかったり、エラーとなる場合が発生する。下記のように使用する。
具体的にはs3,s4のソースを参照。(省略。ルビーDOGさんのYouTubeでダウンロードできる)

    final s3 = ref.watch(s3NotifierProvider);
    // S3 AsyncValue
    final s3Text = s3.when(
      loading: () => const Text('準備中...'),
      error: (e, s) => Text('エラー $e'),    // e:エラーの種類、s:エラーが発生した場所
      data: (d) => Text(d),
    );

応用編

select

selectは、stateの一部のみwatchするような場合に使用する。以下はルビーDOGさんのサンプルを修正したものです。

shikoku.dart

// freezed 使えばもっと短く書ける

// 四国
class Shikoku {
  const Shikoku({
    required this.kagawa,
    required this.tokushima,
    required this.kochi,
    required this.ehime,
  });

  // 香川
  final int kagawa;
  // 徳島
  final int tokushima;
  // 高知
  final int kochi;
  // 愛媛
  final int ehime;

  /// copyWith
  Shikoku copyWith({
    int? kagawa,
    int? tokushima,
    int? kochi,
    int? ehime,
  }) {
    return Shikoku(
      kagawa: kagawa ?? this.kagawa,
      tokushima: tokushima ?? this.tokushima,
      kochi: kochi ?? this.kochi,
      ehime: ehime ?? this.ehime,
    );
  }

  /// ==
  @override
  bool operator ==(Object other) {
    return other is Shikoku &&
        other.runtimeType == runtimeType &&
        other.kagawa == kagawa &&
        other.tokushima == tokushima &&
        other.kochi == kochi &&
        other.ehime == ehime;
  }

  /// hashCode
  @override
  int get hashCode {
    return Object.hash(
      runtimeType,
      kagawa,
      tokushima,
      kochi,
      ehime,
    );
  }
}

state.dart (ジェネレータが必要)

import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:shikoku.dart';
part 'state.g.dart';

@riverpod
class ShikokuNotifier extends _$ShikokuNotifier {
  @override
  Shikoku build() {
    // 人口
    return const Shikoku(
      kagawa: 93,
      tokushima: 70,
      kochi: 69,
      ehime: 130,
    );
  }

  void updateKagawa() {
    final oldState = state;
    final newState = oldState.copyWith(
      kagawa: oldState.kagawa + 1,
    );
    state = newState;
  }

  void updateTokushima() {
    final oldState = state;
    final newState = oldState.copyWith(
      tokushima: oldState.tokushima + 1,
    );
    state = newState;
  }

  void updateKochi() {
    final oldState = state;
    final newState = oldState.copyWith(
      kochi: oldState.kochi + 1,
    );
    state = newState;
  }

  void updateEhime() {
    final oldState = state;
    final newState = oldState.copyWith(
      ehime: oldState.ehime + 1,
    );
    state = newState;
  }
}

widget.dart

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

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 四国の状態を watch
    final shikoku = ref.watch(shikokuNotifierProvider);

    // 香川だけを listen (select)
    ref.listen(
      shikokuNotifierProvider.select(
        (shikoku) => shikoku.kagawa,
      ),
      (oldState, newState) {
        // スナックバーを表示する
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('$oldState から $newState へ変化しました'),
          ),
        );
      },
    );

    // 人口たち
    final popurations = Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        Text('${shikoku.kagawa}'),
        Text('${shikoku.tokushima}'),
        Text('${shikoku.kochi}'),
        Text('${shikoku.ehime}'),
      ],
    );

    // ボタンたち
    final buttons = Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        ElevatedButton(
          onPressed: () {
            final notifier = ref.read(shikokuNotifierProvider.notifier);
            notifier.updateKagawa();
          },
          child: const Text('香川'),
        ),
        ElevatedButton(
          onPressed: () {
            final notifier = ref.read(shikokuNotifierProvider.notifier);
            notifier.updateTokushima();
          },
          child: const Text('徳島'),
        ),
        ElevatedButton(
          onPressed: () {
            final notifier = ref.read(shikokuNotifierProvider.notifier);
            notifier.updateKochi();
          },
          child: const Text('高知'),
        ),
        ElevatedButton(
          onPressed: () {
            final notifier = ref.read(shikokuNotifierProvider.notifier);
            notifier.updateEhime();
          },
          child: const Text('愛媛'),
        ),
      ],
    );

    return Column(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        // 人口たち
        popurations,
        // ボタンたち
        buttons,
      ],
    );
  }
}

main.dart

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

void main() {
  const app = MayApp();
  const scope = ProviderScope(child: app);
  runApp(scope);
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

buildとonDispose

最初のwatch時にbuildが動き、最初の状態が確定する。誰も状態をwatchしなくなったら、onDisposeが動く。(buildの中に記載する)
Riverpod2 では、基本的にautoDisposeとなっており、データを破棄されたくなければ、次のkeepAliveを指定する必要がある。
(ソースは省略)

keepAlive

データを捨てられなくする。@Riverpod ( keepAlive : true ) 最初が大文字のRなのがポイント。
(Riverpod v1ではkeepAliveがデフォルトだったが、v2からは onDisposeがデフォルト)

state.dart (ジェネレータが必要)

import 'package:flutter/widgets.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'state.g.dart';

//@riverpod    //v2ではonDisposeがデフォルト。
@Riverpod(keepAlive: true)        // v1と同じ動き。onDisposeが働かなくなる
class CountNotifier extends _$CountNotifier {
  @override
  int build() {
    // 最初のデータを準備する
    debugPrint('誰かにwatchされたのでデータを準備します');

    // データが捨てられた時のことを決めておく
    ref.onDispose(() {
      debugPrint('誰にもwatchされなくなったのでデータを捨てます');
    });

    return 0;
  }

  // データを変更する関数
  void updateState() {
    final oldState = state;
    final newState = oldState + 1;
    state = newState;
  }
}

main.dart (ルビーDOGさんのソースをかなり修正しています。赤字部分が修正箇所)

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      initialRoute: '/1',
      routes: {
        '/1': (context) => const Page1(),
        '/2': (context) => const Page2(),
        '/3': (context) => const Page3(),
      },
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.red,
        title: const Text('1'),
        actions: [
          IconButton(
            icon: const Icon(Icons.arrow_forward_ios),
            onPressed: () {
              Navigator.of(context).pushNamed('/2');
            },
          ),
        ],
      ),
      body: const Center(),
    );
  }
}

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final int count = ref.watch(countNotifierProvider);

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.green,
        title: const Text('2'),
        actions: [
          IconButton(
            icon: const Icon(Icons.arrow_forward_ios),
            onPressed: () {
              Navigator.of(context).pushNamed('/3');
            },
          ),
        ],
      ),
      body: Center(child: Text('${count}')),
    );
  }
}


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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final int count = ref.watch(countNotifierProvider);

    return Scaffold(
      appBar: AppBar(backgroundColor: Colors.blue, title: const Text('3')),
      body: Center(child: Text('${count}')),

      floatingActionButton: FloatingActionButton(
        onPressed: () {
          final notifier = ref.read(countNotifierProvider.notifier);
          notifier.updateState();
        },
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

Basic Provider

Basic Provider (単にProviderと表記) Notifierが存在しないProvider。
refを使って、Notifierをref.readして他のNotifireProviderのNotifierを呼び出せる。
ConsumerWegetの原型でもある。

-proxy

複数のStateを監視して1つのデータに纏める時に便利。例えば、s1,s2,s3の3つのNotifierProviderが持つstateを同時に監視して、s1+s2+s3を返すようなことができる。

-logic

NotifierProviderのデータを監視して、ある条件が満たされた場合に、stateが変化する。重たい処理をするwatchが、stateが変化するたびに、無駄に動作しないようにできるメリットがある。

-cache

データをキープして再計算を避けるために準備されたプロバイダ。

ProviderFamily

似たようなプロバイダを複数準備するのではなく、IDを与えることで、1つの記述で内部動作を変え、あたかも別のプロバイダがあるように見せる仕組み。

family.dart (ジェネレータが必要)

import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'family.g.dart';

@riverpod
int family(FamilyRef ref, String id) {
  if (id == '日本') {
    return 3;
  }
  if (id == 'アメリカ') {
    return 2;
  }
  return 0;
}

widget.dart

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

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final japan = ref.watch(familyProvider('日本'));
    final usa = ref.watch(familyProvider('アメリカ'));

    // WBC優勝おめでとう!
    final wbcScore = Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        Text('$japan'),
        Text('$usa'),
      ],
    );

    return wbcScore;
  }
}

main.dart

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

void main() {
  const app = MayApp();
  const scope = ProviderScope(child: app);
  runApp(scope);
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

ProviderScope

デバッグ等で、複数のProviderScopeを活用することができる。普通はMyAppをProvideScopeで囲うが、実はPage単位でも囲える。別のスコープになる。以下のサンプルソースではflutter_hooksが必要。ターミナルで下記コマンドを実行すると、pubspec.yamlに追記される

flutter pub add flutter_hooks

state.dart  (ジェネレータが必要)

import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'state.g.dart';

@riverpod
class TestNotifier extends _$TestNotifier {
  @override
  int build() {
    return 50;
  }

  void plus() {
    final oldState = state;
    final newState = oldState + 1;
    state = newState;
  }

  void minus() {
    final oldState = state;
    final newState = oldState - 1;
    state = newState;
  }
}

main.dart

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

main() {
  const app = MyApp(
    pages: [
      // プロバイダースコープ
      ProviderScope(child: PageA()),

      // プロバイダースコープ
      ProviderScope(child: PageB()),
    ],
  );
  runApp(app);
}

// アプリ
class MyApp extends HookWidget {
  const MyApp({
    super.key,
    required this.pages,
  });

  final List<Widget> pages;

  @override
  Widget build(BuildContext context) {
    final index = useState(0);

    // タブのアイテムたち
    const items = [
      BottomNavigationBarItem(
        icon: Icon(Icons.circle),
        label: 'A',
      ),
      BottomNavigationBarItem(
        icon: Icon(Icons.circle),
        label: 'B',
      ),
    ];

    // タブバー
    final bar = BottomNavigationBar(
      items: items,
      backgroundColor: Colors.orange,
      selectedItemColor: Colors.black,
      unselectedItemColor: Colors.white,
      currentIndex: index.value,
      onTap: (i) => index.value = i,
    );

    return MaterialApp(
      home: Scaffold(
        // プロバイダースコープがWidgetTreeから消えないようにするIndexedStack
        body: IndexedStack(
          index: index.value,
          children: pages,
        ),
        bottomNavigationBar: bar,
      ),
    );
  }
}

// 画面 A
class PageA extends ConsumerWidget {
  const PageA({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // watch
    final test = ref.watch(testNotifierProvider);
    // ボタン
    final button = FloatingActionButton(
      backgroundColor: Colors.orange,
      onPressed: () {
        ref.read(testNotifierProvider.notifier).minus();
      },
      child: const Icon(Icons.remove),
    );
    // 画面
    return Scaffold(
      floatingActionButton: button,
      body: Center(
        child: Text('$test'),
      ),
    );
  }
}

// 画面 B
class PageB extends ConsumerWidget {
  const PageB({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // watch
    final test = ref.watch(testNotifierProvider);
    // ボタン
    final button = FloatingActionButton(
      backgroundColor: Colors.orange,
      onPressed: () {
        ref.read(testNotifierProvider.notifier).plus();
      },
      child: const Icon(Icons.add),
    );
    // 画面
    return Scaffold(
      floatingActionButton: button,
      body: Center(
        child: Text('$test'),
      ),
    );
  }
}

overrideWith

NotifierProviderをフェイクのプロバイダに差し替えて、デバッグ等で使える。ProvideScope内で定義する。

final scope = ProvideScope(
    overrides: [

        originalProvider.overrideWith((ref){
            return 'テストデータ';
        }),
    ],
);

サンプルソース(動作確認済)

state.dart

import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'state.g.dart';

// 本物

@riverpod
String apple(AppleRef ref) {
  return 'りんご';
}

widget.dart

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

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // りんご ?
    final apple = ref.watch(appleProvider);

    return Text(apple);
  }
}

main.dart

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

void main() {
  const app = MyApp();

  final scope = ProviderScope(
    overrides: [
      // ここに偽物のデータを使いたいプロバイダーたち

      appleProvider.overrideWith((ref) {
        return '毒りんご';
      }),
    ],
    child: app,
  );
  runApp(scope);
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

-Flutter Dart Unity アプリ開発