overtime_detail_dialog.dart 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726
  1. // ignore_for_file: use_build_context_synchronously
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/services.dart';
  4. import 'package:tdesign_flutter/tdesign_flutter.dart';
  5. import '../../../core/i18n/app_localizations.dart';
  6. import '../../../core/theme/app_colors.dart';
  7. import '../../../core/theme/app_colors_extension.dart';
  8. /// 加班明细输入数据。
  9. class OvertimeDetailData {
  10. final String empCode;
  11. final String shiftType;
  12. final String startDate;
  13. final String endDate;
  14. final double approvedHours;
  15. final String isApproved;
  16. final String directOt;
  17. final double otQuantity;
  18. final String reason;
  19. final String handleMethod;
  20. final String chargeMethod;
  21. final String remark;
  22. final String isClosed;
  23. final String outBillNo;
  24. const OvertimeDetailData({
  25. required this.empCode,
  26. required this.shiftType,
  27. required this.startDate,
  28. required this.endDate,
  29. required this.approvedHours,
  30. required this.isApproved,
  31. required this.directOt,
  32. required this.otQuantity,
  33. required this.reason,
  34. required this.handleMethod,
  35. required this.chargeMethod,
  36. required this.remark,
  37. required this.isClosed,
  38. required this.outBillNo,
  39. });
  40. }
  41. /// 加班明细录入弹窗。
  42. class OvertimeDetailDialog extends StatefulWidget {
  43. final AppLocalizations l10n;
  44. final OvertimeDetailData? initialData;
  45. const OvertimeDetailDialog({super.key, required this.l10n, this.initialData});
  46. /// 显示弹窗,返回 [OvertimeDetailData] 或 `null`(取消时)。
  47. static Future<OvertimeDetailData?> show(
  48. BuildContext context, {
  49. required AppLocalizations l10n,
  50. OvertimeDetailData? initialData,
  51. }) {
  52. FocusScope.of(context).unfocus();
  53. return Navigator.push<OvertimeDetailData>(
  54. context,
  55. TDSlidePopupRoute<OvertimeDetailData>(
  56. slideTransitionFrom: SlideTransitionFrom.bottom,
  57. isDismissible: true,
  58. builder: (_) =>
  59. OvertimeDetailDialog(l10n: l10n, initialData: initialData),
  60. ),
  61. );
  62. }
  63. @override
  64. State<OvertimeDetailDialog> createState() => _OvertimeDetailDialogState();
  65. }
  66. class _OvertimeDetailDialogState extends State<OvertimeDetailDialog> {
  67. final _empCodeCtrl = TextEditingController();
  68. final _shiftTypeCtrl = TextEditingController();
  69. String _startDate = '';
  70. String _endDate = '';
  71. final _approvedHoursCtrl = TextEditingController();
  72. String _isApproved = 'N';
  73. String _directOt = 'N';
  74. final _reasonCtrl = TextEditingController();
  75. final _handleMethodCtrl = TextEditingController();
  76. final _chargeMethodCtrl = TextEditingController();
  77. final _remarkCtrl = TextEditingController();
  78. String _isClosed = 'N';
  79. final _outBillNoCtrl = TextEditingController();
  80. final _scrollCtrl = ScrollController();
  81. AppLocalizations get _l10n => widget.l10n;
  82. bool get _isEdit => widget.initialData != null;
  83. @override
  84. void initState() {
  85. super.initState();
  86. final d = widget.initialData;
  87. if (d != null) {
  88. _empCodeCtrl.text = d.empCode;
  89. _shiftTypeCtrl.text = d.shiftType;
  90. _startDate = d.startDate;
  91. _endDate = d.endDate;
  92. _approvedHoursCtrl.text = d.approvedHours > 0
  93. ? d.approvedHours.toString()
  94. : '';
  95. _isApproved = d.isApproved;
  96. _directOt = d.directOt;
  97. _reasonCtrl.text = d.reason;
  98. _handleMethodCtrl.text = d.handleMethod;
  99. _chargeMethodCtrl.text = d.chargeMethod;
  100. _remarkCtrl.text = d.remark;
  101. _isClosed = d.isClosed;
  102. _outBillNoCtrl.text = d.outBillNo;
  103. }
  104. }
  105. @override
  106. void dispose() {
  107. _empCodeCtrl.dispose();
  108. _shiftTypeCtrl.dispose();
  109. _approvedHoursCtrl.dispose();
  110. _reasonCtrl.dispose();
  111. _handleMethodCtrl.dispose();
  112. _chargeMethodCtrl.dispose();
  113. _remarkCtrl.dispose();
  114. _outBillNoCtrl.dispose();
  115. _scrollCtrl.dispose();
  116. super.dispose();
  117. }
  118. void _confirm() {
  119. if (_empCodeCtrl.text.trim().isEmpty) {
  120. TDToast.showText('请输入员工代号', context: context);
  121. return;
  122. }
  123. final otQuantity = double.tryParse(_approvedHoursCtrl.text) ?? 0;
  124. Navigator.pop(
  125. context,
  126. OvertimeDetailData(
  127. empCode: _empCodeCtrl.text.trim(),
  128. shiftType: _shiftTypeCtrl.text,
  129. startDate: _startDate,
  130. endDate: _endDate,
  131. approvedHours: otQuantity,
  132. isApproved: _isApproved,
  133. directOt: _directOt,
  134. otQuantity: otQuantity,
  135. reason: _reasonCtrl.text,
  136. handleMethod: _handleMethodCtrl.text,
  137. chargeMethod: _chargeMethodCtrl.text,
  138. remark: _remarkCtrl.text,
  139. isClosed: _isClosed,
  140. outBillNo: _outBillNoCtrl.text,
  141. ),
  142. );
  143. }
  144. @override
  145. Widget build(BuildContext context) {
  146. final colors = Theme.of(context).extension<AppColorsExtension>()!;
  147. return AnimatedPadding(
  148. padding: EdgeInsets.only(
  149. bottom: MediaQuery.of(context).viewInsets.bottom,
  150. ),
  151. duration: const Duration(milliseconds: 200),
  152. child: SafeArea(
  153. child: ConstrainedBox(
  154. constraints: BoxConstraints(
  155. maxHeight: MediaQuery.of(context).size.height * 0.85,
  156. ),
  157. child: Container(
  158. decoration: BoxDecoration(
  159. color: colors.bgPage,
  160. borderRadius: const BorderRadius.vertical(
  161. top: Radius.circular(16),
  162. ),
  163. ),
  164. child: Column(
  165. mainAxisSize: MainAxisSize.min,
  166. crossAxisAlignment: CrossAxisAlignment.stretch,
  167. children: [
  168. _buildHeader(colors),
  169. Flexible(
  170. child: GestureDetector(
  171. onTap: () => FocusScope.of(context).unfocus(),
  172. behavior: HitTestBehavior.translucent,
  173. child: SingleChildScrollView(
  174. controller: _scrollCtrl,
  175. keyboardDismissBehavior:
  176. ScrollViewKeyboardDismissBehavior.manual,
  177. padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
  178. child: Column(
  179. mainAxisSize: MainAxisSize.min,
  180. crossAxisAlignment: CrossAxisAlignment.stretch,
  181. children: [
  182. _inputCard(
  183. label: '员工代号',
  184. required: true,
  185. controller: _empCodeCtrl,
  186. hintText: '请输入',
  187. ),
  188. const SizedBox(height: 12),
  189. _inputCard(
  190. label: '班别',
  191. required: false,
  192. controller: _shiftTypeCtrl,
  193. hintText: '请输入',
  194. ),
  195. const SizedBox(height: 12),
  196. _datePickerCard(
  197. label: '开始日期',
  198. value: _startDate,
  199. hint: '请选择',
  200. colors: colors,
  201. onPick: (d) => setState(() => _startDate = d),
  202. onClear: _startDate.isNotEmpty
  203. ? () => setState(() => _startDate = '')
  204. : null,
  205. ),
  206. const SizedBox(height: 12),
  207. _datePickerCard(
  208. label: '结束日期',
  209. value: _endDate,
  210. hint: '请选择',
  211. colors: colors,
  212. onPick: (d) => setState(() => _endDate = d),
  213. onClear: _endDate.isNotEmpty
  214. ? () => setState(() => _endDate = '')
  215. : null,
  216. ),
  217. const SizedBox(height: 12),
  218. _inputCard(
  219. label: '加班核准量',
  220. required: false,
  221. controller: _approvedHoursCtrl,
  222. hintText: '0',
  223. keyboardType: const TextInputType.numberWithOptions(
  224. decimal: true,
  225. ),
  226. inputFormatters: [
  227. FilteringTextInputFormatter.allow(
  228. RegExp(r'^\d*\.?\d{0,2}$'),
  229. ),
  230. ],
  231. onChanged: () => setState(() {}),
  232. ),
  233. const SizedBox(height: 12),
  234. _toggleCard(
  235. label: '核准',
  236. value: _isApproved,
  237. options: const ['Y', 'N'],
  238. onChanged: (v) => setState(() => _isApproved = v),
  239. ),
  240. const SizedBox(height: 12),
  241. _toggleCard(
  242. label: '直接转加班',
  243. value: _directOt,
  244. options: const ['Y', 'N'],
  245. onChanged: (v) => setState(() => _directOt = v),
  246. ),
  247. const SizedBox(height: 12),
  248. // 加班数量(只读,=核准量)
  249. _readOnlyCard(
  250. label: '加班数量',
  251. value: _approvedHoursCtrl.text.isNotEmpty
  252. ? _approvedHoursCtrl.text
  253. : '0',
  254. ),
  255. const SizedBox(height: 12),
  256. _inputCard(
  257. label: '原因说明',
  258. required: false,
  259. controller: _reasonCtrl,
  260. hintText: '请输入',
  261. ),
  262. const SizedBox(height: 12),
  263. _inputCard(
  264. label: '处理方式',
  265. required: false,
  266. controller: _handleMethodCtrl,
  267. hintText: '请输入',
  268. ),
  269. const SizedBox(height: 12),
  270. _inputCard(
  271. label: '收费方式',
  272. required: false,
  273. controller: _chargeMethodCtrl,
  274. hintText: '请输入',
  275. ),
  276. const SizedBox(height: 12),
  277. _inputCard(
  278. label: '备注',
  279. required: false,
  280. controller: _remarkCtrl,
  281. hintText: '请输入',
  282. ),
  283. const SizedBox(height: 12),
  284. _toggleCard(
  285. label: '结案',
  286. value: _isClosed,
  287. options: const ['Y', 'N'],
  288. onChanged: (v) => setState(() => _isClosed = v),
  289. ),
  290. const SizedBox(height: 12),
  291. _inputCard(
  292. label: '转出单号',
  293. required: false,
  294. controller: _outBillNoCtrl,
  295. hintText: '请输入',
  296. ),
  297. ],
  298. ),
  299. ),
  300. ),
  301. ),
  302. Container(
  303. padding: const EdgeInsets.fromLTRB(16, 12, 16, 16),
  304. decoration: BoxDecoration(
  305. color: colors.bgCard,
  306. border: Border(
  307. top: BorderSide(color: colors.border, width: 0.5),
  308. ),
  309. ),
  310. child: Row(
  311. children: [
  312. Expanded(
  313. child: TDButton(
  314. text: _l10n.get('cancel'),
  315. size: TDButtonSize.large,
  316. type: TDButtonType.outline,
  317. shape: TDButtonShape.rectangle,
  318. theme: TDButtonTheme.defaultTheme,
  319. onTap: () => Navigator.pop(context),
  320. ),
  321. ),
  322. const SizedBox(width: 12),
  323. Expanded(
  324. child: TDButton(
  325. text: _isEdit ? '确认修改' : '添加',
  326. size: TDButtonSize.large,
  327. type: TDButtonType.fill,
  328. shape: TDButtonShape.rectangle,
  329. theme: TDButtonTheme.primary,
  330. onTap: _confirm,
  331. ),
  332. ),
  333. ],
  334. ),
  335. ),
  336. ],
  337. ),
  338. ),
  339. ),
  340. ),
  341. );
  342. }
  343. // ── 标题栏 ──
  344. Widget _buildHeader(AppColorsExtension colors) {
  345. return Column(
  346. mainAxisSize: MainAxisSize.min,
  347. children: [
  348. Center(
  349. child: Container(
  350. margin: const EdgeInsets.only(top: 8, bottom: 4),
  351. width: 36,
  352. height: 4,
  353. decoration: BoxDecoration(
  354. color: colors.border,
  355. borderRadius: BorderRadius.circular(2),
  356. ),
  357. ),
  358. ),
  359. Padding(
  360. padding: const EdgeInsets.fromLTRB(20, 8, 12, 16),
  361. child: Row(
  362. children: [
  363. const SizedBox(width: 24),
  364. Expanded(
  365. child: Center(
  366. child: Text(
  367. '加班明细',
  368. style: TextStyle(
  369. fontSize: AppFontSizes.title,
  370. fontWeight: FontWeight.w600,
  371. color: colors.textPrimary,
  372. ),
  373. ),
  374. ),
  375. ),
  376. GestureDetector(
  377. onTap: () => Navigator.pop(context),
  378. child: Padding(
  379. padding: const EdgeInsets.all(4),
  380. child: Icon(
  381. Icons.close,
  382. size: 20,
  383. color: colors.textSecondary,
  384. ),
  385. ),
  386. ),
  387. ],
  388. ),
  389. ),
  390. ],
  391. );
  392. }
  393. // ── 通用输入卡片 ──
  394. Widget _inputCard({
  395. required String label,
  396. required bool required,
  397. required TextEditingController controller,
  398. required String hintText,
  399. TextInputType? keyboardType,
  400. List<TextInputFormatter>? inputFormatters,
  401. VoidCallback? onChanged,
  402. }) {
  403. final tdTheme = TDTheme.of(context);
  404. final hasValue = controller.text.isNotEmpty;
  405. return Container(
  406. padding: const EdgeInsets.only(left: 16, right: 10, top: 12, bottom: 12),
  407. decoration: BoxDecoration(
  408. color: tdTheme.bgColorContainer,
  409. borderRadius: BorderRadius.circular(tdTheme.radiusDefault),
  410. border: Border.all(color: tdTheme.componentStrokeColor),
  411. ),
  412. child: Row(
  413. children: [
  414. TDText(
  415. label,
  416. maxLines: 1,
  417. overflow: TextOverflow.visible,
  418. font: tdTheme.fontBodyLarge,
  419. fontWeight: FontWeight.w400,
  420. style: const TextStyle(letterSpacing: 0),
  421. ),
  422. if (required)
  423. Padding(
  424. padding: const EdgeInsets.only(left: 4),
  425. child: TDText(
  426. '*',
  427. font: tdTheme.fontBodyLarge,
  428. fontWeight: FontWeight.w400,
  429. style: TextStyle(color: tdTheme.errorColor6),
  430. ),
  431. ),
  432. const SizedBox(width: 12),
  433. Expanded(
  434. child: Row(
  435. mainAxisAlignment: MainAxisAlignment.end,
  436. mainAxisSize: MainAxisSize.max,
  437. children: [
  438. Flexible(
  439. child: TextField(
  440. controller: controller,
  441. textAlign: TextAlign.end,
  442. keyboardType: keyboardType,
  443. inputFormatters: inputFormatters,
  444. style: TextStyle(
  445. fontSize: 16,
  446. color: tdTheme.textColorPrimary,
  447. ),
  448. decoration: InputDecoration(
  449. hintText: hintText,
  450. hintStyle: TextStyle(
  451. fontSize: 16,
  452. color: tdTheme.textColorPlaceholder,
  453. ),
  454. border: InputBorder.none,
  455. isDense: true,
  456. contentPadding: EdgeInsets.zero,
  457. ),
  458. onChanged: (_) => setState(() {}),
  459. ),
  460. ),
  461. const SizedBox(width: 4),
  462. SizedBox(
  463. width: 18,
  464. height: 18,
  465. child: hasValue
  466. ? GestureDetector(
  467. onTap: () {
  468. controller.clear();
  469. onChanged?.call();
  470. setState(() {});
  471. },
  472. child: Icon(
  473. Icons.close,
  474. size: 18,
  475. color: tdTheme.textColorPlaceholder,
  476. ),
  477. )
  478. : null,
  479. ),
  480. ],
  481. ),
  482. ),
  483. ],
  484. ),
  485. );
  486. }
  487. // ── 日期 Picker 卡片 ──
  488. Widget _datePickerCard({
  489. required String label,
  490. required String value,
  491. required String hint,
  492. required AppColorsExtension colors,
  493. required ValueChanged<String> onPick,
  494. VoidCallback? onClear,
  495. }) {
  496. final tdTheme = TDTheme.of(context);
  497. final hasValue = value.isNotEmpty;
  498. return GestureDetector(
  499. onTap: () => _pickDate(onPick),
  500. child: Container(
  501. padding: const EdgeInsets.only(
  502. left: 16,
  503. right: 10,
  504. top: 12,
  505. bottom: 12,
  506. ),
  507. decoration: BoxDecoration(
  508. color: tdTheme.bgColorContainer,
  509. borderRadius: BorderRadius.circular(tdTheme.radiusDefault),
  510. border: Border.all(color: tdTheme.componentStrokeColor),
  511. ),
  512. child: Row(
  513. children: [
  514. TDText(
  515. label,
  516. maxLines: 1,
  517. overflow: TextOverflow.visible,
  518. font: tdTheme.fontBodyLarge,
  519. fontWeight: FontWeight.w400,
  520. style: const TextStyle(letterSpacing: 0),
  521. ),
  522. const SizedBox(width: 12),
  523. Expanded(
  524. child: Row(
  525. mainAxisAlignment: MainAxisAlignment.end,
  526. mainAxisSize: MainAxisSize.max,
  527. children: [
  528. Flexible(
  529. child: TDText(
  530. value.isEmpty ? hint : value,
  531. maxLines: 1,
  532. overflow: TextOverflow.ellipsis,
  533. font: tdTheme.fontBodyLarge,
  534. fontWeight: FontWeight.w400,
  535. textColor: value.isEmpty
  536. ? tdTheme.textColorPlaceholder
  537. : tdTheme.textColorPrimary,
  538. textAlign: TextAlign.end,
  539. ),
  540. ),
  541. const SizedBox(width: 4),
  542. SizedBox(
  543. width: 18,
  544. height: 18,
  545. child: hasValue
  546. ? GestureDetector(
  547. onTap: onClear,
  548. child: Icon(
  549. Icons.close,
  550. size: 18,
  551. color: tdTheme.textColorPlaceholder,
  552. ),
  553. )
  554. : Icon(
  555. Icons.chevron_right,
  556. size: 18,
  557. color: tdTheme.textColorPlaceholder,
  558. ),
  559. ),
  560. ],
  561. ),
  562. ),
  563. ],
  564. ),
  565. ),
  566. );
  567. }
  568. // ── 切换卡片(Y/N) ──
  569. Widget _toggleCard({
  570. required String label,
  571. required String value,
  572. required List<String> options,
  573. required ValueChanged<String> onChanged,
  574. }) {
  575. final tdTheme = TDTheme.of(context);
  576. final colors = Theme.of(context).extension<AppColorsExtension>()!;
  577. return Container(
  578. padding: const EdgeInsets.only(left: 16, right: 10, top: 10, bottom: 10),
  579. decoration: BoxDecoration(
  580. color: tdTheme.bgColorContainer,
  581. borderRadius: BorderRadius.circular(tdTheme.radiusDefault),
  582. border: Border.all(color: tdTheme.componentStrokeColor),
  583. ),
  584. child: Row(
  585. children: [
  586. TDText(
  587. label,
  588. maxLines: 1,
  589. overflow: TextOverflow.visible,
  590. font: tdTheme.fontBodyLarge,
  591. fontWeight: FontWeight.w400,
  592. style: const TextStyle(letterSpacing: 0),
  593. ),
  594. const Spacer(),
  595. ...options.map((opt) {
  596. final sel = value == opt;
  597. return Padding(
  598. padding: const EdgeInsets.only(left: 8),
  599. child: GestureDetector(
  600. onTap: () => onChanged(opt),
  601. child: Container(
  602. padding: const EdgeInsets.symmetric(
  603. horizontal: 12,
  604. vertical: 6,
  605. ),
  606. decoration: BoxDecoration(
  607. color: sel
  608. ? colors.primary.withValues(alpha: 0.1)
  609. : tdTheme.bgColorContainer,
  610. borderRadius: BorderRadius.circular(tdTheme.radiusDefault),
  611. border: Border.all(
  612. color: sel
  613. ? colors.primary
  614. : tdTheme.componentStrokeColor,
  615. ),
  616. ),
  617. child: Text(
  618. opt == 'Y' ? '是' : '否',
  619. style: TextStyle(
  620. fontSize: 14,
  621. color: sel ? colors.primary : tdTheme.textColorPrimary,
  622. ),
  623. ),
  624. ),
  625. ),
  626. );
  627. }),
  628. ],
  629. ),
  630. );
  631. }
  632. // ── 只读卡片 ──
  633. Widget _readOnlyCard({required String label, required String value}) {
  634. final tdTheme = TDTheme.of(context);
  635. return Container(
  636. padding: const EdgeInsets.only(left: 16, right: 10, top: 12, bottom: 12),
  637. decoration: BoxDecoration(
  638. color: tdTheme.bgColorContainer,
  639. borderRadius: BorderRadius.circular(tdTheme.radiusDefault),
  640. border: Border.all(color: tdTheme.componentStrokeColor),
  641. ),
  642. child: Row(
  643. children: [
  644. TDText(
  645. label,
  646. maxLines: 1,
  647. overflow: TextOverflow.visible,
  648. font: tdTheme.fontBodyLarge,
  649. fontWeight: FontWeight.w400,
  650. style: const TextStyle(letterSpacing: 0),
  651. ),
  652. const SizedBox(width: 12),
  653. Expanded(
  654. child: Row(
  655. mainAxisAlignment: MainAxisAlignment.end,
  656. mainAxisSize: MainAxisSize.max,
  657. children: [
  658. Flexible(
  659. child: TDText(
  660. value,
  661. maxLines: 1,
  662. overflow: TextOverflow.ellipsis,
  663. font: tdTheme.fontBodyLarge,
  664. fontWeight: FontWeight.w400,
  665. textColor: tdTheme.textColorPrimary,
  666. textAlign: TextAlign.end,
  667. ),
  668. ),
  669. ],
  670. ),
  671. ),
  672. ],
  673. ),
  674. );
  675. }
  676. void _pickDate(ValueChanged<String> onPick) {
  677. final l10n = AppLocalizations.of(context);
  678. final colors = Theme.of(context).extension<AppColorsExtension>()!;
  679. final now = DateTime.now();
  680. FocusManager.instance.primaryFocus?.unfocus();
  681. TDPicker.showDatePicker(
  682. context,
  683. title: l10n.get('selectDate'),
  684. backgroundColor: colors.bgCard,
  685. useYear: true,
  686. useMonth: true,
  687. useDay: true,
  688. useHour: false,
  689. useMinute: false,
  690. useSecond: false,
  691. useWeekDay: false,
  692. dateStart: const [2020, 1, 1],
  693. dateEnd: [now.year + 1, 12, 31],
  694. initialDate: [now.year, now.month, now.day],
  695. onConfirm: (selected) {
  696. onPick(
  697. '${selected['year']}-${selected['month'].toString().padLeft(2, '0')}-${selected['day'].toString().padLeft(2, '0')}',
  698. );
  699. Navigator.of(context).pop();
  700. },
  701. );
  702. }
  703. }