🐕
RiverpodでViewModelを作ってみた
概要
RiverpodでViewModelを作成する際に、情報が古いものもあり、ある程度理解するまでに
時間がかかったので記事にまとめました。
網羅的に解説するものではなく、とりあえずRiverpodを使ってMVVMの書き方の基本を知りたい方向けです。
自分と同じようにFlutter始めたばかりの方の参考になれば幸いです。
サンプルのクラス名等を変えれば、使えるようになってます。
サンプルコード
State
ViewModelで持ちたい状態を定義し、freezedでprofile_state.freezed.dartを自動生成します。
Stateのプロパティはご自由に差し替えてください。
- freezed_annotation.dartをimport
- part 'xxxx.freezed.dart';を記述。(xxxはstateのファイル名)
- Stateのclassに@freezedをつける
-
flutter pub run build_runner build --delete-conflicting-outputs
コマンドでxxxx.freezed.dartを生成。
import 'package:freezed_annotation/freezed_annotation.dart';
part 'profile_state.freezed.dart';
class ProfileState with _$ProfileState {
const factory ProfileState({
('') String name,
(0) int age,
(false) bool isInit,
String? description,
}) = _ProfileState;
}
ViewModel
状態の変更をしたいので、classでProviderを生成します。
生成されるProviderはViewModel名+Providerとなります。(ex. profileViewModelProvider)
buildをoverrideして先ほど作成したStateを返すようにします。
- riverpod_annotation.dartをimport
- part 'xxx.g.dart';を記述(xxxはViewModelのファイル名)
- classに@riverpodをつける
-
flutter pub run build_runner build --delete-conflicting-outputs
コマンドでxxxx.g.dartを生成。 - 必要に応じてViewModelにメソッドを定義。
import 'package:flutter_basic/mvvm/profile/profile_state.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'profile_view_model.g.dart';
final class ProfileViewModel extends _$ProfileViewModel {
ProfileState build() {
return const ProfileState();
}
void isInitToggle() {
state = state.copyWith(
isInit: !state.isInit
);
}
}
View
ViewModelを使用する箇所をConsumerで囲う。
ViewModelの状態は、ref.watch(provider名).
でアクセスできる。(サンプルでは、不要な描画を避けるためにselectを使用)
ViewModelのメソッドは、ref.watch(provider名.notifier).
でアクセスできる。
( onPressedの中ではwatchでなくreadを使う)
import 'package:flutter/material.dart';
import 'package:flutter_basic/mvvm/profile/profile_view_model.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class ProfilePage extends StatelessWidget {
const ProfilePage({super.key});
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Consumer(
builder: (context, ref, _) {
final profileName = ref.watch(profileViewModelProvider
.select((value) => value.name));
final isInit = ref.watch(profileViewModelProvider
.select((value) => value.isInit));
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('名前: $profileName'),
Text('isInit: $isInit'),
const SizedBox(height: 30,),
ElevatedButton(
onPressed: () {
ref.read(profileViewModelProvider.notifier).isInitToggle();
},
child: const Text('Change'),)
],);
}
),)
);
}
}
最後に
最後までお読みいただきありがとうございます。
コードのサンプルと簡単な説明のみになってますが、サンプルコードを真似しながら
公式ドキュメント等で補完し、学習にお役立ていただければ幸いです。
参考記事
Discussion