フロント側をFlutter(スマホ)Thymeleaf(PC)、バックエンド側SpringBootの自動作成勉強中
14:09
①昨日Flutter側のページングを作ったので、今日はバックエンドSpringBoot側を作っている。
16:32
①昨日作ったFlutter側のページングダメなところが出てきた。例えば”[最初]”のバリューが0で、当ページが0なら、1つのドロップダウンリストに同じバリューが複数できて、例外が発生する。
ので、'[最初]'は-20、'[前グループ]'は「((pageNumberGroup - 1) * maxPageSizes - 1)*-1 - 20」、'[前ページ]'は「(pageNumber0Org - 1)*-1 - 20」、'[次ページ]'は「(pageNumber0Org + 1)*-1 -20」、'[次グループ]'は「(((pageNumberGroup * maxPageSizes) - 1) + 1)*-1 - 20」、'[最後]'は「(totalPages - 1)*-1 -20」にして、ドロップダウンリストのバリューが-20よりマイナスの時は、+20して*-1して正の値にする。ってことにしよう。
もともと-1~-19までは処理しないために使用しているので、-20以下を使うって感じ。
22:44
①例外はこんなもんだった。ほんと、手こずった。自分的には、”よくやるよ”って感じのでき。ほめていない。こんぽん原因はpageを0起算にするか1起算にするかがうまくハンドリングできなかったことだった。javaのPageオブジェクトでは0起算、画面は1起算がどこかでうまくいかなかった。
”There should be exactly one item with value 0.
Either zero or 2 or more Items were detected with the same value.”
をgoogle翻訳で、
”値が0のアイテムが1つだけ存在する必要があります。
ゼロまたは2つ以上のアイテムが同じ値で検出されました。”
■Flutter側スマホ画面
↓2ページを選択
■PC側WEB画面
■Flutter側プロフラム
class _UserListState extends State<UserList> { ・・・ Widget build(BuildContext context) { ・・・ body: Form( key: _formKey, child : ListView( controller: _scrollController, children: _makeWidgets(), ), ), ・・・ } List<Widget> _makeWidgets() { if (_selectedIndex == 0) { return _makeCndsWidgets(); } else { return _makeListWidgets(); } } ・・・ List<Widget> _makeListWidgets() { ・・・ contentWidgets.add(Center( // ページ指定選択 child:Wrap( //mainAxisAlignment: MainAxisAlignment.center, direction: Axis.horizontal, children: <Widget>[ DropdownButton( items: getPageDropDown(5, _userSrchForm.pageObjSize, _userSrchForm.pageObjTotalPages, _userSrchForm.page), value: _userSrchForm.page>0?_userSrchForm.page-1:0, icon: const Icon(Icons.arrow_downward), elevation: 16, style: const TextStyle(color: Colors.deepPurple), underline: Container( height: 2, color: Colors.deepPurpleAccent, ), onChanged: (value) { setState(() { // ページ指定可能な物のみ処理する int intWk = (value as int); // -20以下または、0以上(-1から-19の時は何もしない) if (intWk <= -20 || intWk >= 0) { if (intWk <= -20) { // -20よりマイナスの時は、+20して*-1して正の値にし、+1(画面用ページ番号は+1するので)する。 intWk = (intWk + 20) * -1 + 1; } else { // +1(画面用ページ番号は+1するので)する。 intWk += 1; } if (intWk != _userSrchForm.page) { _userSrchForm.page = intWk; // 情報一覧リストへ、list_backで、httpアクセス httpForListBack(); } } } ); }, ), // 並び順選択 ・・・ ]), ), ); ・・・ return contentWidgets; } /// ページング用DropdownMenuItemを戻す /// /// maxPageSizes 1画面あたりのページ数 /// contentSize 1ページのレコード数 /// totalPages 総ページ数 /// pageNumber 現在のページ番号 /// return ページング用DropdownMenuItem static List<DropdownMenuItem<int>> getPageDropDown(int maxPageSizes, int contentSize, int totalPages, int pageNumberBase) { List<DropdownMenuItem<int>> _items = <DropdownMenuItem<int>>[]; int totalGroups = (totalPages / (maxPageSizes + 0.0)).ceil(); int pageNumber = contentSize == 0 ? 0 : pageNumberBase; int pageNumberGroup = (pageNumber / (maxPageSizes + 0.0)).ceil(); int startPage = (pageNumberGroup - 1) * maxPageSizes + 1; int endPage = startPage + maxPageSizes - 1; if (endPage >= totalPages) { endPage = totalPages; } int pageNumber0Org = pageNumber - 1; Map<int,int> _minusMap = {}; if (pageNumberGroup > 1) { _items.add(getPageMinusItem('[最初]', _minusMap, 0 - 20, -3)); _items.add(getPageMinusItem('[前グループ]', _minusMap, ((pageNumberGroup - 1) * maxPageSizes - 1)*-1 - 20, -4)); } else { _items.add(const DropdownMenuItem( value: -5, child: Text('[最初]', style: TextStyle(fontWeight: FontWeight.normal, color: Colors.grey)), )); } if (pageNumber == 1) { _items.add(const DropdownMenuItem( value: -6, child: Text('[前ページ]', style: TextStyle(fontWeight: FontWeight.normal, color: Colors.grey)), )); } else { _items.add(getPageMinusItem('[前ページ]', _minusMap, (pageNumber0Org - 1)*-1 - 20, -7)); } for (int i = startPage; i <= endPage; i++) { if (pageNumber == i) { _items.add(DropdownMenuItem( value: i - 1, child: Text('[' + CommUtils.chgToString(i) + ']', style: const TextStyle(fontWeight:FontWeight.w900, color: Colors.black, backgroundColor: Colors.yellow)), )); } else { _items.add(DropdownMenuItem( value: i - 1, child: Text('[' + CommUtils.chgToString(i) + ']', style: const TextStyle(fontWeight: FontWeight.normal, color: Colors.black)), )); } } if (pageNumber == totalPages || totalPages == 1 || totalPages == 0) { if (totalPages == 0) { _items.add(const DropdownMenuItem( value: -8, child: Text('[1]', style: TextStyle( fontWeight: FontWeight.normal, color: Colors.grey)), )); _items.add(const DropdownMenuItem( value: -9, child: Text('[次ページ]"', style: TextStyle( fontWeight: FontWeight.normal, color: Colors.grey)), )); } else { _items.add(const DropdownMenuItem( value: -10, child: Text('[次ページ]', style: TextStyle( fontWeight: FontWeight.normal, color: Colors.grey)), )); } } else { _items.add(getPageMinusItem('[次ページ]', _minusMap, (pageNumber0Org + 1)*-1 - 20, -11)); } if (pageNumberGroup < totalGroups) { _items.add(getPageMinusItem('[次グループ]', _minusMap, (((pageNumberGroup * maxPageSizes) - 1) + 1)*-1 - 20, -12)); _items.add(getPageMinusItem('[最後]', _minusMap, (totalPages - 1)*-1 - 20, -13)); } else { _items.add(const DropdownMenuItem( value: -14, child: Text('[最後]', style: TextStyle(fontWeight: FontWeight.normal, color: Colors.grey)), )); } return _items.toList(); } static DropdownMenuItem<int> getPageMinusItem(String str, Map<int,int> minusMap, int minusVal, int constVal) { // すでに設定したバリューの場合 if (minusMap.containsKey(minusVal)) { return DropdownMenuItem( // 指定の値にする value: constVal, child: Text(str, style: const TextStyle(fontWeight: FontWeight.normal, color: Colors.grey)), ); } else { minusMap[minusVal] = minusVal; return DropdownMenuItem( value: minusVal, child: Text(str, style: const TextStyle(fontWeight: FontWeight.normal, color: Colors.black)), ); } }
④これがSpringBootバックエンドがわのController抜粋
・PC・スマホ向け共通Controller
public class UserCommController { ・・・ /** * リターン共通処理(Flutter、PC・WEB共用) * リターン共通処理 * * @param url 遷移先 * @param model モデル * @param result チェック結果 * @param flutterFlg true:Flutter用 false:PC・WEB用 * @param resFormName Flutter用レスポンスForm名 * @return Flutter用String:jsonデータ PC・WEB用 Map<String, Object>:遷移先 */ protected Object returnComm(String url ,Model model,BindingResult result, boolean flutterFlg, String resFormName) { if (flutterFlg) { ResData resData = new ResData(model, result); return resData.getResDataMap(messageSource, resFormName); } else { return url; } } ・・・ protected Object userListBackComm(SrchOrderFForm srchOrderFForm, Model model, Pageable pageable, boolean flutterFlg) { //補足:フロント側がFlutter時のsrchOrderFormはsrchOrderFFormを指しています。 userListBackSub(model, pageable); if (flutterFlg) { UserSrchFForm userSrchFForm = new UserSrchFForm(); //同一プロパティ(型名まで同じもの)コピー BeanUtils.copyProperties(this.sessionUserSrchForm.getUserSrchForm(), userSrchFForm); BeanUtils.copyProperties(srchOrderFForm, userSrchFForm); Map<String, Object> modelMap = model.asMap(); userSrchFForm.setPage(((Page)modelMap.get("page")).getNumber() + 1); // Flutter向け検索条件+ソート条件+ページ番号 model.addAttribute("userSrchFForm", userSrchFForm); } return returnComm("/members/admin/user/userList", model, null, flutterFlg, "userSrchFForm"); } /** * userListBackSubメソッド * ユーザー情報リスト一覧表示サブ処理 * * @param model モデル * @param pageable ページ */ protected void userListBackSub(Model model, Pageable pageable) { userListSub(this.sessionUserSrchForm.getUserSrchForm(), this.sessionUserSrchOrderForm.getSrchOrderForm(), model, pageable); model.addAttribute("userSrchForm", this.sessionUserSrchForm.getUserSrchForm()); model.addAttribute("srchOrderForm", this.sessionUserSrchOrderForm.getSrchOrderForm()); }
・スマホ向けController
public class UserFlutterController extends UserCommController { ・・・ /** * ユーザー情報リスト一覧表示処理 * ページリンク押下により、ユーザー情報リスト一覧を表示する処理 * * @param mode モード * @param model モデル * @param pageable ページ * @return 遷移先 */ @SuppressWarnings("unchecked") @PostMapping("/members/admin/user/userA/list_back") @ResponseBody public Map<String, Object> userListBack(@RequestBody SrchOrderFForm srchOrderFForm, Model model) { int page = ObjectUtils.isEmpty(srchOrderFForm.getPage())||srchOrderFForm.getPage()==0?0:srchOrderFForm.getPage()-1; Pageable pageable = PageRequest.of(page, pageableDefaultSize, Sort.unsorted()); return (Map<String, Object>)userListBackComm(srchOrderFForm, model, pageable, true); }
・PC向けController
public class UserPcController extends UserCommController { /** * ユーザー情報リスト一覧表示処理 * ページリンク押下により、ユーザー情報リスト一覧を表示する処理 * * @param mode モード * @param model モデル * @param pageable ページ * @return 遷移先 */ @PostMapping(params="mode=list_back") public String userListBack( @RequestParam("mode") String mode, Model model, @PageableDefault( size=pageableDefaultSize ) Pageable pageable) { return (String)userListBackComm(null, model, pageable, false); }