Line data Source code
1 : // Copyright 2023 The terCAD team. All rights reserved.
2 : // Use of this source code is governed by a CC BY-NC-ND 4.0 license that can be found in the LICENSE file.
3 :
4 : import 'package:app_finance/_classes/herald/app_locale.dart';
5 : import 'package:app_finance/_configs/custom_color_scheme.dart';
6 : import 'package:app_finance/_configs/custom_text_theme.dart';
7 : import 'package:app_finance/_configs/theme_helper.dart';
8 : import 'package:app_finance/_ext/build_context_ext.dart';
9 : import 'package:app_finance/design/form/abstract_selector.dart';
10 : import 'package:app_finance/design/form/date_input.dart';
11 : import 'package:app_finance/design/wrapper/row_widget.dart';
12 : import 'package:material_ui/material_ui.dart';
13 : import 'package:intl/intl.dart';
14 :
15 : class DateTimeInput extends AbstractSelector {
16 : final Function setState;
17 : @override
18 : // ignore: overridden_fields
19 : final DateTime value;
20 : final double? width;
21 :
22 2 : const DateTimeInput({
23 : super.key,
24 : required this.setState,
25 : required this.value,
26 : this.width,
27 2 : }) : super(value: value);
28 :
29 2 : @override
30 2 : DateTimeInputState createState() => DateTimeInputState();
31 : }
32 :
33 : class DateTimeInputState extends AbstractSelectorState<DateTimeInput> {
34 0 : Future<TimeOfDay?> _showCompactTimePicker(BuildContext context) {
35 0 : final initial = TimeOfDay.fromDateTime(widget.value);
36 0 : final indent = ThemeHelper.getIndent(2);
37 0 : return showModalBottomSheet<TimeOfDay>(
38 : context: context,
39 : isScrollControlled: true,
40 : useSafeArea: true,
41 : shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero),
42 0 : builder: (sheetContext) {
43 0 : int hour = initial.hour;
44 0 : int minute = initial.minute;
45 0 : return StatefulBuilder(
46 0 : builder: (context, setModalState) {
47 0 : return Padding(
48 0 : padding: EdgeInsets.fromLTRB(
49 : indent,
50 : indent,
51 : indent,
52 0 : MediaQuery.of(context).viewInsets.bottom + indent,
53 : ),
54 0 : child: ConstrainedBox(
55 : constraints: const BoxConstraints(maxWidth: 420),
56 0 : child: Column(
57 : mainAxisSize: MainAxisSize.min,
58 : crossAxisAlignment: CrossAxisAlignment.stretch,
59 0 : children: [
60 0 : Text(
61 0 : AppLocale.labels.balanceDate,
62 0 : style: sheetContext.textTheme.titleMedium,
63 : ),
64 0 : SizedBox(height: indent),
65 0 : Row(
66 0 : children: [
67 0 : Expanded(
68 0 : child: DropdownButtonFormField<int>(
69 : initialValue: hour,
70 0 : decoration: InputDecoration(
71 0 : labelText: AppLocale.labels.hour,
72 : filled: true,
73 : border: InputBorder.none,
74 0 : fillColor: context.colorScheme.fieldBackground,
75 : ),
76 0 : items: List<DropdownMenuItem<int>>.generate(
77 : 24,
78 0 : (index) => DropdownMenuItem<int>(
79 : value: index,
80 0 : child: Text(index.toString().padLeft(2, '0')),
81 : ),
82 : ),
83 0 : onChanged: (value) {
84 : if (value != null) {
85 0 : setModalState(() => hour = value);
86 : }
87 : },
88 : ),
89 : ),
90 0 : SizedBox(width: indent),
91 0 : Expanded(
92 0 : child: DropdownButtonFormField<int>(
93 : initialValue: minute,
94 0 : decoration: InputDecoration(
95 0 : labelText: AppLocale.labels.minute,
96 : filled: true,
97 : border: InputBorder.none,
98 0 : fillColor: context.colorScheme.fieldBackground,
99 : ),
100 0 : items: List<DropdownMenuItem<int>>.generate(
101 : 60,
102 0 : (index) => DropdownMenuItem<int>(
103 : value: index,
104 0 : child: Text(index.toString().padLeft(2, '0')),
105 : ),
106 : ),
107 0 : onChanged: (value) {
108 : if (value != null) {
109 0 : setModalState(() => minute = value);
110 : }
111 : },
112 : ),
113 : ),
114 : ],
115 : ),
116 0 : SizedBox(height: indent),
117 0 : Row(
118 : mainAxisAlignment: MainAxisAlignment.end,
119 0 : children: [
120 0 : TextButton(
121 0 : onPressed: () => Navigator.of(sheetContext).pop(),
122 0 : child: Text(AppLocale.labels.btnCancel),
123 : ),
124 0 : SizedBox(width: indent),
125 0 : FilledButton(
126 0 : style: FilledButton.styleFrom(foregroundColor: sheetContext.colorScheme.surface),
127 0 : onPressed: () => Navigator.of(sheetContext).pop(TimeOfDay(hour: hour, minute: minute)),
128 0 : child: Text(AppLocale.labels.btnConfirm),
129 : ),
130 : ],
131 : ),
132 : ],
133 : ),
134 : ),
135 : );
136 : },
137 : );
138 : },
139 : );
140 : }
141 :
142 0 : @override
143 : Future<void> onTap(BuildContext context) async {
144 0 : FocusManager.instance.primaryFocus?.unfocus();
145 0 : final time = await _showCompactTimePicker(context);
146 : if (time != null) {
147 0 : widget.setState(DateTime(widget.value.year, widget.value.month, widget.value.day, time.hour, time.minute));
148 0 : focusController.onEditingComplete(this);
149 : }
150 : }
151 :
152 2 : @override
153 : Widget buildContent(BuildContext context) {
154 2 : double indent = ThemeHelper.getIndent(2);
155 4 : double width = widget.width ?? ThemeHelper.getWidth(context, 4);
156 2 : final DateFormat formatterTime = DateFormat.Hms(AppLocale.code);
157 2 : final textTheme = context.textTheme;
158 :
159 2 : return RowWidget(
160 : indent: indent,
161 2 : maxWidth: width + indent,
162 : chunk: const [0.6, 0.4],
163 2 : children: [
164 2 : [
165 2 : DateInput(
166 4 : value: widget.value,
167 4 : setState: widget.setState,
168 : ),
169 : ],
170 2 : [
171 2 : Material(
172 4 : color: context.colorScheme.fieldBackground,
173 2 : child: ListTile(
174 2 : focusNode: focus,
175 4 : autofocus: focusController.isFocused(this),
176 2 : title: Text(
177 6 : formatterTime.format(widget.value),
178 : overflow: TextOverflow.ellipsis,
179 2 : style: textTheme.numberMedium,
180 : ),
181 0 : onTap: () => onTap(context),
182 : ),
183 : ),
184 : ],
185 : ],
186 : );
187 : }
188 : }
|