93661


「方法1」みたいにview側に具体的なやることを書いていくのか、それとも「方法2」のようにbuttonTapActionのようなタップ処理だけ呼び出す処理を書いて、中身はviewModel(Controller)に書いていくのかどちらが最適だと思いますか？  
個人的には後者だと思います。  
メリットとしてはtakePictureやsavePhotoFromDeviceなどのモデルのメソッドがテストしやすいと感じました。

不安点としては３点あり、まず他の記事のサンプルコードの少なさです。  
写真を撮影する記事を確認したところ、ほとんどの記事がviewのonTapでtakePicture()メソッドが呼ばれていました。

それからbuttonTapActionに引数が多くなってしまいがちなことも不安点の１つです。  
最後に、今回のようにタップ処理で写真を撮ったり、デバイスに写真を保存するような複雑になった状態で、タップ処理をviewModelにまとめて記載したとします。その場合、どうしてもbuttonTapActionのようなview側で何をやっているか分からない処理になってしまいます。  
アドバイスいただけると幸いです。お願いします。

#### 方法1

[code]

    ElevatedButton(
                // ボタンタップした時の処理               
               onPressed: () async {
                  try {
                     // 写真を撮る処理
    　　　　　　　　　　　viewModel.takePicture();　　　　　　　　　　　　
    　　　　　　　　　　// デバイスに写真を保存する処理
                      viewModel.savePhotoFromDevice();
    　　　　　　　　　　// ローカルDBに保存する処理
                     viewModel.updateDb();
                 　} on Exception catch (e) {
                       logger.i(e);
                    }
                 },
             child: const Text('カメラを撮影'),
        ),
    
[/code]

#### 方法2

[code]

    ElevatedButton(
                // ボタンタップした時の処理               
               onPressed: () async {
                  try {
                    // Buttonをタップする関数
                    viewModel.buttonTapAction();
                 　} on Exception catch (e) {
                       logger.i(e);
                    }
                 },
             child: const Text('カメラを撮影'),
        ),
    
[/code]

[code]

    class ViewModel {
      var counter1 = 0;
      var counter2 = 0;
      void buttonTapAction() {
       // 写真を撮る処理
    　　model.takePicture();　　　　　　　　　　　　
    　　// デバイスに写真を保存する処理
       model.savePhotoFromDevice();
    　　// ローカルDBに保存する処理
        model.updateDb();
        changeCounter1();
        changeCounter2();
      }
      
      void changeCounter1() {
        counter1++;
        notifylisteners();
      }
    
       void changeCounter2() {
        counter2++;
        notifylisteners();
      }
    }
    
[/code]


CC BY-SA 4.0
2023-02-01T03:17:09.273
93650
2023-02-02T01:24:54.967
2023-02-02T01:24:54.967
3060
36424
question
flutter mvvm
ボタンタップ時の責務の分け方が分からない


個人的には、View側にはViewModel呼び出しのみとするのが好きです。

以下の様な感じでtryなんちゃらも全てViewModelに押し込んでしまう。

[code]

        ElevatedButton(
            // ボタンタップした時の処理               
            onPressed: viewModel.buttonTapAction,
            child: const Text('カメラを撮影'),
        ),
    
        class ViewModel {
          void buttonTapAction() {
           try {
               // 写真を撮る処理
           　　model.takePicture();　　　　　　　　　　　　
        　　   // デバイスに写真を保存する処理
              model.savePhotoFromDevice();
           　 // ローカルDBに保存する処理
              model.updateDb();
           } on Exception catch (e) {
               logger.i(e);
           }
          }
        }
    
[/code]

View側も処理がないので単体テストも楽だし、ViewModel側も処理まとまってるのでテストも楽だろうし。

>
> 最後に、今回のようにタップ処理で写真を撮ったり、デバイスに写真を保存するような複雑になった状態で、タップ処理をviewModelにまとめて記載したとします。その場合、どうしてもbuttonTapActionのようなview側で何をやっているか分からない処理になってしまいます。

MVVMとはそういうものだと思って設計してます。  
View側は基本UI部分メインでUIでのイベントが発生した場合ViewModel側にイベントがあったことを知らせる。ViewModel側は発生したイベントに対して何らかの処理を行う。

「複雑」と言っている部分も、何をもって複雑だと感じているのかが不明です。  
使用するパッケージの仕様を考慮し、画面遷移とか状態遷移とかを含めた設計を行えば何ら問題ないのでは。

強いて言えばFlutterのパッケージ類はドキュメントが弱い、もしくはWEB上での例も少ないので、何を言わんとしているのか不明なものが多く、そういうものは自分でサンプル作って動きを追って自分の知識に落とし込む必要があるのがつらいところかも。

なお、設計に関しての最適解は個人の感覚もしくはプロジェクトの方針などにより決まると思います。

以下、コメントに対しての返答。

> １点目： 上記のコードに状態管理を更新するメソッドを追加しました。
> その際、ボタンタップ時にUIの更新が行われるためview側でもそれが分かるようなメソッド名にした方がいいのかなと感じました。これについてご意見をいただきたいです。

メソッド名とかは、個人の好みで何とかしてください。  
またプロジェクト等ではその中で命名規則とかが決まってると思います。  
それに従ってください。

> ２点目： buttonTapActionの中身を全てviewModelで定義する場合だと、
> 例えばボタンタップ時にshowDialogメソッドを使ってアラート出す処理などviewModelのメソッドの引数にcontextが入ってしまう場合があります。
> またシンプルにviewModelでアラートやインディケーターのようなUIが表示できるのもviewModelにviewが一部表示されてしまい不自然に感じてしまいます。
> viewModelにcontextが入る場合、viewModelにUIが表示される場合、この２点で問題がないのかもご意見を伺いたいです。

上位側からcontextを受け渡さなくても良いパッケージというのもあります。  
私はGetXというパッケージを使ってますが、グローバルで最新のcontextが得られます。まあ使い方を間違えれば、不具合（副作用）があるので注意が必要ですが。

もしくはViewModel側にcontextを引数で渡してしまうという方法もあると思います。

後「viewModelにviewが一部表示されてしまう不自然さ」は気のせいです。そう思うことにするのです。  
Viewが表示されるという感覚を持つのではなく、(Viewを表示しその)結果を取得するAPIの呼び出しをするという感じにするのです。そういう意味にするとDBからのデータ取得と何ら変わらない感覚になると思います。  
Viewの実装をViewModelにちまちま書くとそういう感覚が薄れるので、Viewの呼び出しとそのデータ取得もクラス化（カプセル化）してしまえばいいのです。  
これにより各クラスの単体テストもよりしやすくなります。


CC BY-SA 4.0
2023-02-01T08:19:28.403
93661
2023-02-02T01:23:59.073
2023-02-02T01:23:59.073
54446
54446
93650
answer