diff --git a/lib/pages/add_page/add_page.dart b/lib/pages/add_page/add_page.dart index cb31433..5d2dcf8 100644 --- a/lib/pages/add_page/add_page.dart +++ b/lib/pages/add_page/add_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../utils/decimal_text_input_formatter.dart'; import '../../model/transaction.dart'; import 'widgets/details_tile.dart'; import 'widgets/type_tab.dart'; @@ -337,9 +338,9 @@ class _AddPageState extends ConsumerState with Functions { color: typeToColor(selectedType), ), ), - keyboardType: TextInputType.number, + keyboardType: const TextInputType.numberWithOptions(decimal: true), inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[0,0-9,9]')), + DecimalTextInputFormatter(decimalDigits: 2) ], autofocus: true, textAlign: TextAlign.center, diff --git a/lib/pages/transactions_page/widgets/categories_tab.dart b/lib/pages/transactions_page/widgets/categories_tab.dart index 2a1ef9f..9c94d2e 100644 --- a/lib/pages/transactions_page/widgets/categories_tab.dart +++ b/lib/pages/transactions_page/widgets/categories_tab.dart @@ -38,46 +38,40 @@ class _CategoriesTabState extends ConsumerState with Functions { Map categoryToAmount = {}; double total = 0; - if (transactions.value != null) { - for (var t in transactions.value!) { - // add transaction to its category - int categoryId = t.idCategory!; + for (var transaction in transactions.value ?? []) { + print(transaction.idCategory); + final categoryId = transaction.idCategory; + if (categoryId != null) { + final updateValue = { + "account": transaction.idBankAccount.toString(), + "amount": transaction.amount, + "category": categoryId.toString(), + "title": transaction.note, + }; + if (categoryToTransactions.containsKey(categoryId)) { - categoryToTransactions[categoryId]?.add({ - "account": t.idBankAccount.toString(), - "amount": t.amount, - "category": categoryId.toString(), - "title": t.note, - }); + categoryToTransactions[categoryId]?.add(updateValue); } else { - categoryToTransactions.putIfAbsent( - categoryId, - () => [ - { - "account": t.idBankAccount.toString(), - "amount": t.amount, - "category": categoryId.toString(), - "title": t.note, - } - ], - ); + categoryToTransactions.putIfAbsent(categoryId, () => [updateValue]); } // update total amount for the category - total += t.amount; + total += transaction.amount; if (categoryToAmount.containsKey(categoryId)) { categoryToAmount[categoryId] = - categoryToAmount[categoryId]! + t.amount.toDouble(); + categoryToAmount[categoryId]! + transaction.amount.toDouble(); } else { - categoryToAmount.putIfAbsent(categoryId, () => t.amount.toDouble()); + categoryToAmount.putIfAbsent(categoryId, () => transaction.amount.toDouble()); } } } // Add missing catogories (with amount 0) // This will be removed when only categories with transactions are queried - for (var c in categories.value!) { - categoryToAmount.putIfAbsent(c.id!, () => 0); + for (var category in categories.value ?? []) { + if (category.id != null) { + categoryToAmount.putIfAbsent(category.id, () => 0); + } } return Container( @@ -137,8 +131,8 @@ class _CategoriesTabState extends ConsumerState with Functions { percent: (categoryToAmount[t.id] ?? 0) / total * 100, color: const Color(0xFFEBC35F), - icon: iconList[t.symbol] ?? - Icons.swap_horiz_rounded, + icon: + iconList[t.symbol] ?? Icons.swap_horiz_rounded, notifier: selectedCategory, index: index, ); diff --git a/lib/utils/decimal_text_input_formatter.dart b/lib/utils/decimal_text_input_formatter.dart new file mode 100644 index 0000000..7e7cc68 --- /dev/null +++ b/lib/utils/decimal_text_input_formatter.dart @@ -0,0 +1,55 @@ +import 'dart:math' as math; +import 'package:flutter/services.dart'; + +class DecimalTextInputFormatter extends TextInputFormatter { + DecimalTextInputFormatter({this.decimalDigits}) + : assert(decimalDigits == null || decimalDigits > 0); + + final int? decimalDigits; + final String decimalSeparator = "."; + final String thousandsSeparator = ","; + + @override + TextEditingValue formatEditUpdate( + TextEditingValue oldValue, + TextEditingValue newValue, + ) { + TextSelection newSelection = newValue.selection; + String value = newValue.text; + + RegExp regex = RegExp(r'[\d\,\.]'); + + if (value.isNotEmpty && !regex.hasMatch(value[value.length -1])) { + return oldValue; + } + + if (value.contains(thousandsSeparator)) { + value = value.replaceAll(thousandsSeparator, decimalSeparator); + } + + if (decimalSeparator.allMatches(value).length > 1 || value.contains(thousandsSeparator)) { + return oldValue; + } + + if (value == decimalSeparator) { + // Allow for .x decimal notation + value = "0$decimalSeparator"; + } + + if (decimalDigits != null && + value.contains(decimalSeparator) && value.substring(value.indexOf(decimalSeparator) + 1).length > decimalDigits!) { + value = oldValue.text; + } + + newSelection = newValue.selection.copyWith( + baseOffset: math.min(value.length, value.length + 1), + extentOffset: math.min(value.length, value.length + 1), + ); + + return TextEditingValue( + text: value, + selection: newSelection, + composing: TextRange.empty, + ); + } +}