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_design.dart';
5 : import 'package:app_finance/_classes/herald/app_locale.dart';
6 : import 'package:app_finance/_classes/structure/currency/exchange.dart';
7 : import 'package:app_finance/_classes/structure/goal_app_data.dart';
8 : import 'package:app_finance/_classes/controller/focus_controller.dart';
9 : import 'package:app_finance/_classes/storage/app_data.dart';
10 : import 'package:app_finance/_configs/theme_helper.dart';
11 : import 'package:app_finance/_classes/storage/app_preferences.dart';
12 : import 'package:app_finance/design/wrapper/input_wrapper.dart';
13 : import 'package:app_finance/pages/_interfaces/abstract_page_state.dart';
14 : import 'package:app_finance/design/button/full_sized_button_widget.dart';
15 : import 'package:app_finance/design/form/simple_input.dart';
16 : import 'package:app_finance/design/wrapper/row_widget.dart';
17 : import 'package:app_finance/design/wrapper/single_scroll_wrapper.dart';
18 : import 'package:flutter/material.dart';
19 : import 'package:flutter_currency_picker/flutter_currency_picker.dart';
20 :
21 : class GoalAddPage extends StatefulWidget {
22 : final String? title;
23 : final IconData? icon;
24 : final MaterialColor? color;
25 : final Currency? currency;
26 : final double? details;
27 : final DateTime? closedAt;
28 :
29 5 : const GoalAddPage({
30 : super.key,
31 : this.title,
32 : this.icon,
33 : this.color,
34 : this.currency,
35 : this.details,
36 : this.closedAt,
37 : });
38 :
39 0 : @override
40 0 : GoalAddPageState createState() => GoalAddPageState();
41 : }
42 :
43 : class GoalAddPageState<T extends GoalAddPage> extends AbstractPageState<GoalAddPage> {
44 : late FocusController focus;
45 : late TextEditingController title;
46 : late TextEditingController details;
47 : IconData? icon;
48 : MaterialColor? color;
49 : Currency? currency;
50 : DateTime? closedAt;
51 : bool hasError = false;
52 :
53 0 : @override
54 : void initState() {
55 0 : focus = FocusController();
56 0 : title = TextEditingController(text: widget.title);
57 0 : icon = widget.icon;
58 0 : color = widget.color;
59 0 : details = TextEditingController(text: widget.details != null ? widget.details.toString() : '');
60 0 : final currencyId = AppPreferences.get(AppPreferences.prefCurrency);
61 0 : currency = widget.currency ?? CurrencyProvider.find(currencyId);
62 0 : closedAt = widget.closedAt;
63 0 : super.initState();
64 : }
65 :
66 0 : @override
67 : void dispose() {
68 0 : focus.dispose();
69 0 : title.dispose();
70 0 : details.dispose();
71 0 : super.dispose();
72 : }
73 :
74 0 : @override
75 0 : String getTitle() => AppLocale.labels.createGoalHeader;
76 :
77 0 : bool hasFormErrors() {
78 0 : setState(() => hasError = title.text.isEmpty || closedAt == null);
79 0 : return hasError;
80 : }
81 :
82 0 : void updateStorage() {
83 0 : if (currency != null) {
84 0 : CurrencyProvider.pin(currency!);
85 : }
86 0 : state.add(GoalAppData(
87 0 : title: title.text,
88 0 : initial: Exchange(store: state).reform(state.getTotal(AppDataType.accounts), Exchange.defaultCurrency, currency),
89 : progress: 0.0,
90 0 : color: color,
91 : hidden: false,
92 0 : currency: currency,
93 0 : icon: icon,
94 0 : details: double.tryParse(details.text) ?? 0.0,
95 0 : closedAt: closedAt,
96 : ));
97 : }
98 :
99 0 : @override
100 0 : String getButtonName() => AppLocale.labels.createGoalTooltip;
101 :
102 0 : @override
103 : Widget buildButton(BuildContext context, BoxConstraints constraints) {
104 0 : NavigatorState nav = Navigator.of(context);
105 0 : return FullSizedButtonWidget(
106 : constraints: constraints,
107 0 : controller: focus,
108 0 : onPressed: () => {
109 0 : setState(() {
110 0 : if (hasFormErrors()) {
111 : return;
112 : }
113 0 : updateStorage();
114 0 : nav.pop();
115 : })
116 : },
117 0 : title: getButtonName(),
118 : icon: Icons.save,
119 : );
120 : }
121 :
122 0 : @override
123 : Widget buildContent(BuildContext context, BoxConstraints constraints) {
124 0 : double indent = ThemeHelper.getIndent(2);
125 0 : double width = ThemeHelper.getWidth(context, 6, constraints);
126 :
127 0 : return SingleScrollWrapper(
128 0 : controller: focus,
129 0 : child: Container(
130 0 : margin: EdgeInsets.fromLTRB(indent, indent, indent, 240),
131 0 : child: Column(
132 0 : crossAxisAlignment: AppDesign.getAlignment(),
133 0 : children: [
134 0 : InputWrapper.text(
135 0 : title: AppLocale.labels.titleGoal,
136 0 : showError: hasError && title.text.isEmpty,
137 0 : controller: title,
138 : isRequired: true,
139 0 : tooltip: AppLocale.labels.titleGoalTooltip,
140 : ),
141 0 : RowWidget(
142 : indent: indent,
143 0 : maxWidth: width + indent,
144 0 : alignment: AppDesign.getAlignment<MainAxisAlignment>(),
145 : chunk: const [null, null],
146 0 : children: [
147 0 : [
148 0 : InputWrapper.icon(
149 0 : title: AppLocale.labels.icon,
150 0 : value: icon,
151 0 : onChange: (value) => setState(() => icon = value),
152 : ),
153 : ],
154 0 : [
155 0 : InputWrapper.color(
156 0 : title: AppLocale.labels.color,
157 0 : value: color,
158 0 : onChange: (value) => setState(() => color = value),
159 : ),
160 : ],
161 : ],
162 : ),
163 0 : RowWidget(
164 : indent: indent,
165 0 : maxWidth: width + indent,
166 0 : alignment: AppDesign.getAlignment<MainAxisAlignment>(),
167 : chunk: const [90, null],
168 0 : children: [
169 0 : [
170 0 : InputWrapper.currency(
171 0 : title: AppLocale.labels.currency,
172 : type: NamedInputType.currencyShort,
173 0 : value: currency,
174 0 : tooltip: AppLocale.labels.currencyTooltip,
175 0 : onChange: (value) => setState(() => currency = value),
176 : ),
177 : ],
178 0 : [
179 0 : InputWrapper.text(
180 0 : title: AppLocale.labels.targetAmount,
181 0 : controller: details,
182 : inputType: const TextInputType.numberWithOptions(decimal: true),
183 0 : tooltip: AppLocale.labels.billSetTooltip,
184 0 : formatter: [
185 0 : SimpleInputFormatter.filterDouble,
186 : ],
187 : ),
188 : ],
189 : ],
190 : ),
191 0 : InputWrapper(
192 0 : title: AppLocale.labels.closedAt,
193 : type: NamedInputType.dateSelector,
194 0 : showError: hasError && closedAt == null,
195 0 : value: closedAt,
196 0 : onChange: (value) => setState(() => closedAt = value),
197 : ),
198 : ],
199 : ),
200 : ),
201 : );
202 : }
203 : }
|