概要
やりたいこと
hooks_riverpod の公式 Example をコードリーディングし、機能ごとにファイル分割を行う。
題材となるプログラムは下記を参考にする。
用語
Riverpod
大したこと書いてないが前回参照。
hooks_riverpod
Riverpod のプロバイダを hooks 風に記述量を少なく書ける。
StateNotifier
StateNotifier は一つの immutable な状態を格納する監視可能なクラス。
Riverpod が依存する state_notifier パッケージのクラスであり、後述する StateNotifierProvider とセットで利用される。
今回は StateNotifier を継承した Counter
クラスを作成し、 increment
メソッドでカウント部分を定義している。
この Counter
は StateNotifierProvider に渡されることになる。
class Counter extends StateNotifier<int> {
Counter() : super(0);
void increment() => state++;
}
StateNotifierProvide
StateNotifierProvider は StateNotifier を監視し、公開するためのプロバイダ。
今回は StateNotifier である Counter
を値に持つ StateNotifierProvider を作成する。
final counterProvider = StateNotifierProvider<Counter, int>((_) => Counter());
HookConsumerWidget
HookConsumerWidget は ConsumerWidget と HookWidget の役割を併せ持つ Widget。
今回は MyHomePage メソッド内で ref
オブジェクトを使用するため HookConsumerWidget から継承した。
counterProvider
の監視 ( watch )FloatingActionButton
をクリックした際の値の取得とincrement
メソッド呼び出し
class MyHomePage extends HookConsumerWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Riverpod counter example'),
),
body: Center(
child: Text(
'$count',
style: Theme.of(context).textTheme.headline4,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => ref.read(counterProvider.notifier).increment(),
child: const Icon(Icons.add),
),
);
}
}
また、build メソッド内でフック (useState) を利用できるようになるが、今回は未使用なので割愛。
チュートリアル
hooks_riverpod 公式サンプルを動かす
以下の手順でプロジェクトを作成し、hooks_riverpod を追加。
flutter create hooks_riverpod_tutorial cd hooks_riverpod_tutorial flutter pub add hooks_riverpod
main.dart を公式 Example に変更。
vi lib/main.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
void main() {
runApp(
const ProviderScope(
child: MyApp(),
),
);
}
final counterProvider = StateNotifierProvider<Counter, int>((_) => Counter());
class Counter extends StateNotifier<int> {
Counter() : super(0);
void increment() => state++;
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends HookConsumerWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Riverpod counter example'),
),
body: Center(
child: Text(
'$count',
style: Theme.of(context).textTheme.headline4,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => ref.read(counterProvider.notifier).increment(),
child: const Icon(Icons.add),
),
);
}
}
アプリを起動すると、いつものカウンターアプリできあがる。
flutter run
機能ごとにファイル分割
以下 4 つに分割。
- main.dart
- app.dart
- widget.dart
- provider.dart
main.dart
Root を ProviderScope で囲むところは flutter_riverpod と同じ。
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'app.dart';
void main() {
runApp(
const ProviderScope(
child: MyApp(),
),
);
}
app.dart
MaterialApp を構築する部分。MyHomePage クラスを呼び出す。
import 'package:flutter/material.dart';
import 'view.dart';
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
widget.dart
ここでの役割は HookConsumerWidget による画面描画。いわゆる View 層として分割。
コードの内容は上で解説したまま。
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'provider.dart';
class MyHomePage extends HookConsumerWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Riverpod counter example'),
),
body: Center(
child: Text(
'$count',
style: Theme.of(context).textTheme.headline4,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => ref.read(counterProvider.notifier).increment(),
child: const Icon(Icons.add),
),
);
}
}
provider.dart
ここでは Provider による状態の保持とカウントする操作をまとめた。いわゆる Model 層。
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
final counterProvider = StateNotifierProvider<Counter, int>((_) => Counter());
class Counter extends StateNotifier<int> {
Counter() : super(0);
void increment() => state++;
}
まとめ
flutter_riverpod と hooks_riverpod の書き方の違いを学べた。個人的には hooks_riverpod の書き方が好き。
コードは Github にまとめた。
コメント