Skip to content

Instantly share code, notes, and snippets.

@DaisukeNagata
Created July 23, 2023 04:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DaisukeNagata/b99ac85d9cdb1b880d9c422f52ee305e to your computer and use it in GitHub Desktop.
Save DaisukeNagata/b99ac85d9cdb1b880d9c422f52ee305e to your computer and use it in GitHub Desktop.
Riverbod and FlutterHooks example
import 'package:flutter/material.dart';
/*
ここではflutterとflutter_riverpodをインポートしています。
flutter_riverpod: ^2.3.6を使用してます。
*/
import 'package:flutter_riverpod/flutter_riverpod.dart';
/*
flutter_hooksはFlutterのパッケージで、より簡単にウィジェットの状態のライフサイクルを管理することを助けます。新しい種類のウィジェットであるHookWidgetを導入しており、
これはStatefulWidgetやStatelessWidgetの代わりに使い、より良い状態管理を行うことができます。
flutter_hooksパッケージには、ウィジェットで使用できるいくつかの異なる種類のフックがあります:
useState:ウィジェットがツリーから削除されるまでウィジェットの再構築を超えて維持される状態をウィジェットに作成できるフックです。
useEffect:ウィジェットが最初に構築されたときや特定の値が変更されたときに副作用(タイマーの開始、ネットワークリクエストの行など)を実行できるフックです。
useMemoized:計算にコストがかかる値を作成し、ウィジェットが再構築されるたびに再計算を避けたい場合に使用できるフックです。
useRef:ウィジェットの再構築を超えて維持されるミュータブルな参照オブジェクトを作成できるフックです。
useContext:現在のウィジェットに関連付けられたBuildContextに簡単にアクセスできるフックです。
flutter_hooksを使用する利点はたくさんあります:
必要なボイラープレートコードの量を減らすことができます。
コードを読みやすく、理解しやすくします。
状態のロジックを簡単に共有して再利用することができます。
*/
import 'package:flutter_hooks/flutter_hooks.dart';
/*
counterProviderは、RiverpodパッケージのStateNotifierProviderクラスを使用して定義されたプロバイダーです。
StateNotifierProviderは、StateNotifier(この場合、CounterNotifier)を作成し、そのStateNotifierの状態(この場合、CounterState)を提供するためのプロバイダーです。
プロバイダーとは、アプリケーション全体で利用できるように値またはオブジェクトを提供するものであり、これによりアプリケーションの状態が一元管理されます。
特定の値を提供するプロバイダーは、その値が必要なすべてのウィジェットからアクセスできます。
上記のcounterProviderでは、カッコ内の関数(プロバイダーの作成ロジックを含む)がCounterNotifierの新しいインスタンスを返しています。
これにより、このCounterNotifierがアプリケーション全体で利用できるようになります。
つまり、counterProviderは、CounterNotifierのインスタンスを作成し、それを通じてCounterStateを提供します。
これにより、アプリケーション全体でカウンターの状態が共有され、管理されます。
*/
final counterProvider =
StateNotifierProvider<CounterNotifier, CounterState>((ref) {
return CounterNotifier();
});
/*
CounterStateクラスは、カウンターの現在の状態を保持するための単純なデータクラスです。このクラスには2つのフィールドがあります。
count: これは現在のカウンターの値を保持します。ユーザーがアプリケーションで「+」ボタンを押すたびに、この値が増加します。
isLoading: これはアプリケーションが「+」ボタンを押した後のカウント増加処理が実行中かどうかを示すフラグです。
trueの場合、カウンターの増加が遅延処理中であることを示し、アプリケーションはローディングインジケータ(通常はスピニングウィールまたはプログレスバー)を表示します。
コンストラクタは、CounterStateクラスのインスタンスを作成するために使用します。これはcountとisLoadingの両方の初期値を引数として受け取ります。
このCounterStateクラスは、アプリケーションのカウンターの状態を表現するための単純で効率的な方法を提供します。
これにより、状態管理ライブラリ(このケースではriverpod)を通じて、アプリケーション全体で状態を簡単に共有できます。
*/
class CounterState {
int count;
bool isLoading;
CounterState(this.count, this.isLoading);
}
class CounterNotifier extends StateNotifier<CounterState> {
CounterNotifier() : super(CounterState(0, false));
void increment() {
state = CounterState(state.count + 1, false);
}
Future<void> incrementAfterDelay() async {
state = CounterState(state.count, true);
await Future.delayed(const Duration(seconds: 3));
increment();
}
}
/*
アプリケーションのエントリーポイントです。
ここではProviderScopeというRiverpodのウィジェットを使ってCounterAppをラップしています。
ProviderScopeはRiverpodのすべてのプロバイダーがアプリケーション内で利用できるようにするためのものです。
*/
void main() => runApp(const ProviderScope(child: CounterApp()));
/*
CounterAppクラスはアプリケーション全体を表すウィジェットです。
buildメソッドでMaterialAppを返し、そのホームページにCounterPageを設定しています。
*/
class CounterApp extends StatelessWidget {
const CounterApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: CounterPage(),
);
}
}
/*
CounterPageクラスはConsumerWidgetを継承しています。
プロバイダーが提供する状態を読み取り、ウィジェットの描画に反映することができます。
buildメソッド内で、WidgetRef refを通じてcounterProviderから状態を読み取り、その結果をstate変数に格納しています。
次にScaffoldウィジェットを使ってアプリの基本的なレイアウトを定義しています。
Scaffoldはアプリケーションの基本的なビジュアルレイアウトを提供するウィジェットで、アプリケーションバー(AppBar)、ボディ部分、フローティングアクションボタン(FloatingActionButton)などが含まれます。
AppBarのタイトルには固定のテキスト"Counter"を設定しています。
body部分では、state.isLoadingがtrueのときにはCircularProgressIndicatorを表示し(これはローディング中を表します)、falseのときにはTextウィジェットを用いて現在のカウント数を表示します。
この部分が状態の変更に応じて自動的に再描画されます。
floatingActionButtonでは、ボタンが押されたときにincrementAfterDelayメソッドを呼び出すように設定しています。
このメソッドはcounterProviderから提供されるCounterNotifierのメソッドです。これにより、ボタンが押されるとカウンターの値が遅延して増加します。
*/
class CounterPage extends ConsumerWidget {
const CounterPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(counterProvider);
return Scaffold(
appBar: AppBar(
title: GestureDetector(
child: const Text('CounterPage'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondCounterPage(ref: ref)),
);
},
),
),
body: Center(
child: state.isLoading
? const CircularProgressIndicator()
: Text('Count: ${state.count}'),
),
floatingActionButton: FloatingActionButton(
onPressed: () =>
ref.read(counterProvider.notifier).incrementAfterDelay(),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
/*
SecondCounterPageは、Flutter Hooksパッケージを使って実装されたウィジェットです。
このウィジェットは、アプリ内でカウンターの状態を表示し、ボタンを押すことでカウンターを非同期に増やすページを表します。
コンストラクタでrefという名前のWidgetRefを受け取ります。WidgetRefはRiverpodのProviderへのアクセスを提供するためのものです。
これを通じて、RiverpodのプロバイダーであるcounterProviderから状態を読み取ります。
incrementCounterメソッドが定義されています。このメソッドは非同期処理を行い、カウンターを非同期に増やすために使われます。
isLoadingというuseStateフックを使ってローカルな状態を定義しています。
このローカルな状態は、非同期処理中のローディング状態を管理するために使用されます。
incrementCounterメソッド内では、isLoading.valueにtrueを設定してローディング状態を開始します。
次に、await Future.delayed(const Duration(seconds: 3))によって3秒間待機します。
この部分が非同期処理であり、その間に他の操作が行われてもカウンターの増加は実行されません。
その後、ref.read(counterProvider.notifier).increment()を使って、RiverpodのcounterProviderから提供されるCounterNotifierのincrement()メソッドを呼び出してカウンターを増加させます。
再度isLoading.value = falseとしてローディング状態を終了します。
Scaffoldウィジェットを使ってページの基本的なレイアウトを定義しています。ローディング中はCircularProgressIndicatorが表示され、カウント数はテキストで表示されます。
フローティングアクションボタンを押すことでincrementCounterメソッドが呼び出され、カウンターが非同期に増加します
*/
class SecondCounterPage extends HookWidget {
const SecondCounterPage({super.key, required this.ref});
final WidgetRef ref;
@override
Widget build(BuildContext context) {
final counter = ref.watch(counterProvider);
final isLoading = useState(false);
void incrementCounter() async {
isLoading.value = true;
await Future.delayed(const Duration(seconds: 3));
ref.read(counterProvider.notifier).increment();
isLoading.value = false;
}
return Scaffold(
appBar: AppBar(
title: const Text('SecondCounterPage'),
),
body: Center(
child: isLoading.value
? const CircularProgressIndicator()
: Text('Count: ${counter.count}'),
),
floatingActionButton: FloatingActionButton(
onPressed: incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
@DaisukeNagata
Copy link
Author

2023-07-23.13.40.50.mov

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment