【Flutter】FlutterにおけるMVVMの実装例を3つ解説!

Flutter

こんにちは。新人プログラマーの岩本です。

今回はFlutterでMVVMを実現するための方法を紹介します。

MVVMは初学者でも扱いやすいアーキテクチャなので、ぜひ最後までご覧ください。

この記事は新卒エンジニアが執筆しています。(Flutter歴2週間)
そのため内容に間違いや不備がある場合があります。
もし間違いを発見しましたら、どんどん指摘していただけると幸いです。

MVVMとは

MVVMとはGUIアーキテクチャの1種です。

GUIアーキテクチャとは、UIとロジックを分離させるもので、MVCやMVPなどがあります。

MVVMは3つの構成要素によって成り立っています。

名前役割
ViewUI担当
ViewModelViewとModelの中継役。
Modelの値の変更をViewに通知する役割を持つ。
ModelデータモデルやAPI通信など。
ViewとViewModel以外の全てを担当する。

それぞれの頭文字を取ることで、M(Model)V(View)VM(ViewModel)となります。

ではここからFlutterでMVVMを実現するための方法を紹介します。

またここで紹介する方法はあくまで一例で、絶対のものではありません。

ChangeNotifier

ChangeNotifierとは、Flutter SDKに含まれているクラスです。

notifyListeners() を呼ぶことで、値の変更をViewに通知することができます。
またView側はChangeNotifierProviderでViewModelをcreateできます。

言葉で説明しても伝わらないと思うので、実際の実装例を紹介します。

実装例

事前準備

まずは必要なパッケージをインストールします。

今回はProviderのみでOKです。

Bash
flutter pub add provider

MVVMを実装

次にview_model.dartを作成し、以下のコードを入力します。

view_model.dart
class ViewModel extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count += 1;
    notifyListeners();
  }
}

ViewModelはChangeNotifierを継承し、値の変更時にnotifyListeners()を呼び出しています。

次にmain.dart内のMyHomePageクラスを以下のように書き換えます。

main.dart
class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => ViewModel(),
      builder: (context, _) {
        final viewModel = context.watch<ViewModel>();
        return Scaffold(
          appBar: AppBar(
            backgroundColor: Theme.of(context).colorScheme.inversePrimary,
            title: const Text('ChangeNotifier'),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  '${viewModel.count}',
                  style: Theme.of(context).textTheme.headlineMedium,
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: viewModel.increment,
            tooltip: 'Increment',
            child: const Icon(Icons.add),
          ),
        );
      },
    );
  }
}

ChangeNotifierProviderでViewModelをcreateし、表示する値はViewModelのものを参照するようにしています。

StateNotifier

StateNotifierとは、Providerパッケージの作者の方が作った状態管理のためのパッケージです。

ChangeNotifierと違い、notifyListeners() を呼ばなくても変更を検知できます。

Riverpod2.0からStateNotifierは非推奨になりました。
代わりに後述するNotifierを使用してください。
今回は調べたので、一応解説させてください。

実装例

事前準備

まず必要なパッケージをインストールします。

今回はRiverpodを使用します。

Bash
flutter pub add flutter_riverpod

MVVMの実装

次にhome_page_notifier.dartを作成し、以下のコードを入力してください。

home_page_notifier.dart
// 状態を操作するclass
class HomePageNotifier extends StateNotifier<int> {
  HomePageNotifier(): super(0); // 0で初期化する

  void increment() {
    state += 1;
  }
}

// UIから状態にアクセスできるようにするProvier
// UIはこのProviderを通じて状態にアクセスする
final homePageProvider = StateNotifierProvider((ref) => HomePageNotifier());

StateNotifierを使用するには、以下の2つの要素が必要になります。

  • 状態を操作するclass(StateNotifierを継承)
  • UIから状態にアクセスできるようにするProvider(StateNotifierProvider)

次にmain.dart内のMyHomePageクラスを以下のように書き換えます。

main.dart
class MyHomePage extends ConsumerWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final homePageState = ref.watch(homePageProvider);
    final homePageNotifier = ref.read(homePageProvider.notifier);

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('StateNotifier'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              '$homePageState',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: homePageNotifier.increment,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

homePageProviderをwatchすることで、値の変更を検知してUIが再レンダリングされるようになります。

Notifier

Notifierとは、変更される可能性のある状態を管理するためのProviderです。

使い方はStateNotifierとほぼ同じです。

しかしNotifierの場合は、Riverpod Generatorでコードを自動生成することができます。

実装例

まずは必要なパッケージをインストールします。

Bash
flutter pub add flutter_riverpod riverpod_annotation
Bash
flutter pub add --dev riverpod_generator build_runner

インストールしたパッケージは以下の通りです。

パッケージ名役割
flutter_riverpodFlutterでRiverpodを使用するためのパッケージ
riverpod_annotationコード生成のためのアノテーションを提供
riverpod_generatorRiverpodのコード生成を行うためのパッケージ
build_runnerソースコード生成ツール

MVVMの実装

次にhome_page_notifier.dartを作成し、以下のコードを入力してください。

home_page_notifier.dart
part 'home_page_notifier.g.dart';

@riverpod
class HomePageNotifier extends _$HomePageNotifier {
  @override
  int build() => 0;

  void increment() {
    state += 1;
  }
}

次に以下のコマンドを入力して、コードを自動生成してください。

Bash
flutter packages pub run build_runner build

するとhome_page_notifier.g.dartが生成され、homePageNotifierProviderが追加されていると思います。

次にmain.dart内のMyHomePageクラスを以下のように書き換えます。

main.dart
class MyHomePage extends ConsumerWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final homePageState = ref.watch(homePageNotifierProvider);
    final homePageNotifier = ref.read(homePageNotifierProvider.notifier);

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('Notifier'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              '$homePageState',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: homePageNotifier.increment,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

基本的な書き方はStateNotifierと同じで、homePageNotifierProviderをwatchすることで、値の変更を検知することができます。

まとめ

今回はFlutterでのMVVMの実装例を紹介しました。

個人的には、特別な理由がなければNotifierを使うのが良いと思いました。

MVVMは色々な場面で使えるので、ぜひご自身の手で色々と試してみてください。

ここまでのご閲覧ありがとうございました!

参考にした記事

MVVM で作る|Flutter設計パターン入門
【Flutter】実務で使うMVVMの実装例
【Flutter】MVVMをRiverpodで実装する!サンプルを用いて解説|TERUPRO
こんにちは、テルプロです! 「MVVMをRiverpodで実装する方法は?」とお悩みではないでしょうか。 テルプロ 本記
Flutterで次のレベルへ!中級者向けRiverpod NotifierProviderの使い方 - Qiita
はじめにこんにちは、株式会社viviON アプリユニットのDiegoですFlutterでアプリの開発担当してます。皆さんFlutterの開発楽しんでいますでしょうか?私は最近のFlutter…
【Flutter】Riverpod 2.0 の Notifier と riverpod_generator の解説

コメント

タイトルとURLをコピーしました