Why Riverpod? Riverpod 101

Managing state

Prop drilling (プロップ・ドリリング)

Prop drilling(プロップ・ドリリング)とは、Reactなどのコンポーネントベースのフレームワークで、ある親コンポーネントから子コンポーネント、さらにその子コンポーネントへと、props(プロパティ)を何段階にもわたって渡していく状態を指します。

この概念は、Reactに限らずFlutterのような他のUIフレームワークでも広く見られます。Flutterでは、親ウィジェットから子ウィジェットへとデータやコールバック関数を渡していく形で同様の構造が現れます。多くの階層を通じてデータを伝搬させる必要がある場合、コードの可読性や保守性が低下しやすくなるため、適切な状態管理手法(例:ReactではContext APIやRedux、FlutterではProviderやRiverpodなど)を導入することが推奨されます。

Prop drilling

An example

Parent Widget(親)

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
	/// variable declaration
  bool isSoundEnabled = true;

	/// function declaration
  void updateSound(bool value) {
    setState(() {
      isSoundEnabled = value;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: TopScreen(
	      /// parameters
        isSoundEnabled: isSoundEnabled,
        updateSound: updateSound,
      ),
    );
  }
}

Child Widget (子ども)

class TopScreen extends StatelessWidget {
  final bool isSoundEnabled;
  final Function(bool) updateSound;

  TopScreen({required this.isSoundEnabled, required this.updateSound});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Sound Settings')),
      body: Center(
        child: Switch(
          value: isSoundEnabled,
          onChanged: (value) {
            updateSound(value);
          },
        ),
      ),
    );
  }
}

Why Riverpod?

Managing state outside of a Widget’s lifestyle

Riverpodを使うと、アプリ全体で使いたいデータ(=状態)をWidgetの
外側で管理(=グローバル管理)できます。

たとえば「音をオン・オフする設定」や「ログインしているユーザー情報」などをどの画面からでも簡単に使えるようになります。

通常、状態をあるWidgetで定義すると、そのWidget内でしか使えません。

でもRiverpodを使えば…

✅ 他の画面(Widget)からも呼び出せる
✅ データの変更もどこからでもできる
✅ データが変われば、使っているWidgetが自動で再ビルドされる

Why Riverpod?

Managing state outside of a Widget’s lifestyle

/// type = bool
final soundEffectsProvider = NotifierProvider.autoDispose<SoundEffectsProvider, bool>(
	
	SoundEffectsProvider.new,
);

class SoundEffectsProvider extends AutoDisposeNotifier<bool> {
	@override
	/// initial value
	bool build() {
		return true;
	}
	
	/// state function
	void updateSound(value) {
		state = value;
	}
}
final isSoundEnabled = ref.watch(soundEffectsProvider);
final isSoundEnabled = ref.watch(soundEffectsProvider);
ref.read(soundEffectsProvider.notifer).updateSound()
final wordleViewControllerProvider = Provider<WordleViewController>(
	/// ref object
	(ref) => WordleViewController(ref),
);

class WordleViewController {
	final Ref _ref;
	
	/// access ref object
	WordleViewController(this._ref);
	
	/// using ref object to access other providers
	KeyboardProvider get _keyboardState => _ref.read(keyboardProvider.notifier);
	WordleProvider get _wordleState => _ref.read(wordleProvider.notifier);
	
	SoundEffectsService get _soundEffectsService => _ref.read(soundEffectsServiceProvider);
}

RiverpodのProviderは、refオブジェクトを通じて他のプロバイダ(Provider)への参照や依存関係の取得を行うことができます。refは、プロバイダのコールバック関数(例:Provider, FutureProvider, StateNotifierProviderなど)に渡されるオブジェクトであり、他のプロバイダをref.watch()やref.read()などのメソッドを使って参照するために使用されます。

Conclusion

FeatureProp DrillingRiverpod
✅️ Simple to Use✔ Very simple for small apps❌️ Slightly more setup
🧠 Easy to Understand✔Clear where state comes from❌️ Indirection(you read from providers)
📐 Scalablility❌️ Becomes messy in deep trees✔ Scales cleanly across large apps
🔁 Reusability❌️ Hard to reuse logic cleany✔ Providers can be reused anywhre
📚️ Testability❌️ UI and logic tightly coupled✔ Easily test logic in isolation
📥️ Boilerplate✔ Less boilerplate upfront❌️ More boilerplate for small use cases
📦️ Global Accese❌️ Must pass props everywhere✔ Access state from any widget
🧩 Composition❌️ Harder to compose nested logic✔ Providers can depend on providers
🧪 Debuggin✔ Straightforward(usually)❌️ Slightly more abstract
特徴Prop DrillingRiverpod
✅️ シンプルさ✔ 小規模なアプリではとても簡単❌️ 最初のセットアップが少し必要
🧠 理解のしやすさ✔状態の流れが明確❌️ Provider経由で少し抽象的
📐 スケーラビリティ❌️ ツリーが深くなると煩雑に✔ Providerはどこでも再利用可能
🔁 再利用性❌️ ロジックの再利用が難しい✔ Providers can be reused anywhre
📚️ テストのしやすさ❌️ UIとロジックが密結合✔ ロジックを個別にテストしやすい
📥️ ボイラープレート✔ 初期の記述量が少ない❌️ 小規模な用途だとやや多め
📦️ グローバルアクセス❌️ 毎回手動で状態を渡す必要あり✔ どのウィジェットからでもアクセス可能
🧩 ロジックの構成❌️ ネストしたロジックの構造が難しい✔ Provider同士で依存関係を持てる
🧪 デバッグ✔ 状態の流れがわかりやすい❌️ 少し抽象度が高くなることも