サンプルアプリケーションの概要
今回のサンプルアプリケーションは、ログイン画面での認証を想定したもので、図1になります。
今回のサンプルアプリのように、起動時にアプリのバージョン情報や前回の状態を考慮した処理を行うことがあります。そこでこのアプリでも、画面初期化の部分で設定取得を想定したGETリクエストを行います。
そして、IDとパスワード入力でのログイン処理をPOSTリクエストで行うことを想定しています。また、それぞれのサーバへのリクエスト中はローディング中を示す画面を表示するようにします。
なお、それぞれのタイミングで発生するサーバへのリクエストでは、セキュリティを考慮したリクエストヘッダなどの設定が必要になることでしょう。本サンプルではそのようなケースを想定したタイミングで処理を実装しています。
実装の流れ
今回と次回を通じて以下の流れで進めていきます。
1. データ連係部分でモック機能のみ(画面処理を確認する方法)
2. http通信を使った場合のデータ連携(基本的な使い方を用いた連携方法)
3. http通信を使った場合のデータ連係(ヘッダなどのカスタマイズなどより実利用を想定した連携方法)
今回は、1. のモックを使った画面処理を確認できるようにし、次回は実際にhttpを使ったアクセス方法を紹介します。
画面側のプログラム
まず、ログイン画面側のプログラムを紹介します。これまで紹介してきた部分を含めてUI部品については表示を省略しています。
全体のコードはサンプルコード内にあるので、そちらを参照してください。また、各部品についてわからない部分があれば、これまでの紹介記事を参考にしてもらえればと思います。
class LoginPage extends StatefulWidget{ // : (省略) } class _LoginPage extends State{ // : (省略) final _loginIdFocus = FocusNode(); final _pswdFocus = FocusNode(); // (1) このページでデータ取得とログイン処理などをするためのクラス final ILoginController _pageController = MockLoginController(); // (2) 初期データ読みこみ完了フラグ var _init = false; // (3) ログインリクエストの読み込み中フラグ var _loading = false; @override Widget build(BuildContext context) { return Scaffold( body: Stack( children: [ Form( // : (省略) ), // (4) 読込中の場合には、画面の上にローディング画面を表示 if(!_init) createLoadingGlass(context), if(_loading) createLoadingGlass(context), ], ) ); } @override void initState() { // (5) 初期データを読みこむ var f = _pageController.config(); f.then((value) => setState((){ _init = true; })); super.initState(); } // : (省略) // (6) ローディングを示す画面を作成する Widget createLoadingGlass(BuildContext context){ return Container( // : (省略) child: Center( child: CircularProgressIndicator() ) ); } // : (省略) Widget createLoginForm(BuildContext context){ return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ TextFormField( // : (省略) ), TextFormField( // : (省略) ), ElevatedButton( onPressed: () async { final ok = _formState.currentState!.validate(); if(ok){ // : (省略) // (7) フォーカスを外す(キーボードを閉じる) _loginIdFocus.unfocus(); _pswdFocus.unfocus(); // (8) ローディング画面を表示 setState(() { _loading = true; }); // (9) サーバ側にリクエストする try { Response res = await _pageController.login( _loginIdController.text, _pswdController.text); // : (省略) } on Error catch(e){ showErrorSnackBar(context, '不明なエラーが発生しました'); } finally{ setState(() { _loading = false; }); } } }, child: Text('ログイン')) ], ); } // : (省略) }
(1)は、データ取得とログイン処理を行う部分のクラスです。このクラスと実装については、あとで説明します。
(2)、(3)はサーバへのリクエスト中にローディング画面を表示するためのフラグ管理をするための変数で、(4)のようにStackレイアウトで画面上部に表示されるようにしています。
(5)が画面の初期化時にサーバへ初期設定を読みこむことを想定した処理になります。この時、画面側で注意しなければいけない点があります。それは、リスト2のようにメソッドにasyncキーワードをつけて書き換えられないということです。従って、initStateなどではthenなどを使った非同期処理が必要になります。
void initState() async{ _pageController.config(); setState(() { _init = true; }); super.initState(); }
(6)がローディング画面の表示です。これまで紹介していませんでしたが、CircularProgressIndicatorがローディング中に表示されるアニメーション部分のクラスです。
このローディング表示には、進捗割合を表す表示方法と何らかの処理が実行中であることだけを示す方法の2種類が用意されていますが、今回は、後者の何らかの処理が実施している状況を示すだけの表示を採用しています。
そして、(7)では、入力フィールドからフォーカスを外しています。これは、キーボードが表示されたままだと、実際には入力ができるようになってしまうので、その抑制をしています。続いて、(8)でローディング画面を表示し、(9)で実際にログイン時の処理を行っています。
これまでの連載ではおもにデータ処理を伴わない場合の画面のみを扱ってきましたが、データ処理を扱うと画面側でも考慮しなければならないことが増えるということが実感できたと思います。