カウンターアプリを作りながらflutter_riverpodについてカウンターアプリを作成しながら、ConsumerWidget, Providerについて紹介していきます。
その中でモデル(model)とビュー(view)を分けることにより、コードリーディングを上げるための考え方についても触れていきます。
最終的な成果物
環境
Flutter 3.10.2
Dart 3.0.2
flutter_riverpodとは
Flutter専用の状態管理パッケージです。
状態管理ってなんか難しい言葉ですが、
つまりflutter_riverpodを使ってどこのWidgetからでもアクセスできるグローバルな変数を
作っちゃおう!ということです
flutter_riverpodを使った状態管理をすることで他のファイルからも、それらにアクセスすることができます。
正直、flutter_riverpodがなくてもアプリを作ることはできます。(ただ、神クラスを作りかねない)
flutter_riverpodがあることで開発のコードリーディングを向上させたりすることができます。
アプリ自体が大きくなることで状態とビュー側を分けることにより管理がしやすくなるということです。
実装方針
- flutter_riverpodをインストールする
- ConsumerWidget, ProviderScopeを実装
- Providerを使ってロジックを分離する
- カウントできるボタンの実装
flutter_riverpodをインストールする
以下のコマンドをプロジェクトのルートディレクトリにて実行しましょう。
flutter pub add flutter_riverpod
ConsumerWidget, ProviderScopeの実装
ProviderScopeをMyAppにラップし、状態管理した値を利用するウィジェットにてConsumerWidgetをextendsしています。
また、ConsumerWidgetのbuildメソッドをオーバーライドする際にWidgetRefという引数を追加します。
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(
ProviderScope(
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: Home(),
);
}
}
class Home extends ConsumerWidget {
const Home({
super.key,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
body: SafeArea(
child: Text("hello"),
),
);
}
}
ChangeNotifierProviderを使ってロジックの分離を行う
カウンター部分を実装していくに当たってロジックの分離をおこなっていきます。
ChangeNotifierProviderとは別の場所に保管している状態管理変数を
Widget側で見れるようにするパイプみたいな役割を持っています。
今回は以下の2つで分けて実装します。。
ファイル名 | 目的 |
model | ロジック系の処理を集める |
view | 画面系の処理を集める |
また、それらはhomeというディレクトリの中に作成することとします。
// ディレクトリのイメージ
lib -- main.dart
|- home -- model.dart
|- view.dart
わざわざhomeディレクトリの中にview, modelを作成する理由はコードリーディングを向上するため
ですが、詳細は後述します
main.dart
import 'package:tmp_blog/home/view.dart';
...
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: Home(),
);
}
}
home/view.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:tmp_blog/home/model.dart';
class Home extends ConsumerWidget {
const Home({
super.key,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final CounterModel counterModel = ref.watch(homeProvider);
return Scaffold(
body: SafeArea(
child: Text(counterModel.count.toString()),
),
);
}
}
home/model.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart';
final homeProvider = ChangeNotifierProvider((ref) => HomeModel());
class CounterModel extends ChangeNotifier {
int count = 10;
}
Widget内で変数を管理することも可能ですが、大きなアプリになってくると、一つのWidgetで書いていくには誰も読めないコードを作ってしまうことになりかねません。
そのためコードリーディングを向上させる目的でロジックの分離を行います。
単一責任の原則といって1つのクラスには1種類の処理のみを任せるべきというコードリーディングの考えに則ったものです。
今回作るカウンターアプリ自体はHome(いわば初期の画面)のみで実装しますが、
例えば以下の例も追加で実装するとなると単一責任はどうなるでしょうか?
作成したい機能 | ディレクトリ名 |
過去のカウントの履歴を見る | CounterHistory |
アプリのテーマスタイルを変更する | Theme |
ディレクトリを作らずに実装していた場合はmodel.dart, view.dartの中に
Home, CounterHistory, Themeを記述することになり、
何がどこに書いてあるかが、他の人には分からなくなってしまいます。
それを解析するのに無駄なコードリーディングの時間が費やされてしまいます。
model, viewに実装されているフォーマットはスニペットにしておくと、今後楽ですね!
カウントをプラス・マイナスできるボタンを追加
最後にカウンターアプリっぽく体裁を整えていきます。
ElevateButtonウィジェットを使ってボタンを配置しました。
view.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:tmp_blog/home/model.dart';
class Home extends ConsumerWidget {
const Home({
super.key,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final CounterModel counterModel = ref.watch(homeProvider);
return Scaffold(
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
counterModel.count.toString(),
style: TextStyle(
fontSize: 40,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
counterModel.plus();
},
child: Text("+"),
),
ElevatedButton(
onPressed: () {
counterModel.minus();
},
child: Text("-"),
),
],
)
],
),
),
),
);
}
}
countをプラス・マイナスするメソッドを作成し、
notifyListeners()にてcountの変更結果をProviderを通して伝えるようにしました。
notifyListeners()がないと変更を検知できず、ボタンを押下しても何も反応しません。
model.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart';
final homeProvider = ChangeNotifierProvider((ref) => CounterModel());
class CounterModel extends ChangeNotifier {
int count = 10;
void plus() {
count = count + 1;
notifyListeners();
}
void minus() {
count = count - 1;
notifyListeners();
}
}
まとめ
ここまで読んでいただきありがとうございます
今回はflutter_riverpodを使って状態管理を実装しました。
その中でコードリーディングについても触れました。
そのまま実装に使うことは難しいものの、今後のコード記述の考え方に一役買えたら幸いです。
コメント