LCOV - code coverage report
Current view: top level - lib/pages/start/widgets - setting_tab.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 111 222 50.0 %
Date: 2024-10-04 11:12:13 Functions: 0 0 -

          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           2 :   const SettingTab({
      35             :     super.key,
      36             :     required super.setState,
      37             :     required super.setButton,
      38             :     super.isFirstBoot = true,
      39             :   });
      40             : 
      41           1 :   @override
      42           1 :   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           2 :   final languages = languageList.map((e) => ListSelectorItem(id: e.id, name: e.name)).toList();
      67             : 
      68           1 :   @override
      69             :   void initState() {
      70           1 :     super.initState();
      71           2 :     controller = FocusController();
      72           1 :     final doEncrypt = AppPreferences.get(AppPreferences.prefDoEncrypt);
      73           1 :     hasEncrypted = doEncrypt != null;
      74           2 :     isEncrypted = doEncrypt == 'true' || doEncrypt == null;
      75           2 :     AppPreferences.set(AppPreferences.prefDoEncrypt, isEncrypted ? 'true' : 'false');
      76           3 :     brightness = AppPreferences.get(AppPreferences.prefTheme) ?? brightness;
      77           3 :     colorMode = AppPreferences.get(AppPreferences.prefColor) ?? colorMode;
      78           3 :     currency = CurrencyProvider.find(AppPreferences.get(AppPreferences.prefCurrency) ?? '');
      79             :   }
      80             : 
      81           1 :   @override
      82             :   void dispose() {
      83           2 :     controller.dispose();
      84           1 :     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           1 :   Future<void> initCurrencyFromLocale(String locale) async {
     132           1 :     final format = NumberFormat.simpleCurrency(locale: locale);
     133           1 :     final code = format.currencyName ?? 'EUR';
     134           1 :     await AppPreferences.set(AppPreferences.prefCurrency, code);
     135           4 :     setState(() => currency = CurrencyProvider.find(code));
     136           1 :     Exchange.defaultCurrency = currency;
     137           1 :     CurrencyDefaults.defaultCurrency = currency;
     138             :   }
     139             : 
     140           1 :   @override
     141           2 :   String getButtonTitle() => AppLocale.labels.saveSettingsTooltip;
     142             : 
     143           1 :   @override
     144             :   Widget buildContent(BuildContext context, BoxConstraints constraints) {
     145           2 :     theme = Provider.of<AppTheme>(context, listen: false);
     146           2 :     zoom = Provider.of<AppZoom>(context, listen: false);
     147           2 :     palette = Provider.of<AppPalette>(context, listen: false);
     148           2 :     locale = Provider.of<AppLocale>(context, listen: false);
     149           2 :     design = Provider.of<AppDesign>(context, listen: false);
     150           2 :     state = Provider.of<AppData>(context, listen: true);
     151           1 :     final textTheme = context.textTheme;
     152           1 :     final indent = ThemeHelper.getIndent(2);
     153           3 :     final width = ThemeHelper.getMaxWidth(context, constraints) - 2 * indent;
     154           1 :     if (currency == null) {
     155           5 :       WidgetsBinding.instance.addPostFrameCallback((_) => initCurrencyFromLocale(AppLocale.code));
     156             :       return ThemeHelper.emptyBox;
     157             :     }
     158           1 :     return SingleScrollWrapper(
     159           1 :       controller: controller,
     160           1 :       child: Column(
     161           1 :         crossAxisAlignment: AppDesign.getAlignment(),
     162           1 :         children: [
     163             :           ThemeHelper.hIndent2x,
     164           1 :           InputWrapper.select(
     165           2 :             title: AppLocale.labels.language,
     166           2 :             tooltip: AppLocale.labels.language,
     167           1 :             value: AppLocale.code,
     168           1 :             options: languages,
     169           0 :             onChange: (v) => saveLocale(v ?? AppLocale.code),
     170             :           ),
     171           1 :           InputWrapper.select(
     172           2 :             title: AppLocale.labels.design,
     173           2 :             tooltip: AppLocale.labels.design,
     174           2 :             value: AppDesign.get().name,
     175           1 :             options: [
     176           4 :               ListSelectorItem(id: AppDesignType.global.name, name: AppLocale.labels.designGlobal),
     177           4 :               ListSelectorItem(id: AppDesignType.asiaGeneral.name, name: AppLocale.labels.designAsiaGeneral),
     178           4 :               ListSelectorItem(id: AppDesignType.rtlGeneral.name, name: AppLocale.labels.designRtlGeneral),
     179           4 :               ListSelectorItem(id: AppDesignType.germany.name, name: AppLocale.labels.designGermany),
     180           1 :             ].cast<ListSelectorItem>(),
     181           0 :             onChange: (v) => saveDesign(AppDesign.find(v)?.name ?? AppDesignType.global.name),
     182             :           ),
     183           1 :           InputWrapper.currency(
     184           2 :             title: AppLocale.labels.currencyDefault,
     185           2 :             tooltip: AppLocale.labels.currencyDefault,
     186           1 :             value: currency,
     187           1 :             onChange: saveCurrency,
     188             :           ),
     189           1 :           if (kDebugMode) ...[
     190           1 :             Row(
     191           1 :               mainAxisAlignment: AppDesign.getAlignment<MainAxisAlignment>(),
     192           1 :               children: [
     193           1 :                 Text(
     194           2 :                   AppLocale.labels.encryptionMode,
     195           1 :                   style: textTheme.bodyLarge,
     196             :                 ),
     197           1 :                 Switch(
     198           1 :                   value: isEncrypted,
     199           1 :                   onChanged: saveEncryption,
     200             :                 ),
     201           1 :                 Expanded(
     202           1 :                   child: hasEncrypted ? Text(AppLocale.labels.hasEncrypted) : ThemeHelper.emptyBox,
     203             :                 ),
     204             :               ],
     205             :             ),
     206             :             ThemeHelper.hIndent2x,
     207             :           ],
     208           1 :           InputWrapper.select(
     209           2 :             title: AppLocale.labels.brightnessTheme,
     210           1 :             value: brightness,
     211           2 :             tooltip: AppLocale.labels.brightnessTheme,
     212           1 :             options: [
     213           3 :               ListSelectorItem(id: '0', name: AppLocale.labels.systemMode),
     214           3 :               ListSelectorItem(id: '1', name: AppLocale.labels.lightMode),
     215           3 :               ListSelectorItem(id: '2', name: AppLocale.labels.darkMode),
     216           1 :             ].cast<ListSelectorItem>(),
     217           0 :             onChange: (v) => saveTheme(v ?? '0'),
     218             :           ),
     219           1 :           InputWrapper.select(
     220           2 :             title: AppLocale.labels.colorTheme,
     221           1 :             value: colorMode,
     222           2 :             tooltip: AppLocale.labels.colorTheme,
     223           1 :             options: [
     224           3 :               ListSelectorItem(id: AppColors.colorApp, name: AppLocale.labels.colorApp),
     225           3 :               ListSelectorItem(id: AppColors.colorSystem, name: AppLocale.labels.colorSystem),
     226           3 :               ListSelectorItem(id: AppColors.colorUser, name: AppLocale.labels.colorUser),
     227           1 :             ].cast<ListSelectorItem>(),
     228           0 :             onChange: (v) => saveColor(v ?? AppColors.colorApp),
     229             :           ),
     230           2 :           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           1 :           RowWidget(
     340             :             indent: indent,
     341             :             maxWidth: width,
     342           6 :             chunk: [null, indent + ThemeHelper.getTextWidth(Text(AppLocale.labels.reset))],
     343           1 :             children: [
     344           1 :               [
     345           1 :                 Text(
     346           2 :                   AppLocale.labels.zoomState,
     347           1 :                   style: textTheme.bodyLarge,
     348             :                 ),
     349             :               ],
     350           1 :               [
     351           1 :                 Align(
     352             :                   alignment: Alignment.centerRight,
     353           3 :                   child: LinkWidget(AppLocale.labels.reset, onTap: () => zoom.set(1)),
     354             :                 ),
     355             :               ],
     356             :             ],
     357             :           ),
     358           1 :           Container(
     359           2 :             color: context.colorScheme.fieldBackground,
     360           1 :             child: Slider(
     361           2 :               value: zoom.value,
     362           2 :               onChanged: zoom.set,
     363             :               min: AppZoom.min,
     364             :               max: AppZoom.max,
     365             :               divisions: 15,
     366           3 :               label: zoom.value.toStringAsFixed(2),
     367             :             ),
     368             :           ),
     369           1 :           ThemeHelper.hIndent2x,
     370             :         ],
     371             :       ),
     372             :     );
     373             :   }
     374             : }

Generated by: LCOV version 1.14