データ処理と画面の分離
先ほどの画面のコード内でも紹介しましたが、ILoginControllerというクラスが初期設定用のデータの取得とログイン処理をするためのクラスになります。画面表示と処理を分離する方法はさまざまな方法がありますが、ここではできるだけ他のライブラリなどに依存せず簡単にできるようにしています。
ただし、ILoginControllerというクラスには処理の実装はなく、実装に応じたクラスを図2のように定義する想定です。
このように分けた理由としては、今回紹介する画面動作を確認するための実装から、次回紹介するhttpを用いた実装へと切り替えた際にも画面側の変更を最小にするためです。
また、ILoginControllerクラスの実装がリスト3です。
import 'package:chat_app/data/Response.dart';
// (1) abstractクラスとして定義
abstract class ILoginController{
// (2) 初期設定の読込
Future config();
// (3) ログイン処理
Future login(String loginId,String password);
// (4) 終了処理
void dispose(){
}
}
Flutterではインターフェースクラスはないので、(1)のようにabstractクラスとして定義しています。クラス名の名前に"I"をつけているのはインターフェース的な役割を意味するためですが、特にそれ以上の意味はありません。
また、実際の(2)設定の取得や、(3)ログイン処理は実装クラスで行うことを前提としています。終了処理として(4)のdisposeメソッドを用意していますが、こちらは実装により後処理が必要なケースを想定したものです。今回は利用しません。
また、結果を示すデータはリスト4のように定義しています。このように結果を示すデータクラスを定義した方が、画面と処理が密連携にならずに済みます。
class Response{
final bool _ok;
final int _code;
final String _message;
Response(this._ok, this._code) : _message = '';
Response.error(this._code,this._message) : _ok = false;
String get message => _message;
int get code => _code;
bool get ok => _ok;
// : (省略)
}
Futureクラスを使った非同期処理の実装
では、続いてデータ連携・処理部分を実装していきます。ただし、今回は、実際にサーバにリクエストをしないモックでの実装です。
実際にサーバにリクエストする場合では、結果が非同期型になります。そのため、モックでも非同期型で結果が返ってくるようにしなければなりません。そのような場合に表1で示すFutureクラスのメソッドが利用できます。
| メソッド名 | 説明 |
|---|---|
| value | 非同期で返す値を設定するメソッド |
| delayed | 一定時間の待ち時間のあとに処理をするメソッド |
| sync | 非同期で行う処理を定義するメソッド。valueではなく関数内で結果を返すようにするメソッド |
| error | エラーを返すためのメソッド |
これらのメソッドを使ったモック実装例がリスト5のようになります。
class MockLoginController extends ILoginController{
// (1) ログイン処理
@override
Future login(String loginId, String password) {
return loginFalseWithDuration(loginId, password);
}
// (2) valueの使い方
Future loginFalse(String loginId,String password){
return Future.value(Response.error(500, 'ログインIDまたはパスワードを確認してください'));
}
// (3) delayedの使い方
Future loginTrueWithDuration(String loginId,String password){
return Future.delayed(const Duration(seconds: 1),(){
return Response(true,200);
});
}
// (4) syncをの使い方
Future loginSync(String loginId,String password){
return Future.sync((){
print("実行中");
return Response.error(500, 'ログインIDまたはパスワードを確認して下さい');
});
}
// (5) errorの使い方
Future loginError(String loginId,String password){
return Future.error(Error());
}
}
(1)はILoginControllerのlogin処理の実装です。実際にさまざまな動作を確認するためにはこのメソッド内で(2)~(5)までのように返す結果を変えることを想定しています。
非同期での結果を返す最も単純な方法は(2)のvalueメソッドを利用する方法です。valueメソッドの引数に値を設定すれば、その値が非同期で返却されるようになります。
また、実際の非同期処理ではある程度、時間がかかってしまう場合があります。今回の画面の例でもそのためにローディング画面を用意しました。その画面を確認するためにはある程度時間をかけて処理を実行する必要があります。
そのような場合に利用できるのが(3)のdelayedメソッドです。このメソッドでは、待ち時間を第1引数に、そして、第2引数に結果を返す関数を定義します。また、(4)のsyncメソッドは非同期で処理をするためのメソッドです。
今回のサンプル上ではあまり、利用しなければいけないケースはありませんが、返す値を作る処理自体が非同期処理内で記述する必要があるケースもあります。
また、今回のサンプルでは値を返していますが、必ずしも値を返す必要はありません。そのため、これらのメソッドは、画面をロックしないように何らかの処理が必要な場合にも利用されることがあります。
非同期処理の中でエラーが発生したことと同じようにするのが、(5)のerrorメソッドです。実際に画面内などでエラー処理をする場合には、リスト6のコードでエラー処理を行います。
// Futureクラスを使った場合
var f = _pageController.config();
f.catchError((error){
// エラー処理
});
// awaitキーワードを使った場合
try {
Response res = await _pageController.login(id,pswd);
} on Error catch(e){
// エラー処理
};
まとめ
スマホアプリを開発する際に筆者が最も面倒に感じるのが、外部サービスとの連携部分です。すべての開発者が常に同じように外部サービスを使う上で、何の制限もなく利用できるのであればまだ問題は小さいかも知れません。
しかし、実際には、そのようなケースはほとんどなく、さまざまな制限が多く発生する場合の方が多いでしょう。そのため、アプリを開発する際には、画面の動作を確認するためと外部サービスも含めた動作で別に確認できる必要があります。
また、データ操作を含む多くの処理が非同期になってしまいます。今回のログイン画面のように常に画面操作ができることが望ましくないケースもあり、利用者の画面操作を抑制したほうがいい場合もあります。
このようにデータ連携・操作をする場合には、その目的にあった画面処理も必要になるので、処理の実現方法だけではなく、その処理をしている最中に、利用者側で何ができて、何ができないかを常に考慮する必要があります。
次回は、実際にサーバにhttpリクエストをする方法を紹介します。
