フロント側をFlutter(スマホ)Thymeleaf(PC)、バックエンド側SpringBootの自動作成勉強中
9:16
①昨日の、Flutter(スマホ)側の”この辺を調査する。”を調査してる。
・”日付選択”っていう文字は消す。(書いてなくてもわかるので)をやった。
SimpleDialogのtitle: をコメントにしたらできた。簡単だった。
return SimpleDialog( // title: Text('日付選択'), // 日付選択ダイヤログはタイトルを出さない children: <Widget>[ TableCalendar(
9:33
①年月表示が左に寄っているので中央にしたい。をやった。
HeaderStyleのtitleCenteredをtrueにしたらできた。簡単だった。
TableCalendar( ・・・ // カレンダーフォーマット変更ボタンを表示しない headerStyle: const HeaderStyle( titleCentered: true,
12:07
①罫線が少し見づらい。をやってる。
まず、今日の枠の罫線がないので、少し薄く感じる。ので、todayBuilderを書き換えた。
青っぽい丸に白色の日付がデフォルト表示なので、真似した。以外に難しかった。
まだ終わりじゃない。月の最初に日と最後の日の罫線が気になる。ので調べよう。
さらに、なぜか、有効範囲(firstDay~lastDay)以外の日付部分の罫線が変なので。調査する
TableCalendar( ・・・ calendarBuilders: CalendarBuilders( ・・・ /// 今日のセルを枠線のついたContainerに置き換える todayBuilder: ( BuildContext context, DateTime day, DateTime focusedDay) { return AnimatedContainer( duration: const Duration(milliseconds: 250), margin: EdgeInsets.zero, decoration: BoxDecoration( border: Border.all( color: Colors.green[600]!, width: 0.5, ), ), child: AnimatedContainer ( duration: const Duration(milliseconds: 250), margin: const EdgeInsets.all(5), decoration: const BoxDecoration( color: Color(0xFF9FA8DA), shape: BoxShape.circle, ), alignment: Alignment.center, child: Text( day.day.toString(), style: const TextStyle( fontWeight: FontWeight.bold, color: Colors.white, ), ), ), ); },
15:16
①よくよく見てみると「CalendarBuilders」(有効範囲(firstDay~lastDay)以外の日付部分)の「disabledBuilder」が効いていないのだ。と思ってたら、実は「outsideBuilder」(現在フォーカスされている月と一致しない日付部分)を使うのが正解だった。これで、全てのマスに罫線がついた。よかった
TableCalendar( ・・・ calendarBuilders: CalendarBuilders( ・・・ /// 現在フォーカスされている月と一致しない日付部分を生成する outsideBuilder: ( BuildContext context, DateTime day, DateTime focusedDay) { return AnimatedContainer( duration: const Duration(milliseconds: 250), margin: EdgeInsets.zero, decoration: BoxDecoration( border: Border.all( color: Colors.green[600]!, width: 0.5, ), ), alignment: Alignment.topCenter, child: Text( day.day.toString(), style: const TextStyle( color: Colors.grey, ), ), ); },
②さらに、「CalendarBuilders」(有効範囲(firstDay~lastDay)以外の日付部分)の「disabledBuilder」も必要だということも思いついた。カレンダーの開始年月日、終了年月日を指定するので、それ以外の日付は選択できなくなる。っていうふうに使うため。
17:04
①Flutter(スマホ)側のダイヤログ表示時に入力日付がわかるようにマークを付ける。をやった。各Builder(outsideBuilder、disabledBuilder、defaultBuilder、todayBuilder)の「Container」の「decoration: BoxDecoration」を、入力日付の時は赤罫線を出すようにした。
20:40
①Flutter(スマホ)側のダイヤログ表示時に入力日付がわかるようにマークを付ける。も含めて、完成した。多分これなら使えると思う。(罫線の外枠がちょっと細いのは、このままでいいかなって思っている。)
■Flutter(スマホ)側のopenDateDialogCommメソッド呼びもと。入力日付が入っていなくても動く様に修正した。
//--テスト日付入力 start---------------------------- contentWidgets.add( Row ( // mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ // テスト日付の入力フォーム Container( margin: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 5), padding: const EdgeInsets.fromLTRB(10.0, 0, 10.0, 0), child:Text(_focusedDayStr, textAlign: TextAlign.left, overflow: TextOverflow.clip, ), ), Container( margin: const EdgeInsets.fromLTRB(5, 0, 5, 5), padding: const EdgeInsets.fromLTRB(5, 0, 5, 0), child: ElevatedButton( onPressed: () async { DateTime? _date; if (_focusedDayStr == "") { _date = null; } else { _date = DateTime.utc(int.parse((_focusedDayStr).substring(0, 4)), int.parse((_focusedDayStr).substring(5, 7)), int.parse((_focusedDayStr).substring(8, 10)),0,0); } String? _str = await CommUtils.openDateDialogComm(context, DateTime.utc(1900, 1, 1), DateTime.utc(2099, 12, 31), _date); if (_str != null && _str != Consts.cancel) { setState(() { _focusedDayStr = _str; }); } }, style: ElevatedButton.styleFrom( primary: Colors.blue, ), child: const Text("日付選択"), ), ), ] ), ); //--テスト日付入力 end----------------------------
■Flutter(スマホ)側のCommUtilsクラス(共通ユーティリティクラス)・openDateDialogCommメソッド
/// 日付入力ダイヤログ /// カレンダー表示の開始、終了年月日とフォーカスする年月日を入力し、 /// 選択日付、CANCELを戻す。 static Future<String?> openDateDialogComm(BuildContext context, DateTime _firstDay, DateTime _lastDay, DateTime? _inFocusedDay) async { initializeDateFormatting('ja', null); DateFormat outputFormat = DateFormat('yyyy/MM/dd'); DateTime? _selectedDay; DateTime _focusedDay = _inFocusedDay ?? DateTime.now(); DateTime _originFocusedDay = _focusedDay; int getHashCode(DateTime key) { return key.day * 1000000 + key.month * 10000 + key.year; } // TableCalendarでは、カレンダーに読み込むイベントをMapで定義した場合、 // LinkedHashMap使用が推奨されている。 final _events = LinkedHashMap<DateTime, List>( equals: isSameDay, hashCode: getHashCode, ); List getEventForDay(DateTime day) { return _events[day] ?? []; } // 祝祭日リスト Map<DateTime, List> _eventsList = {}; // 日付入力ダイヤログ // イベントマーカーを赤丸”休”とする Widget _buildEventsMarker(DateTime date, List events) { return Positioned( right: 5, bottom: 5, child: AnimatedContainer( duration: const Duration(milliseconds: 300), decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.red[300], ), width: 16.0, height: 16.0, child: Center( child: Text( '休', style: const TextStyle().copyWith( color: Colors.white, fontSize: 12.0, ), ), ), ), ); } /// 日曜日を赤色、土曜日を青色にしたい Color _textColor(DateTime day) { const _defaultTextColor = Colors.black87; if (day.weekday == DateTime.sunday) { return Colors.red; } if (day.weekday == DateTime.saturday) { return Colors.blue[600]!; } return _defaultTextColor; } /// 項目入力日及び選択日の罫線を赤にする BoxDecoration _boxDecoration(DateTime day) { BoxDecoration _boxDecoration; // ダイヤログ表示後、日付が選択されるまでと、日付が選択された時 if (_selectedDay == null && isSameDay(day, _originFocusedDay) || _selectedDay != null && isSameDay(day, _selectedDay)) { _boxDecoration = BoxDecoration( border: Border.all( color: Colors.red[800]!, width: 3.0, ), ); } else { _boxDecoration = BoxDecoration( border: Border.all( color: Colors.green[600]!, width: 0.5, ), ); } return _boxDecoration; } /// todayBuilder向け。今日の罫線枠を赤にして、今日の日付を青丸文字にする AnimatedContainer _animatedContainerForToday(DateTime day) { return AnimatedContainer( duration: const Duration(milliseconds: 250), margin: EdgeInsets.zero, decoration: _boxDecoration(day), // 今日の日付を青丸文字にする child: AnimatedContainer ( duration: const Duration(milliseconds: 250), margin: const EdgeInsets.all(5), decoration: const BoxDecoration( color: Color(0xFF9FA8DA), shape: BoxShape.circle, ), alignment: Alignment.center, child: Text( day.day.toString(), style: const TextStyle( fontWeight: FontWeight.bold, color: Colors.white, ), ), ), ); } /// selectedBuilder向け。項目入力日及び選択日の罫線を赤にする。さらに、今日の日付を青丸文字にする AnimatedContainer _animatedContainerForSelect(DateTime day) { AnimatedContainer _animatedContainer; // 日付が今日の時 if (isSameDay(day, DateTime.now())) { _animatedContainer = _animatedContainerForToday(day); } else { // 日付が今日以外の時 _animatedContainer = AnimatedContainer( duration: const Duration(milliseconds: 250), margin: EdgeInsets.zero, decoration: BoxDecoration( border: Border.all( color: Colors.red[800]!, width: 3.0, ), ), alignment: Alignment.topCenter, child: Text( day.day.toString(), style: TextStyle( color: _textColor(day), ), ), ); } return _animatedContainer; } // 祝祭日XMLを読む final url = Uri.parse(Consts.myHttpUrl + '/js/holidays.xml'); HttpClientRequest request = await HttpClient().getUrl(url); HttpClientResponse response = await request.close(); // 祝祭日XMLのdateエレメントのみ抽出する final stream = response.transform(utf8.decoder) .toXmlEvents() .selectSubtreeEvents((event) => event.name == 'date'); // _eventsListに祝祭日を登録する await stream.forEachEvent(onText: (event) => _eventsList.addAll( {DateTime(int.parse((event.text).substring(0,4)), int.parse((event.text).substring(5,7)), int.parse((event.text).substring(8,10)),0,0): ['祝祭日']})); // _eventsListに登録した祝祭日を、eventsに登録する _events.addAll(_eventsList); // 日付入力ダイヤログを表示する var result = await showDialog<String>( context: context, barrierDismissible: false, builder: (BuildContext context) { return StatefulBuilder( builder: (context, setState) { return SimpleDialog( // title: Text('日付選択'), // 日付選択ダイヤログはタイトルを出さない children: <Widget>[ TableCalendar( locale: 'ja', // カレンダーの最初と最後の月を設定 firstDay: _firstDay, lastDay: _lastDay, // focusedDayによって表示月が決定される。 focusedDay: _focusedDay, // イベントを読み込む。祝祭日に赤丸”休”を表示 eventLoader: getEventForDay, // カレンダーフォーマットを月単位とする calendarFormat: CalendarFormat.month, // カレンダーフォーマット変更ボタンを表示しない headerStyle: const HeaderStyle( titleCentered: true, formatButtonVisible: false, ), // どの日が現在選択されているかを判断する。 // dayが選択されたら、trueを返す。 selectedDayPredicate: (day) { return isSameDay(_selectedDay, day); }, // 選択した日付を一時的に保存し、selectedDayPredicateオプションで // 保存された日付情報にフォーカスを当てて印が付けている。 onDaySelected: (selectedDay, focusedDay) { if (!isSameDay(_selectedDay, selectedDay)) { setState(() { _selectedDay = selectedDay; _focusedDay = focusedDay; }); } }, onPageChanged: (focusedDay) { _focusedDay = focusedDay; }, calendarBuilders: CalendarBuilders( /// 現在フォーカスされている月と一致しない日付部分を生成する outsideBuilder: ( BuildContext context, DateTime day, DateTime focusedDay) { return AnimatedContainer( duration: const Duration(milliseconds: 250), margin: EdgeInsets.zero, decoration: _boxDecoration(day), alignment: Alignment.topCenter, child: Text( day.day.toString(), style: const TextStyle( color: Colors.grey, ), ), ); }, /// 有効範囲(firstDay~lastDay)以外の日付部分を生成する disabledBuilder: ( BuildContext context, DateTime day, DateTime focusedDay) { return AnimatedContainer( duration: const Duration(milliseconds: 250), margin: EdgeInsets.zero, decoration: _boxDecoration(day), alignment: Alignment.topCenter, child: Text( day.day.toString(), style: const TextStyle( color: Colors.grey, ), ), ); }, /// デフォルトのセルを枠線のついたContainerに置き換える defaultBuilder: ( BuildContext context, DateTime day, DateTime focusedDay) { return AnimatedContainer( duration: const Duration(milliseconds: 250), margin: EdgeInsets.zero, decoration: _boxDecoration(day), alignment: Alignment.topCenter, child: Text( day.day.toString(), style: TextStyle( color: _textColor(day), ), ), ); }, /// 今日のセルを枠線のついたContainerに置き換える todayBuilder: ( BuildContext context, DateTime day, DateTime focusedDay) { return _animatedContainerForToday(day); }, /// 曜日のセルを枠線のついたContainerに置き換える dowBuilder: (BuildContext context, DateTime day) { final dowText = const DaysOfWeekStyle().dowTextFormatter?.call(day, 'ja') ?? DateFormat.E('ja').format(day); return Container( decoration: BoxDecoration( border: Border.all( width: 0.5, color: Colors.green[600]!, ), ), child: Center( child: Text( dowText, style: TextStyle( color: _textColor(day), ), ), ), ); }, // 選択されたセルを枠線のついたContainerに置き換える selectedBuilder: ( BuildContext context, DateTime day, DateTime focusedDay) { return _animatedContainerForSelect(day); }, /// イベントマーカーをmarkerBuilderを使用してカスタマイズする markerBuilder: (context, date, events) { if (events.isNotEmpty) { return _buildEventsMarker(date, events); } return null; }, ), ), Row( mainAxisAlignment: MainAxisAlignment.center, children:<Widget>[ SimpleDialogOption(child: const Text('OK'), onPressed: () { // 選択した日付を戻す。選択していないときは入力日付か、入力日付がnullの時は今日の日付を戻す。 Navigator.pop(context, _selectedDay==null?outputFormat.format(_originFocusedDay):outputFormat.format(_selectedDay!)); },), SimpleDialogOption(child: const Text('キャンセル'), onPressed: () { // ”cancel”を戻す Navigator.pop(context, Consts.cancel); },) ], ), ], ); } ); }, ); return result; }
日付選択ボタンを押下する。日付が空白だったため今日日付に青丸がつき、赤枠(選択済み)になる。
4月20日を選択する。4月20日が赤枠(選択済み)になる。
OKボタンを押下する。項目が4月20日になる。
日付選択ボタンを押下する。4月20日が赤枠(選択済み)になる。
4月18日を選択する。4月18日が赤枠(選択済み)になる。今日なので、青丸日付表示になる。
OKボタンを押下する。項目が4月18日になる。
■2022/06/15に、勉強した成果:『Flutter_JavaSpringプログラム自動作成◎自動生成ツール』をVectorに載せました。Zenn本も書きました。使ってみての感想や間違いの指定や、こうやったほうがいいとかの情報があればメールください。
・Vector
www.vector.co.jp
・Zenn本(Flutter_JavaSpringプログラム自動作成)
zenn.dev