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/controller/focus_controller.dart';
5 : import 'package:app_finance/_classes/herald/app_design.dart';
6 : import 'package:app_finance/_classes/herald/app_locale.dart';
7 : import 'package:app_finance/_classes/herald/app_palette.dart';
8 : import 'package:app_finance/_classes/herald/app_theme.dart';
9 : import 'package:app_finance/_classes/herald/app_zoom.dart';
10 : import 'package:app_finance/_classes/storage/app_data.dart';
11 : import 'package:app_finance/_classes/storage/app_preferences.dart';
12 : import 'package:app_finance/_classes/structure/currency/exchange.dart';
13 : import 'package:app_finance/_configs/design_type.dart';
14 : import 'package:app_finance/design/form/list_selector_item.dart';
15 : import 'package:app_finance/_configs/custom_color_scheme.dart';
16 : import 'package:app_finance/_configs/theme_helper.dart';
17 : import 'package:app_finance/_ext/build_context_ext.dart';
18 : import 'package:app_finance/_ext/color_ext.dart';
19 : import 'package:app_finance/design/wrapper/input_wrapper.dart';
20 : import 'package:app_finance/l10n/index.dart';
21 : import 'package:app_finance/design/button/link_widget.dart';
22 : import 'package:app_finance/design/form/color_selector.dart';
23 : import 'package:app_finance/pages/start/widgets/abstract_tab.dart';
24 : import 'package:app_finance/design/wrapper/row_widget.dart';
25 : import 'package:app_finance/design/wrapper/single_scroll_wrapper.dart';
26 : import 'package:app_finance/design/wrapper/table_widget.dart';
27 : import 'package:flutter/foundation.dart';
28 : import 'package:flutter/material.dart';
29 : import 'package:flutter_currency_picker/flutter_currency_picker.dart';
30 : import 'package:intl/intl.dart';
31 : import 'package:provider/provider.dart';
32 :
33 : class SettingTab extends AbstractTab {
34 5 : const SettingTab({
35 : super.key,
36 : required super.setState,
37 : required super.setButton,
38 : super.isFirstBoot = true,
39 : });
40 :
41 0 : @override
42 0 : SettingTabState createState() => SettingTabState();
43 : }
44 :
45 : class SettingTabState<T extends SettingTab> extends AbstractTabState<T> {
46 : late FocusController controller;
47 :
48 : late AppTheme theme;
49 : late AppZoom zoom;
50 : late AppPalette palette;
51 : late AppLocale locale;
52 : late AppDesign design;
53 : late AppData state;
54 :
55 : Currency? currency;
56 : bool isEncrypted = false;
57 : bool hasEncrypted = false;
58 : String brightness = '0';
59 : String colorMode = AppPalette.state;
60 :
61 : var paletteState = (
62 : light: AppDefaultColors.fromString(AppPalette.light),
63 : dark: AppDefaultColors.fromString(AppPalette.dark),
64 : );
65 :
66 : final languages = languageList.map((e) => ListSelectorItem(id: e.id, name: e.name)).toList();
67 :
68 0 : @override
69 : void initState() {
70 0 : super.initState();
71 0 : controller = FocusController();
72 0 : final doEncrypt = AppPreferences.get(AppPreferences.prefDoEncrypt);
73 0 : hasEncrypted = doEncrypt != null;
74 0 : isEncrypted = doEncrypt == 'true' || doEncrypt == null;
75 0 : AppPreferences.set(AppPreferences.prefDoEncrypt, isEncrypted ? 'true' : 'false');
76 0 : brightness = AppPreferences.get(AppPreferences.prefTheme) ?? brightness;
77 0 : colorMode = AppPreferences.get(AppPreferences.prefColor) ?? colorMode;
78 0 : currency = CurrencyProvider.find(AppPreferences.get(AppPreferences.prefCurrency) ?? '');
79 : }
80 :
81 0 : @override
82 : void dispose() {
83 0 : controller.dispose();
84 0 : super.dispose();
85 : }
86 :
87 0 : void saveEncryption(newValue) {
88 0 : if (hasEncrypted) {
89 : return;
90 : }
91 0 : setState(() => isEncrypted = newValue);
92 0 : AppPreferences.set(AppPreferences.prefDoEncrypt, isEncrypted ? 'true' : 'false');
93 : }
94 :
95 0 : Future<void> saveCurrency(Currency? value) async {
96 : if (value != null) {
97 0 : await AppPreferences.set(AppPreferences.prefCurrency, value.code);
98 : Exchange.defaultCurrency = value;
99 : CurrencyDefaults.defaultCurrency = value;
100 0 : await state.restate();
101 0 : setState(() => currency = value);
102 : }
103 : }
104 :
105 0 : Future<void> saveTheme(String value) async {
106 0 : await theme.setTheme(value);
107 0 : setState(() => brightness = value);
108 : }
109 :
110 0 : Future<void> saveColor(String value) async {
111 0 : await palette.setMode(value);
112 0 : setState(() => colorMode = value);
113 : }
114 :
115 0 : Future<void> saveLocale(String value) async {
116 0 : await locale.set(value);
117 0 : await design.set(AppDesign.fromLocale(AppLocale.fromCode(value)));
118 0 : setState(() {});
119 : }
120 :
121 0 : Future<void> saveDesign(String value) async {
122 0 : await design.set(value);
123 0 : setState(() {});
124 : }
125 :
126 0 : Future<void> changePalette(_) async {
127 0 : await palette.set(paletteState.light, paletteState.dark);
128 0 : setState(() => paletteState);
129 : }
130 :
131 0 : Future<void> initCurrencyFromLocale(String locale) async {
132 0 : final format = NumberFormat.simpleCurrency(locale: locale);
133 0 : final code = format.currencyName ?? 'EUR';
134 0 : await AppPreferences.set(AppPreferences.prefCurrency, code);
135 0 : setState(() => currency = CurrencyProvider.find(code));
136 0 : Exchange.defaultCurrency = currency;
137 0 : CurrencyDefaults.defaultCurrency = currency;
138 : }
139 :
140 0 : @override
141 0 : String getButtonTitle() => AppLocale.labels.saveSettingsTooltip;
142 :
143 0 : @override
144 : Widget buildContent(BuildContext context, BoxConstraints constraints) {
145 0 : theme = Provider.of<AppTheme>(context, listen: false);
146 0 : zoom = Provider.of<AppZoom>(context, listen: false);
147 0 : palette = Provider.of<AppPalette>(context, listen: false);
148 0 : locale = Provider.of<AppLocale>(context, listen: false);
149 0 : design = Provider.of<AppDesign>(context, listen: false);
150 0 : state = Provider.of<AppData>(context, listen: true);
151 0 : final textTheme = context.textTheme;
152 0 : final indent = ThemeHelper.getIndent(2);
153 0 : final width = ThemeHelper.getMaxWidth(context, constraints) - 2 * indent;
154 0 : if (currency == null) {
155 0 : WidgetsBinding.instance.addPostFrameCallback((_) => initCurrencyFromLocale(AppLocale.code));
156 : return ThemeHelper.emptyBox;
157 : }
158 0 : return SingleScrollWrapper(
159 0 : controller: controller,
160 0 : child: Column(
161 0 : crossAxisAlignment: AppDesign.getAlignment(),
162 0 : children: [
163 : ThemeHelper.hIndent2x,
164 0 : InputWrapper.select(
165 0 : title: AppLocale.labels.language,
166 0 : tooltip: AppLocale.labels.language,
167 0 : value: AppLocale.code,
168 0 : options: languages,
169 0 : onChange: (v) => saveLocale(v ?? AppLocale.code),
170 : ),
171 0 : InputWrapper.select(
172 0 : title: AppLocale.labels.design,
173 0 : tooltip: AppLocale.labels.design,
174 0 : value: AppDesign.get().name,
175 0 : options: [
176 0 : ListSelectorItem(id: AppDesignType.global.name, name: AppLocale.labels.designGlobal),
177 0 : ListSelectorItem(id: AppDesignType.asiaGeneral.name, name: AppLocale.labels.designAsiaGeneral),
178 0 : ListSelectorItem(id: AppDesignType.rtlGeneral.name, name: AppLocale.labels.designRtlGeneral),
179 0 : ListSelectorItem(id: AppDesignType.germany.name, name: AppLocale.labels.designGermany),
180 0 : ].cast<ListSelectorItem>(),
181 0 : onChange: (v) => saveDesign(AppDesign.find(v)?.name ?? AppDesignType.global.name),
182 : ),
183 0 : InputWrapper.currency(
184 0 : title: AppLocale.labels.currencyDefault,
185 0 : tooltip: AppLocale.labels.currencyDefault,
186 0 : value: currency,
187 0 : onChange: saveCurrency,
188 : ),
189 0 : if (kDebugMode) ...[
190 0 : Row(
191 0 : mainAxisAlignment: AppDesign.getAlignment<MainAxisAlignment>(),
192 0 : children: [
193 0 : Text(
194 0 : AppLocale.labels.encryptionMode,
195 0 : style: textTheme.bodyLarge,
196 : ),
197 0 : Switch(
198 0 : value: isEncrypted,
199 0 : onChanged: saveEncryption,
200 : ),
201 0 : Expanded(
202 0 : child: hasEncrypted ? Text(AppLocale.labels.hasEncrypted) : ThemeHelper.emptyBox,
203 : ),
204 : ],
205 : ),
206 : ThemeHelper.hIndent2x,
207 : ],
208 0 : InputWrapper.select(
209 0 : title: AppLocale.labels.brightnessTheme,
210 0 : value: brightness,
211 0 : tooltip: AppLocale.labels.brightnessTheme,
212 0 : options: [
213 0 : ListSelectorItem(id: '0', name: AppLocale.labels.systemMode),
214 0 : ListSelectorItem(id: '1', name: AppLocale.labels.lightMode),
215 0 : ListSelectorItem(id: '2', name: AppLocale.labels.darkMode),
216 0 : ].cast<ListSelectorItem>(),
217 0 : onChange: (v) => saveTheme(v ?? '0'),
218 : ),
219 0 : InputWrapper.select(
220 0 : title: AppLocale.labels.colorTheme,
221 0 : value: colorMode,
222 0 : tooltip: AppLocale.labels.colorTheme,
223 0 : options: [
224 0 : ListSelectorItem(id: AppColors.colorApp, name: AppLocale.labels.colorApp),
225 0 : ListSelectorItem(id: AppColors.colorSystem, name: AppLocale.labels.colorSystem),
226 0 : ListSelectorItem(id: AppColors.colorUser, name: AppLocale.labels.colorUser),
227 0 : ].cast<ListSelectorItem>(),
228 0 : onChange: (v) => saveColor(v ?? AppColors.colorApp),
229 : ),
230 0 : if (colorMode == AppColors.colorUser) ...[
231 0 : TableWidget(
232 : width: width,
233 0 : shadowColor: context.colorScheme.onSurface.withOpacity(0.05),
234 : chunk: const [120, null, null],
235 0 : data: [
236 0 : [
237 0 : Text(AppLocale.labels.colorType),
238 0 : Row(
239 : mainAxisAlignment: MainAxisAlignment.spaceBetween,
240 0 : children: [
241 0 : Text(AppLocale.labels.colorLight),
242 0 : IconButton(
243 0 : onPressed: () =>
244 0 : changePalette(paletteState = (light: AppDefaultColors(), dark: paletteState.dark)),
245 : icon: const Icon(Icons.restore),
246 0 : tooltip: AppLocale.labels.colorRestore,
247 : ),
248 : ],
249 : ),
250 0 : Row(
251 : mainAxisAlignment: MainAxisAlignment.spaceBetween,
252 0 : children: [
253 0 : Text(AppLocale.labels.colorDark),
254 0 : IconButton(
255 0 : onPressed: () =>
256 0 : changePalette(paletteState = (light: paletteState.light, dark: AppDarkColors())),
257 : icon: const Icon(Icons.restore),
258 0 : tooltip: AppLocale.labels.colorRestore,
259 : ),
260 : ],
261 : ),
262 : ],
263 0 : [
264 0 : Text(AppLocale.labels.colorBackground),
265 0 : ColorSelector(
266 0 : value: paletteState.light.background.toMaterialColor,
267 0 : setState: (v) => changePalette(paletteState.light.background = v)),
268 0 : ColorSelector(
269 0 : value: paletteState.dark.background.toMaterialColor,
270 0 : setState: (v) => changePalette(paletteState.dark.background = v)),
271 : ],
272 0 : [
273 0 : Text(AppLocale.labels.colorPrimary),
274 0 : ColorSelector(
275 0 : value: paletteState.light.primary.toMaterialColor,
276 0 : setState: (v) => changePalette(paletteState.light.primary = v)),
277 0 : ColorSelector(
278 0 : value: paletteState.dark.primary.toMaterialColor,
279 0 : setState: (v) => changePalette(paletteState.dark.primary = v)),
280 : ],
281 0 : [
282 0 : Text(AppLocale.labels.colorInversePrimary),
283 0 : ColorSelector(
284 0 : value: paletteState.light.inversePrimary.toMaterialColor,
285 0 : setState: (v) => changePalette(paletteState.light.inversePrimary = v)),
286 0 : ColorSelector(
287 0 : value: paletteState.dark.inversePrimary.toMaterialColor,
288 0 : setState: (v) => changePalette(paletteState.dark.inversePrimary = v)),
289 : ],
290 0 : [
291 0 : Text(AppLocale.labels.colorInverseSurface),
292 0 : ColorSelector(
293 0 : value: paletteState.light.inverseSurface.toMaterialColor,
294 0 : setState: (v) => changePalette(paletteState.light.inverseSurface = v)),
295 0 : ColorSelector(
296 0 : value: paletteState.dark.inverseSurface.toMaterialColor,
297 0 : setState: (v) => changePalette(paletteState.dark.inverseSurface = v)),
298 : ],
299 0 : [
300 0 : Text(AppLocale.labels.colorOnInverseSurface),
301 0 : ColorSelector(
302 0 : value: paletteState.light.onInverseSurface.toMaterialColor,
303 0 : setState: (v) => changePalette(paletteState.light.onInverseSurface = v)),
304 0 : ColorSelector(
305 0 : value: paletteState.dark.onInverseSurface.toMaterialColor,
306 0 : setState: (v) => changePalette(paletteState.dark.onInverseSurface = v)),
307 : ],
308 0 : [
309 0 : Text(AppLocale.labels.colorSecondary),
310 0 : ColorSelector(
311 0 : value: paletteState.light.secondary.toMaterialColor,
312 0 : setState: (v) => changePalette(paletteState.light.secondary = v)),
313 0 : ColorSelector(
314 0 : value: paletteState.dark.secondary.toMaterialColor,
315 0 : setState: (v) => changePalette(paletteState.dark.secondary = v)),
316 : ],
317 0 : [
318 0 : Text(AppLocale.labels.colorOnSecondary),
319 0 : ColorSelector(
320 0 : value: paletteState.light.onSecondary.toMaterialColor,
321 0 : setState: (v) => changePalette(paletteState.light.onSecondary = v)),
322 0 : ColorSelector(
323 0 : value: paletteState.dark.onSecondary.toMaterialColor,
324 0 : setState: (v) => changePalette(paletteState.dark.onSecondary = v)),
325 : ],
326 0 : [
327 0 : Text(AppLocale.labels.colorOnSecondaryContainer),
328 0 : ColorSelector(
329 0 : value: paletteState.light.onSecondaryContainer.toMaterialColor,
330 0 : setState: (v) => changePalette(paletteState.light.onSecondaryContainer = v)),
331 0 : ColorSelector(
332 0 : value: paletteState.dark.onSecondaryContainer.toMaterialColor,
333 0 : setState: (v) => changePalette(paletteState.dark.onSecondaryContainer = v)),
334 : ],
335 : ],
336 : ),
337 : ThemeHelper.hIndent4x,
338 : ],
339 0 : RowWidget(
340 : indent: indent,
341 : maxWidth: width,
342 0 : chunk: [null, indent + ThemeHelper.getTextWidth(Text(AppLocale.labels.reset))],
343 0 : children: [
344 0 : [
345 0 : Text(
346 0 : AppLocale.labels.zoomState,
347 0 : style: textTheme.bodyLarge,
348 : ),
349 : ],
350 0 : [
351 0 : Align(
352 : alignment: Alignment.centerRight,
353 0 : child: LinkWidget(AppLocale.labels.reset, onTap: () => zoom.set(1)),
354 : ),
355 : ],
356 : ],
357 : ),
358 0 : Container(
359 0 : color: context.colorScheme.fieldBackground,
360 0 : child: Slider(
361 0 : value: zoom.value,
362 0 : onChanged: zoom.set,
363 : min: AppZoom.min,
364 : max: AppZoom.max,
365 : divisions: 15,
366 0 : label: zoom.value.toStringAsFixed(2),
367 : ),
368 : ),
369 0 : ThemeHelper.hIndent2x,
370 : ],
371 : ),
372 : );
373 : }
374 : }
|