LCOV - code coverage report
Current view: top level - lib/pages/budget - budget_add_page.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 1 118 0.8 %
Date: 2024-10-04 11:09:33 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/herald/app_design.dart';
       5             : import 'package:app_finance/_classes/herald/app_locale.dart';
       6             : import 'package:app_finance/_classes/structure/budget_app_data.dart';
       7             : import 'package:app_finance/_classes/controller/focus_controller.dart';
       8             : import 'package:app_finance/_classes/storage/app_preferences.dart';
       9             : import 'package:app_finance/_configs/custom_color_scheme.dart';
      10             : import 'package:app_finance/_configs/theme_helper.dart';
      11             : import 'package:app_finance/_ext/build_context_ext.dart';
      12             : import 'package:app_finance/design/wrapper/input_wrapper.dart';
      13             : import 'package:app_finance/design/wrapper/text_wrapper.dart';
      14             : import 'package:app_finance/pages/_interfaces/abstract_add_page.dart';
      15             : import 'package:app_finance/design/button/full_sized_button_widget.dart';
      16             : import 'package:app_finance/design/form/simple_input.dart';
      17             : import 'package:app_finance/design/wrapper/row_widget.dart';
      18             : import 'package:app_finance/design/wrapper/single_scroll_wrapper.dart';
      19             : import 'package:flutter/material.dart';
      20             : import 'package:flutter/services.dart';
      21             : import 'package:flutter_currency_picker/flutter_currency_picker.dart';
      22             : import 'package:intl/intl.dart';
      23             : 
      24             : class BudgetAddPage extends AbstractAddPage {
      25             :   final String? title;
      26             :   final double? budgetLimit;
      27             :   final IconData? icon;
      28             :   final MaterialColor? color;
      29             :   final Currency? currency;
      30             : 
      31           5 :   const BudgetAddPage({
      32             :     super.key,
      33             :     this.title,
      34             :     this.budgetLimit,
      35             :     this.icon,
      36             :     this.color,
      37             :     this.currency,
      38             :   });
      39             : 
      40           0 :   @override
      41           0 :   BudgetAddPageState createState() => BudgetAddPageState();
      42             : }
      43             : 
      44             : class BudgetAddPageState<T extends BudgetAddPage> extends AbstractAddPageState<BudgetAddPage> {
      45             :   late FocusController focus;
      46             :   late TextEditingController title;
      47             :   late TextEditingController budgetLimit;
      48             :   IconData? icon;
      49             :   MaterialColor? color;
      50             :   Currency? currency;
      51             :   Map<int, double> amountSet = {};
      52             : 
      53           0 :   @override
      54             :   void initState() {
      55           0 :     focus = FocusController();
      56           0 :     title = TextEditingController(text: widget.title);
      57           0 :     budgetLimit = TextEditingController(text: widget.budgetLimit != null ? widget.budgetLimit.toString() : '');
      58           0 :     icon = widget.icon;
      59           0 :     color = widget.color;
      60           0 :     final currencyId = AppPreferences.get(AppPreferences.prefCurrency);
      61           0 :     currency = widget.currency ?? CurrencyProvider.find(currencyId);
      62           0 :     super.initState();
      63             :   }
      64             : 
      65           0 :   @override
      66             :   void dispose() {
      67           0 :     focus.dispose();
      68           0 :     title.dispose();
      69           0 :     budgetLimit.dispose();
      70           0 :     super.dispose();
      71             :   }
      72             : 
      73           0 :   @override
      74             :   String getTitle() {
      75           0 :     return AppLocale.labels.createBudgetHeader;
      76             :   }
      77             : 
      78           0 :   @override
      79             :   bool hasFormErrors() {
      80           0 :     setState(() => hasError = title.text.isEmpty || currency == null);
      81           0 :     return hasError;
      82             :   }
      83             : 
      84           0 :   @override
      85             :   void updateStorage() {
      86           0 :     if (currency != null) {
      87           0 :       CurrencyProvider.pin(currency!);
      88             :     }
      89           0 :     super.state.add(BudgetAppData(
      90           0 :           title: title.text,
      91           0 :           amountLimit: double.tryParse(budgetLimit.text) ?? 0.0,
      92           0 :           amountSet: amountSet,
      93             :           progress: 0.0,
      94           0 :           color: color ?? Colors.red,
      95             :           hidden: false,
      96           0 :           currency: currency,
      97           0 :           icon: icon,
      98           0 :         )..setState(state));
      99             :   }
     100             : 
     101           0 :   @override
     102           0 :   String getButtonName() => AppLocale.labels.createBudgetTooltip;
     103             : 
     104           0 :   @override
     105             :   Widget buildButton(BuildContext context, BoxConstraints constraints) {
     106           0 :     NavigatorState nav = Navigator.of(context);
     107           0 :     return FullSizedButtonWidget(
     108             :       constraints: constraints,
     109           0 :       controller: focus,
     110           0 :       onPressed: () => triggerActionButton(nav),
     111           0 :       title: getButtonName(),
     112             :       icon: Icons.save,
     113             :     );
     114             :   }
     115             : 
     116           0 :   @override
     117             :   Widget buildContent(BuildContext context, BoxConstraints constraints) {
     118           0 :     final textTheme = context.textTheme;
     119           0 :     double indent = ThemeHelper.getIndent(2);
     120           0 :     double width = ThemeHelper.getWidth(context, 6, constraints);
     121             : 
     122           0 :     return SingleScrollWrapper(
     123           0 :       controller: focus,
     124           0 :       child: Container(
     125           0 :         margin: EdgeInsets.fromLTRB(indent, indent, indent, 240),
     126           0 :         child: Column(
     127           0 :           crossAxisAlignment: AppDesign.getAlignment(),
     128           0 :           children: [
     129           0 :             InputWrapper.text(
     130             :               isRequired: true,
     131           0 :               controller: title,
     132           0 :               title: AppLocale.labels.title,
     133           0 :               tooltip: AppLocale.labels.titleBudgetTooltip,
     134           0 :               showError: hasError && title.text.isEmpty,
     135             :             ),
     136           0 :             RowWidget(
     137             :               indent: indent,
     138           0 :               maxWidth: width + indent,
     139             :               chunk: const [0.5, 0.5],
     140           0 :               children: [
     141           0 :                 [
     142           0 :                   InputWrapper.icon(
     143           0 :                     value: icon,
     144           0 :                     title: AppLocale.labels.icon,
     145           0 :                     onChange: (value) => setState(() => icon = value),
     146             :                   ),
     147             :                 ],
     148           0 :                 [
     149           0 :                   InputWrapper.color(
     150           0 :                     value: color,
     151           0 :                     title: AppLocale.labels.color,
     152           0 :                     onChange: (value) => setState(() => color = value),
     153             :                   ),
     154             :                 ],
     155             :               ],
     156             :             ),
     157           0 :             Row(
     158             :               mainAxisAlignment: MainAxisAlignment.spaceBetween,
     159           0 :               children: [
     160           0 :                 TextWrapper(
     161           0 :                   AppLocale.labels.budgetLimit,
     162           0 :                   style: textTheme.bodyLarge,
     163             :                 ),
     164           0 :                 amountSet.isEmpty
     165           0 :                     ? IconButton(
     166             :                         icon: const Icon(Icons.vertical_split),
     167           0 :                         tooltip: AppLocale.labels.splitTooltip,
     168           0 :                         onPressed: () => setState(() {
     169           0 :                           final result = Map<int, double>.from(amountSet);
     170           0 :                           for (int i = 1; i <= 12; i++) {
     171           0 :                             result[i] = 1.0;
     172             :                           }
     173           0 :                           amountSet = result;
     174             :                         }),
     175             :                       )
     176           0 :                     : IconButton(
     177             :                         icon: const Icon(Icons.delete),
     178           0 :                         tooltip: AppLocale.labels.splitCancelTooltip,
     179           0 :                         onPressed: () => setState(() => amountSet = {}),
     180             :                       ),
     181             :               ],
     182             :             ),
     183           0 :             SimpleInput(
     184           0 :               key: ValueKey(AppLocale.labels.budgetLimit),
     185           0 :               controller: budgetLimit,
     186             :               type: const TextInputType.numberWithOptions(decimal: true),
     187           0 :               tooltip: AppLocale.labels.balanceTooltip,
     188           0 :               formatter: [
     189           0 :                 FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,4}')),
     190             :               ],
     191             :             ),
     192             :             ThemeHelper.hIndent2x,
     193           0 :             if (amountSet.isNotEmpty) ...[
     194           0 :               Text(
     195           0 :                 AppLocale.labels.budgetRelativeLimit,
     196           0 :                 style: textTheme.bodyLarge,
     197             :               ),
     198           0 :               ...amountSet.entries.map((e) {
     199           0 :                 return RowWidget(
     200             :                   indent: indent,
     201           0 :                   maxWidth: width + indent,
     202             :                   chunk: const [100, null],
     203           0 :                   children: [
     204           0 :                     [
     205           0 :                       Text(
     206           0 :                         DateFormat.MMMM(AppLocale.code).format(DateTime(DateTime.now().year, e.key)),
     207           0 :                         style: textTheme.headlineMedium,
     208             :                       ),
     209             :                     ],
     210           0 :                     [
     211           0 :                       Container(
     212           0 :                         color: context.colorScheme.fieldBackground,
     213           0 :                         child: Slider(
     214           0 :                           value: e.value,
     215           0 :                           onChanged: (v) => setState(() => amountSet[e.key] = v),
     216             :                           min: 0.0,
     217             :                           max: 4.0,
     218             :                           divisions: 15,
     219           0 :                           label: e.value.toStringAsFixed(2),
     220             :                         ),
     221             :                       ),
     222             :                     ],
     223             :                   ],
     224             :                 );
     225             :               }),
     226           0 :               ThemeHelper.hIndent2x,
     227             :             ],
     228           0 :             InputWrapper.currency(
     229             :               isRequired: true,
     230           0 :               showError: hasError && currency == null,
     231           0 :               value: currency,
     232           0 :               title: AppLocale.labels.currency,
     233           0 :               tooltip: AppLocale.labels.currencyTooltip,
     234           0 :               onChange: (value) => setState(() => currency = value),
     235             :             ),
     236             :           ],
     237             :         ),
     238             :       ),
     239             :     );
     240             :   }
     241             : }

Generated by: LCOV version 1.14