フロント側をFlutter(スマホ)Thymeleaf(PC)、バックエンド側SpringBootの自動作成勉強中
9:04
①情報一覧画面に遷移したとき、検索タブになるが、先頭TextFieldにフォーカスが当たって、キーボードが出ているけど、BottomNavigationBarの「検索タブアイコン」「リストタブアイコン」が見えない。ので、フォーカスあてるのやめる。
■user_list.dart
・以下の「_myFocusNode」に関する箇所をコメントにしたので、PGMソースからは削除する。
余談:ユーザー情報登録更新画面(user_register_amend.dart)からは修正しない。現在のまま、先頭TextFieldにフォーカスを当てる。
// late FocusNode _myFocusNode; ・・・ List<Widget> _makeCndsWidgets() { ・・・ children:<Widget>[TextFormField( // // フォームを含むウィジェットが作成された時点でフォーカスする。 // autofocus: true, controller: _nameController, // focusNode: _myFocusNode, decoration: const InputDecoration( labelText: "名前", hintText: '全角カナ 前方一致', ), ・・・ } /// 情報一覧リストへのhttpアクセス void httpForList(String _mode, String _url) async { ・・・ // 画面を先頭に戻す _scrollController.animateTo(0, duration: const Duration(milliseconds:600), curve: Curves.easeInQuint); // _myFocusNode.requestFocus(); } ・・・ @override void initState() { ・・・ // _myFocusNode = FocusNode(); ・・・ } ・・・ @override void dispose() { _scrollController.dispose(); // _myFocusNode.dispose(); // Clean up the focus node when the Form is disposed. super.dispose(); }
■情報一覧画面に遷移したときの、修正前画面(先頭TextFieldにフォーカスが当たって、キーボードが出ているので、検索タブアイコン」「リストタブアイコン」が見えない。)
■情報一覧画面に遷移したときの、修正後画面(キーボードは出ていないので、検索タブアイコン」「リストタブアイコン」が見える。)
11:03
①昨日、”③いろいろ考えたが、こんな制約が必要なことになった。”としたが、この制約がなくてもOKなようにするための調査をしている。
11:44
結果、バックエンドSpring側UserFForm,.javaを以下のように変更することで、うまくいくことが分かった。もちろんFlutter側のjsonMapの変更も必要だけど。
これに沿って手直ししていく。
■今までのバックエンドSpring側UserFForm,.java(UserForm.javaを拡張していた)
@Data @EqualsAndHashCode(callSuper=false) @NoArgsConstructor @AllArgsConstructor @Component public class UserFForm extends UserForm implements Serializable { private static final long serialVersionUID = 1L; private String roles; //ロール(詳細表示用) private String mode; private String _csrf; private Integer page; }
■これからのバックエンドSpring側UserFForm,.java(UserForm.javaを包含する)→18:25にさらに修正した。
@Data @EqualsAndHashCode(callSuper=false) @NoArgsConstructor @AllArgsConstructor @Component public class UserFForm implements Serializable { private static final long serialVersionUID = 1L; @Valid private UserForm userForm; private String roles; //ロール(詳細表示用) private String mode; private String _csrf; private Integer page; }
18:25 上記11:44を修正した。
■これからのバックエンドSpring側UserFForm,.java(UserForm.javaを包含する)
さらに、mode、_csrf、pageをFormComm.javaにして包含した。
package com.kaz02u.demo.fForm; ・・・ @Data @EqualsAndHashCode(callSuper=false) @NoArgsConstructor @AllArgsConstructor @Component public class UserFForm2 implements Serializable { private static final long serialVersionUID = 1L; @Valid private UserForm userForm; private String roles; //ロール(詳細表示用) private FormComm formComm; } package com.kaz02u.demo.fForm; ・・・ @Data @NoArgsConstructor @AllArgsConstructor public class FormComm implements Serializable { private static final long serialVersionUID = 1L; private String mode; private String _csrf; private Integer page; }
19:02 フロントFlutter側のuser_form.dartを、バックエンドSpringの修正内容に合わせた。
■今までのフロントFlutter側のuser_form.dart(MessageFormを継承している)
import 'dart:convert'; import 'package:flutter/cupertino.dart'; import 'package:flutter_app/form/message_form.dart'; /// ユーザー情報画面Form class UserForm extends MessageForm{ //プロパティ 入出力データ用 late int id; // ID late String name; // 名前 late String email; // メールアドレス late String password; // パスワード late String passwordConfirm; // パスワード確認 late List<String> rolesArray; // ロール late String roles; // ロール (詳細表示用) // バックエンド(SpringBoot)のFormでBooleanだけどStringで処理する late String enableFlag; // 可否フラグ //プロパティ エラーメッセージ用 late List<String> idErr; // ID late List<String> nameErr; // 名前 late List<String> emailErr; // メールアドレス late List<String> passwordErr; // パスワード late List<String> passwordConfirmErr; // パスワード確認 late List<String> checkPasswordPasswordConfirmErr; // パスワード、パスワード確認の相関チェック late List<String> rolesArrayErr; // ロール late List<String> enableFlagErr; // 可否フラグ /// コンストラクタ // Dartでは「コンストラクタ名.任意名称」で複数のコンストラクタを定義する // 「UserForm(this.name,・・・);」コンストラクタはいらないので, // これだけにする。 // 初期値のクラスを作成している UserForm.initData() : super.initData() { //入出力データ用 id = 0; name = ""; email = ""; password = ""; passwordConfirm = ""; rolesArray = <String>[]; roles = ""; enableFlag = ""; //エラーメッセージ用 idErr = <String>[]; nameErr = <String>[]; emailErr = <String>[]; passwordErr = <String>[]; passwordConfirmErr = <String>[]; checkPasswordPasswordConfirmErr = <String>[]; rolesArrayErr = <String>[]; enableFlagErr = <String>[]; } /// HTTPレスポンスデータを反映する @override fromJson(String resData) { Map<String,dynamic> _resDataMap = jsonDecode(resData); //入出力データ用 if (_resDataMap["resForm"]==null) { id = 0; name = ""; email = ""; password = ""; passwordConfirm = ""; rolesArray = <String>[]; roles = ""; enableFlag = ""; } else { id = _resDataMap["resForm"]["id"] ?? 0; name = _resDataMap["resForm"]["name"] ?? ""; email = _resDataMap["resForm"]["email"] ?? ""; password = _resDataMap["resForm"]["password"] ?? ""; passwordConfirm = _resDataMap["resForm"]["passwordConfirm"] ?? ""; //jsonから読み込んだときのエラー(”type 'List<dynamic>' is not a subtype of type 'List<String>'”)に // 対応し、".cast<String>()"でキャストした rolesArray = _resDataMap["resForm"]["rolesArray"]!=null?_resDataMap["resForm"]["rolesArray"].cast<String>():<String>[]; roles = _resDataMap["resForm"]["roles"] ?? ""; enableFlag = ""; if (_resDataMap["resForm"]["enableFlag"] != null) { if (_resDataMap["resForm"]["enableFlag"]) { enableFlag = "true"; } else { enableFlag = "false"; } } } //エラーメッセージ用 if (_resDataMap["resErrorData"]==null) { idErr = <String>[]; nameErr = <String>[]; emailErr = <String>[]; passwordErr = <String>[]; passwordConfirmErr = <String>[]; checkPasswordPasswordConfirmErr = <String>[]; rolesArrayErr = <String>[]; enableFlagErr = <String>[]; } else { idErr = _resDataMap["resErrorData"]["id"]!=null?_resDataMap["resErrorData"]["id"].cast<String>():<String>[]; nameErr = _resDataMap["resErrorData"]["name"]!=null?_resDataMap["resErrorData"]["name"].cast<String>():<String>[]; emailErr = _resDataMap["resErrorData"]["email"]!=null?_resDataMap["resErrorData"]["email"].cast<String>():<String>[]; passwordErr = _resDataMap["resErrorData"]["password"]!=null?_resDataMap["resErrorData"]["password"].cast<String>():<String>[]; passwordConfirmErr = _resDataMap["resErrorData"]["passwordConfirm"]!=null?_resDataMap["resErrorData"]["passwordConfirm"].cast<String>():<String>[]; checkPasswordPasswordConfirmErr = _resDataMap["resErrorData"]["checkPasswordPasswordConfirm"]!=null?_resDataMap["resErrorData"]["checkPasswordPasswordConfirm"].cast<String>():<String>[]; rolesArrayErr = _resDataMap["resErrorData"]["rolesArray"]!=null?_resDataMap["resErrorData"]["rolesArray"].cast<String>():<String>[]; enableFlagErr = _resDataMap["resErrorData"]["enableFlag"]!=null?_resDataMap["resErrorData"]["enableFlag"].cast<String>():<String>[]; } super.fromJson(resData); } /// HTTPレスポンスデータを作成する @override Map<String,dynamic> toJsonMap (String _mode, String _csrf, int page) { Map<String,dynamic> rtnMap = { 'id': id, 'name': name, 'email': email, 'password': password, 'passwordConfirm': passwordConfirm, 'rolesArray': rolesArray, 'enableFlag': enableFlag, }; rtnMap.addAll(super.toJsonMap(_mode, _csrf, page)); return rtnMap; } /// 入出力データを、TextFormFieldに設定する forUserFormController( TextEditingController nameController, TextEditingController emailController, TextEditingController passwordController, TextEditingController passwordConfirmController ) { nameController.text = name; emailController.text = email; passwordController.text = password; passwordConfirmController.text = passwordConfirm; } /// TextFormField値を、入出力データに設定する fromUserFormController( TextEditingController nameController, TextEditingController emailController, TextEditingController passwordController, TextEditingController passwordConfirmController ) { name = nameController.text; email = emailController.text; password = passwordController.text; passwordConfirm = passwordConfirmController.text; } }
■これからのフロントFlutter側のuser_form.dart(MessageFormを包含している)→明日の日記でさらに修正している。
import 'dart:convert'; import 'package:flutter/cupertino.dart'; import 'message_form.dart'; /// ユーザー情報画面Form class UserForm { //プロパティ 入出力データ用 late int id; // ID late String name; // 名前 late String email; // メールアドレス late String password; // パスワード late String passwordConfirm; // パスワード確認 late List<String> rolesArray; // ロール late String roles; // ロール (詳細表示用) // バックエンド(SpringBoot)のFormでBooleanだけどStringで処理する late String enableFlag; // 可否フラグ //プロパティ エラーメッセージ用 late List<String> idErr; // ID late List<String> nameErr; // 名前 late List<String> emailErr; // メールアドレス late List<String> passwordErr; // パスワード late List<String> passwordConfirmErr; // パスワード確認 late List<String> checkPasswordPasswordConfirmErr; // パスワード、パスワード確認の相関チェック late List<String> rolesArrayErr; // ロール late List<String> enableFlagErr; // 可否フラグ // 共通・メッセージ late MessageForm messageForm; // 共通・メッセージ /// コンストラクタ // Dartでは「コンストラクタ名.任意名称」で複数のコンストラクタを定義する // 「UserForm(this.name,・・・);」コンストラクタはいらないので, // これだけにする。 // 初期値のクラスを作成している UserForm.initData() { //入出力データ用 id = 0; name = ""; email = ""; password = ""; passwordConfirm = ""; rolesArray = <String>[]; roles = ""; enableFlag = ""; //エラーメッセージ用 idErr = <String>[]; nameErr = <String>[]; emailErr = <String>[]; passwordErr = <String>[]; passwordConfirmErr = <String>[]; checkPasswordPasswordConfirmErr = <String>[]; rolesArrayErr = <String>[]; enableFlagErr = <String>[]; // 共通・メッセージ messageForm = MessageForm.initData(); } /// HTTPレスポンスデータを反映する fromJson(String resData) { Map<String,dynamic> _resDataMap = jsonDecode(resData); //入出力データ用 if (_resDataMap["resForm"]==null) { id = 0; name = ""; email = ""; password = ""; passwordConfirm = ""; rolesArray = <String>[]; roles = ""; enableFlag = ""; } else { id = _resDataMap["resForm"]["id"] ?? 0; name = _resDataMap["resForm"]["name"] ?? ""; email = _resDataMap["resForm"]["email"] ?? ""; password = _resDataMap["resForm"]["password"] ?? ""; passwordConfirm = _resDataMap["resForm"]["passwordConfirm"] ?? ""; //jsonから読み込んだときのエラー(”type 'List<dynamic>' is not a subtype of type 'List<String>'”)に // 対応し、".cast<String>()"でキャストした rolesArray = _resDataMap["resForm"]["rolesArray"]!=null?_resDataMap["resForm"]["rolesArray"].cast<String>():<String>[]; roles = _resDataMap["resForm"]["roles"] ?? ""; enableFlag = ""; if (_resDataMap["resForm"]["enableFlag"] != null) { if (_resDataMap["resForm"]["enableFlag"]) { enableFlag = "true"; } else { enableFlag = "false"; } } } //エラーメッセージ用 if (_resDataMap["resErrorData"]==null) { idErr = <String>[]; nameErr = <String>[]; emailErr = <String>[]; passwordErr = <String>[]; passwordConfirmErr = <String>[]; checkPasswordPasswordConfirmErr = <String>[]; rolesArrayErr = <String>[]; enableFlagErr = <String>[]; } else { idErr = _resDataMap["resErrorData"]["userForm.id"]!=null?_resDataMap["resErrorData"]["userForm.id"].cast<String>():<String>[]; nameErr = _resDataMap["resErrorData"]["userForm.name"]!=null?_resDataMap["resErrorData"]["userForm.name"].cast<String>():<String>[]; emailErr = _resDataMap["resErrorData"]["userForm.email"]!=null?_resDataMap["resErrorData"]["userForm.email"].cast<String>():<String>[]; passwordErr = _resDataMap["resErrorData"]["userForm.password"]!=null?_resDataMap["resErrorData"]["userForm.password"].cast<String>():<String>[]; passwordConfirmErr = _resDataMap["resErrorData"]["userForm.passwordConfirm"]!=null?_resDataMap["resErrorData"]["userForm.passwordConfirm"].cast<String>():<String>[]; checkPasswordPasswordConfirmErr = _resDataMap["resErrorData"]["userForm.checkPasswordPasswordConfirm"]!=null?_resDataMap["resErrorData"]["userForm.checkPasswordPasswordConfirm"].cast<String>():<String>[]; rolesArrayErr = _resDataMap["resErrorData"]["userForm.rolesArray"]!=null?_resDataMap["resErrorData"]["userForm.rolesArray"].cast<String>():<String>[]; enableFlagErr = _resDataMap["resErrorData"]["userForm.enableFlag"]!=null?_resDataMap["resErrorData"]["userForm.enableFlag"].cast<String>():<String>[]; } messageForm.fromJson(resData); } /// HTTPレスポンスデータを作成する Map<String,dynamic> toJsonMap (String _mode, String _csrf, int page) { Map<String,dynamic> userFormMap = { 'id': id, 'name': name, 'email': email, 'password': password, 'passwordConfirm': passwordConfirm, 'rolesArray': rolesArray, 'enableFlag': enableFlag, }; Map<String,dynamic> rtnMap = { 'userForm': userFormMap, 'formComm': messageForm.toJsonMap(_mode, _csrf, page), }; return rtnMap; } /// http.postのbodyで使用するJsonデータを返却する String toJson(String _mode, String _csrf, [int page=1]) { return json.encode(toJsonMap(_mode, _csrf, page)); } /// 入出力データを、TextFormFieldに設定する forUserFormController( TextEditingController nameController, TextEditingController emailController, TextEditingController passwordController, TextEditingController passwordConfirmController ) { nameController.text = name; emailController.text = email; passwordController.text = password; passwordConfirmController.text = passwordConfirm; } /// TextFormField値を、入出力データに設定する fromUserFormController( TextEditingController nameController, TextEditingController emailController, TextEditingController passwordController, TextEditingController passwordConfirmController ) { name = nameController.text; email = emailController.text; password = passwordController.text; passwordConfirm = passwordConfirmController.text; } }
■2022/06/15に、勉強した成果:『Flutter_JavaSpringプログラム自動作成◎自動生成ツール』をVectorに載せました。Zenn本も書きました。使ってみての感想や間違いの指定や、こうやったほうがいいとかの情報があればメールください。
・Vector
www.vector.co.jp
・Zenn本(Flutter_JavaSpringプログラム自動作成)
zenn.dev