This commit is contained in:
2026-03-26 00:42:54 +03:00
parent d1ef8a64a1
commit 71de991587
18 changed files with 564 additions and 187 deletions
Binary file not shown.
+1
View File
@@ -388,6 +388,7 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen>
AmountInput( AmountInput(
controller: _amountController, controller: _amountController,
currencySymbol: overrideCurrency, currencySymbol: overrideCurrency,
currencyCode: state.overrideCurrencyCode,
showError: _showError, showError: _showError,
borderColorAnimation: _borderColorAnimation, borderColorAnimation: _borderColorAnimation,
isDark: isDark, isDark: isDark,
@@ -1,8 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../shared/widgets/byn_sign.dart';
class AmountInput extends StatelessWidget { class AmountInput extends StatelessWidget {
final TextEditingController controller; final TextEditingController controller;
final String currencySymbol; final String currencySymbol;
final String currencyCode;
final bool showError; final bool showError;
final Animation<Color?> borderColorAnimation; final Animation<Color?> borderColorAnimation;
final bool isDark; final bool isDark;
@@ -12,6 +14,7 @@ class AmountInput extends StatelessWidget {
super.key, super.key,
required this.controller, required this.controller,
required this.currencySymbol, required this.currencySymbol,
required this.currencyCode,
required this.showError, required this.showError,
required this.borderColorAnimation, required this.borderColorAnimation,
required this.isDark, required this.isDark,
@@ -41,7 +44,14 @@ class AmountInput extends StatelessWidget {
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 14), padding: const EdgeInsets.symmetric(horizontal: 14),
child: Text( child: currencyCode == 'BYN'
? BynSign(
fontSize: 18,
color: Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.7),
)
: Text(
currencySymbol, currencySymbol,
style: Theme.of(context).textTheme.bodyLarge?.copyWith( style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: Theme.of( color: Theme.of(
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../shared/widgets/byn_sign.dart';
class CurrencyPicker extends StatelessWidget { class CurrencyPicker extends StatelessWidget {
final String selected; final String selected;
@@ -45,7 +46,14 @@ class CurrencyPicker extends StatelessWidget {
), ),
child: Column( child: Column(
children: [ children: [
Text( c.$1 == 'BYN'
? BynSign(
fontSize: 16,
color: isSelected
? const Color(0xFF7C6DED)
: colorScheme.onSurface,
)
: Text(
c.$2, c.$2,
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
+49 -16
View File
@@ -6,6 +6,7 @@ import '../../core/constants.dart';
import '../../core/l10n/locale_provider.dart'; import '../../core/l10n/locale_provider.dart';
import '../../shared/utils/currency_utils.dart'; import '../../shared/utils/currency_utils.dart';
import '../../shared/providers/amount_format_provider.dart'; import '../../shared/providers/amount_format_provider.dart';
import '../../shared/widgets/byn_sign.dart';
import '../settings/provider.dart'; import '../settings/provider.dart';
import 'provider.dart'; import 'provider.dart';
@@ -78,7 +79,9 @@ class _CategoriesScreenState extends ConsumerState<CategoriesScreen> {
border: Border.all( border: Border.all(
color: !_showIncome color: !_showIncome
? AppColors.accent ? AppColors.accent
: Theme.of(context).colorScheme.onSurface.withOpacity(0.2), : Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.2),
width: 1.5, width: 1.5,
), ),
), ),
@@ -90,7 +93,9 @@ class _CategoriesScreenState extends ConsumerState<CategoriesScreen> {
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: !_showIncome color: !_showIncome
? Colors.white ? Colors.white
: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), : Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.6),
), ),
), ),
), ),
@@ -113,7 +118,9 @@ class _CategoriesScreenState extends ConsumerState<CategoriesScreen> {
border: Border.all( border: Border.all(
color: _showIncome color: _showIncome
? AppColors.accent ? AppColors.accent
: Theme.of(context).colorScheme.onSurface.withOpacity(0.2), : Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.2),
width: 1.5, width: 1.5,
), ),
), ),
@@ -125,7 +132,9 @@ class _CategoriesScreenState extends ConsumerState<CategoriesScreen> {
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: _showIncome color: _showIncome
? Colors.white ? Colors.white
: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), : Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.6),
), ),
), ),
), ),
@@ -149,11 +158,15 @@ class _CategoriesScreenState extends ConsumerState<CategoriesScreen> {
currency: currencyInfo.symbol, currency: currencyInfo.symbol,
) )
else else
_BarChartCard(monthlyData: monthlyData, currency: currencyInfo.symbol), _BarChartCard(
monthlyData: monthlyData,
currency: currencyInfo.symbol,
),
const SizedBox(height: 20), const SizedBox(height: 20),
Text( Text(
s.rankedByAmount, s.rankedByAmount,
style: Theme.of(context).textTheme.titleMedium?.copyWith( style: Theme.of(context).textTheme.titleMedium
?.copyWith(
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.onSurface, color: Theme.of(context).colorScheme.onSurface,
), ),
@@ -235,12 +248,16 @@ class _ToggleButton extends StatelessWidget {
child: Container( child: Container(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isSelected ? AppColors.accent.withOpacity(0.15) : Colors.transparent, color: isSelected
? AppColors.accent.withOpacity(0.15)
: Colors.transparent,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Icon( child: Icon(
icon, icon,
color: isSelected ? AppColors.accent : Theme.of(context).colorScheme.onSurface.withOpacity(0.6), color: isSelected
? AppColors.accent
: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
size: 20, size: 20,
), ),
), ),
@@ -301,7 +318,8 @@ class _PieChartCard extends ConsumerWidget {
final isTouched = i == touchedIndex; final isTouched = i == touchedIndex;
final cat = entries[i].key; final cat = entries[i].key;
final val = entries[i].value; final val = entries[i].value;
final color = AppCategories.colors[cat] ?? AppColors.accent; final color =
AppCategories.colors[cat] ?? AppColors.accent;
return PieChartSectionData( return PieChartSectionData(
color: color, color: color,
value: val, value: val,
@@ -324,7 +342,9 @@ class _PieChartCard extends ConsumerWidget {
Text( Text(
s.total, s.total,
style: Theme.of(context).textTheme.bodySmall?.copyWith( style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), color: Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.6),
), ),
), ),
Text( Text(
@@ -354,7 +374,9 @@ class _BarChartCard extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final s = ref.watch(stringsProvider); final s = ref.watch(stringsProvider);
final fmt = ref.watch(amountFormatProvider); final fmt = ref.watch(amountFormatProvider);
final maxY = monthlyData.map((e) => e.amount).reduce((a, b) => a > b ? a : b); final maxY = monthlyData
.map((e) => e.amount)
.reduce((a, b) => a > b ? a : b);
final adjustedMaxY = maxY * 1.2; final adjustedMaxY = maxY * 1.2;
return Container( return Container(
@@ -400,14 +422,17 @@ class _BarChartCard extends ConsumerWidget {
sideTitles: SideTitles( sideTitles: SideTitles(
showTitles: true, showTitles: true,
getTitlesWidget: (value, meta) { getTitlesWidget: (value, meta) {
if (value.toInt() >= 0 && value.toInt() < monthlyData.length) { if (value.toInt() >= 0 &&
value.toInt() < monthlyData.length) {
final month = monthlyData[value.toInt()].month; final month = monthlyData[value.toInt()].month;
return Padding( return Padding(
padding: const EdgeInsets.only(top: 8), padding: const EdgeInsets.only(top: 8),
child: Text( child: Text(
DateFormat('MMM').format(month), DateFormat('MMM').format(month),
style: TextStyle( style: TextStyle(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), color: Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.6),
fontSize: 11, fontSize: 11,
), ),
), ),
@@ -501,13 +526,19 @@ class _CategoryRow extends ConsumerWidget {
height: 28, height: 28,
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
color: rank <= 3 ? color.withOpacity(0.2) : Theme.of(context).dividerColor, color: rank <= 3
? color.withOpacity(0.2)
: Theme.of(context).dividerColor,
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: Text( child: Text(
'$rank', '$rank',
style: Theme.of(context).textTheme.bodySmall?.copyWith( style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: rank <= 3 ? color : Theme.of(context).colorScheme.onSurface.withOpacity(0.6), color: rank <= 3
? color
: Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.6),
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
), ),
), ),
@@ -544,7 +575,9 @@ class _CategoryRow extends ConsumerWidget {
Text( Text(
'${(pct * 100).toStringAsFixed(1)}%', '${(pct * 100).toStringAsFixed(1)}%',
style: Theme.of(context).textTheme.bodySmall?.copyWith( style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), color: Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.6),
), ),
), ),
], ],
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../shared/models/account.dart'; import '../../../../shared/models/account.dart';
import '../../../../shared/models/transaction.dart'; import '../../../../shared/models/transaction.dart';
import '../../../../shared/widgets/byn_sign.dart';
import '../../../settings/provider.dart'; import '../../../settings/provider.dart';
import '../../provider.dart'; import '../../provider.dart';
import '../balance_card.dart'; import '../balance_card.dart';
@@ -308,7 +309,16 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( entry.$1 == 'BYN'
? BynSign(
fontSize: 14,
color: isSelected
? const Color(0xFF7C6DED)
: Theme.of(
widget.context,
).colorScheme.onSurface,
)
: Text(
entry.$2, entry.$2,
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../shared/widgets/byn_sign.dart';
class AccountEditorPanel extends ConsumerWidget { class AccountEditorPanel extends ConsumerWidget {
final TextEditingController nameController; final TextEditingController nameController;
@@ -160,13 +161,24 @@ class AccountEditorPanel extends ConsumerWidget {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text( selectedCurrency == 'BYN'
? BynSign(
fontSize: 15,
color: Theme.of(
dashboardContext,
).colorScheme.onSurface,
)
: Text(
[ [
('USD', '\$'), ('USD', '\$'),
('EUR', ''), ('EUR', ''),
('BYN', 'Br'), ('BYN', 'Br'),
('RUB', ''), ('RUB', ''),
].firstWhere((c) => c.$1 == selectedCurrency).$2, ]
.firstWhere(
(c) => c.$1 == selectedCurrency,
)
.$2,
style: const TextStyle( style: const TextStyle(
fontSize: 15, fontSize: 15,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@@ -7,12 +7,13 @@ import '../../../core/l10n/locale_provider.dart';
import '../../../core/services/card_color_service.dart'; import '../../../core/services/card_color_service.dart';
import '../../../core/services/haptic_service.dart'; import '../../../core/services/haptic_service.dart';
import '../../../shared/providers/amount_format_provider.dart'; import '../../../shared/providers/amount_format_provider.dart';
import '../../../shared/widgets/byn_sign.dart';
import '../../settings/provider.dart'; import '../../settings/provider.dart';
import '../provider.dart'; import '../provider.dart';
String _smartBalance(double amount, AmountFormat fmt, String symbol) { String _smartBalance(double amount, AmountFormat fmt, String symbol) {
const spaceAfter = {'Br'}; const spaceAfter = {'Br'};
final sep = spaceAfter.contains(symbol) ? ' ' : ''; final sep = spaceAfter.contains(symbol) || symbol.isEmpty ? ' ' : '';
final isWhole = amount == amount.floorToDouble(); final isWhole = amount == amount.floorToDouble();
String formatted; String formatted;
@@ -24,7 +25,7 @@ String _smartBalance(double amount, AmountFormat fmt, String symbol) {
} else { } else {
formatted = fmt.format(amount); formatted = fmt.format(amount);
} }
return '$symbol$sep$formatted'; return symbol.isEmpty ? formatted : '$symbol$sep$formatted';
} }
class BalanceCard extends ConsumerStatefulWidget { class BalanceCard extends ConsumerStatefulWidget {
@@ -242,7 +243,33 @@ class BalanceCardState extends ConsumerState<BalanceCard>
FittedBox( FittedBox(
fit: BoxFit.scaleDown, fit: BoxFit.scaleDown,
alignment: Alignment.center, alignment: Alignment.center,
child: Text( child: widget.currencyInfo.code == 'BYN'
? Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
BynSign(
fontSize: 48,
color: onCard,
),
const SizedBox(width: 4),
Text(
_smartBalance(
widget.balance,
fmt,
'',
),
style: TextStyle(
fontSize: 48,
fontWeight: FontWeight.w700,
color: onCard,
),
maxLines: 1,
),
],
)
: Text(
_smartBalance( _smartBalance(
widget.balance, widget.balance,
fmt, fmt,
@@ -285,8 +312,42 @@ class BalanceCardState extends ConsumerState<BalanceCard>
child: FittedBox( child: FittedBox(
fit: BoxFit.scaleDown, fit: BoxFit.scaleDown,
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: c.$1 == 'BYN'
_smartBalance(converted, fmt, c.$2), ? Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
BynSign(
fontSize: 14,
color: onCard.withOpacity(
0.65,
),
),
const SizedBox(width: 2),
Text(
_smartBalance(
converted,
fmt,
'',
),
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: onCard.withOpacity(
0.65,
),
),
maxLines: 1,
),
],
)
: Text(
_smartBalance(
converted,
fmt,
c.$2,
),
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/l10n/app_strings.dart'; import '../../../core/l10n/app_strings.dart';
import '../../../shared/providers/amount_format_provider.dart'; import '../../../shared/providers/amount_format_provider.dart';
import '../../../shared/utils/currency_utils.dart'; import '../../../shared/utils/currency_utils.dart';
import '../../../shared/widgets/byn_sign.dart';
import '../../settings/provider.dart'; import '../../settings/provider.dart';
class BudgetProgress extends ConsumerWidget { class BudgetProgress extends ConsumerWidget {
@@ -95,8 +96,40 @@ class BudgetProgress extends ConsumerWidget {
const SizedBox(height: 8), const SizedBox(height: 8),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
currencyInfo.code == 'BYN'
? Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Text( Text(
'${strings.spent}: ',
style: Theme.of(context).textTheme.bodySmall
?.copyWith(
color: Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.6),
),
),
BynSign(
fontSize: 12,
color: Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.6),
),
const SizedBox(width: 2),
Text(
formatAmount('', spent, fmt),
style: Theme.of(context).textTheme.bodySmall
?.copyWith(
color: Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.6),
),
),
],
)
: Text(
'${strings.spent}: ${formatAmount(currencyInfo.symbol, spent, fmt)}', '${strings.spent}: ${formatAmount(currencyInfo.symbol, spent, fmt)}',
style: Theme.of(context).textTheme.bodySmall?.copyWith( style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of( color: Theme.of(
@@ -104,7 +137,39 @@ class BudgetProgress extends ConsumerWidget {
).colorScheme.onSurface.withOpacity(0.6), ).colorScheme.onSurface.withOpacity(0.6),
), ),
), ),
currencyInfo.code == 'BYN'
? Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text( Text(
'${strings.limit}: ',
style: Theme.of(context).textTheme.bodySmall
?.copyWith(
color: Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.6),
),
),
BynSign(
fontSize: 12,
color: Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.6),
),
const SizedBox(width: 2),
Text(
formatAmount('', budget, fmt),
style: Theme.of(context).textTheme.bodySmall
?.copyWith(
color: Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.6),
),
),
],
)
: Text(
'${strings.limit}: ${formatAmount(currencyInfo.symbol, budget, fmt)}', '${strings.limit}: ${formatAmount(currencyInfo.symbol, budget, fmt)}',
style: Theme.of(context).textTheme.bodySmall?.copyWith( style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of( color: Theme.of(
@@ -4,6 +4,7 @@ import '../../../core/constants.dart';
import '../../../core/l10n/app_strings.dart'; import '../../../core/l10n/app_strings.dart';
import '../../../shared/providers/amount_format_provider.dart'; import '../../../shared/providers/amount_format_provider.dart';
import '../../../shared/utils/currency_utils.dart'; import '../../../shared/utils/currency_utils.dart';
import '../../../shared/widgets/byn_sign.dart';
import '../../settings/provider.dart'; import '../../settings/provider.dart';
class SummaryRow extends StatelessWidget { class SummaryRow extends StatelessWidget {
@@ -104,7 +105,27 @@ class SummaryCard extends ConsumerWidget {
), ),
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
Text( currencyInfo.code == 'BYN'
? Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
BynSign(fontSize: 14, color: color),
const SizedBox(width: 2),
Flexible(
child: Text(
formatAmount('', amount, fmt),
style: Theme.of(context).textTheme.bodyMedium
?.copyWith(
color: color,
fontWeight: FontWeight.w600,
),
overflow: TextOverflow.ellipsis,
),
),
],
)
: Text(
formatAmount(currencyInfo.symbol, amount, fmt), formatAmount(currencyInfo.symbol, amount, fmt),
style: Theme.of(context).textTheme.bodyMedium?.copyWith( style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: color, color: color,
@@ -9,6 +9,7 @@ import '../../../core/l10n/locale_provider.dart';
import '../../../shared/models/transaction.dart'; import '../../../shared/models/transaction.dart';
import '../../../shared/providers/amount_format_provider.dart'; import '../../../shared/providers/amount_format_provider.dart';
import '../../../shared/utils/currency_utils.dart'; import '../../../shared/utils/currency_utils.dart';
import '../../../shared/widgets/byn_sign.dart';
import '../provider.dart'; import '../provider.dart';
class TransactionTile extends ConsumerWidget { class TransactionTile extends ConsumerWidget {
@@ -117,7 +118,30 @@ class TransactionTile extends ConsumerWidget {
], ],
), ),
), ),
transaction.currencyCode == 'BYN'
? Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text( Text(
isIncome ? '+ ' : '- ',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: color,
fontWeight: FontWeight.w700,
),
),
BynSign(fontSize: 14, color: color),
const SizedBox(width: 2),
Text(
formatAmount('', transaction.amount, fmt),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: color,
fontWeight: FontWeight.w700,
),
),
],
)
: Text(
'${isIncome ? '+ ' : '- '}${formatAmount(transaction.currency, transaction.amount, fmt)}', '${isIncome ? '+ ' : '- '}${formatAmount(transaction.currency, transaction.amount, fmt)}',
style: Theme.of(context).textTheme.bodyMedium?.copyWith( style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: color, color: color,
+1 -1
View File
@@ -46,7 +46,7 @@ class CurrencyInfo {
const Map<String, CurrencyInfo> currencyMap = { const Map<String, CurrencyInfo> currencyMap = {
'USD': CurrencyInfo('\$', 'USD'), 'USD': CurrencyInfo('\$', 'USD'),
'EUR': CurrencyInfo('', 'EUR'), 'EUR': CurrencyInfo('', 'EUR'),
'BYN': CurrencyInfo('Br', 'BYN'), 'BYN': CurrencyInfo('', 'BYN'),
'RUB': CurrencyInfo('', 'RUB'), 'RUB': CurrencyInfo('', 'RUB'),
}; };
@@ -20,7 +20,9 @@ class AmountFormatSection extends ConsumerWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: isDark ? null : Border.all(color: const Color(0xFFDDDDEE), width: 1), border: isDark
? null
: Border.all(color: const Color(0xFFDDDDEE), width: 1),
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -57,9 +59,13 @@ class AmountFormatSection extends ConsumerWidget {
return Padding( return Padding(
padding: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.only(bottom: 8),
child: GestureDetector( child: GestureDetector(
onTap: () => ref.read(amountFormatProvider.notifier).set(format), onTap: () =>
ref.read(amountFormatProvider.notifier).set(format),
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isSelected color: isSelected
? AppColors.accent.withOpacity(0.2) ? AppColors.accent.withOpacity(0.2)
@@ -67,7 +73,12 @@ class AmountFormatSection extends ConsumerWidget {
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: isSelected border: isSelected
? Border.all(color: AppColors.accent, width: 1.5) ? Border.all(color: AppColors.accent, width: 1.5)
: (isDark ? null : Border.all(color: const Color(0xFFDDDDEE), width: 1)), : (isDark
? null
: Border.all(
color: const Color(0xFFDDDDEE),
width: 1,
)),
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -75,14 +86,25 @@ class AmountFormatSection extends ConsumerWidget {
Text( Text(
format.label, format.label,
style: TextStyle( style: TextStyle(
color: isSelected ? AppColors.accent : Theme.of(context).colorScheme.onSurface, color: isSelected
fontWeight: isSelected ? FontWeight.w600 : FontWeight.w500, ? AppColors.accent
: Theme.of(context).colorScheme.onSurface,
fontWeight: isSelected
? FontWeight.w600
: FontWeight.w500,
), ),
), ),
Text( Text(
format.example.replaceFirst('SYM', currencyInfo.symbol), format.example.replaceFirst(
'SYM',
currencyInfo.symbol.isEmpty
? 'Br'
: currencyInfo.symbol,
),
style: TextStyle( style: TextStyle(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), color: Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.6),
fontSize: 12, fontSize: 12,
), ),
), ),
@@ -5,6 +5,7 @@ import '../../../core/constants.dart';
import '../../../core/l10n/locale_provider.dart'; import '../../../core/l10n/locale_provider.dart';
import '../../../shared/providers/amount_format_provider.dart'; import '../../../shared/providers/amount_format_provider.dart';
import '../../../shared/utils/currency_utils.dart'; import '../../../shared/utils/currency_utils.dart';
import '../../../shared/widgets/byn_sign.dart';
import '../provider.dart'; import '../provider.dart';
class BudgetSection extends ConsumerStatefulWidget { class BudgetSection extends ConsumerStatefulWidget {
@@ -59,7 +60,9 @@ class _BudgetSectionState extends ConsumerState<BudgetSection> {
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: isDark ? null : Border.all(color: const Color(0xFFDDDDEE), width: 1), border: isDark
? null
: Border.all(color: const Color(0xFFDDDDEE), width: 1),
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -91,7 +94,9 @@ class _BudgetSectionState extends ConsumerState<BudgetSection> {
if (!_isEditing) if (!_isEditing)
IconButton( IconButton(
icon: const Icon(Icons.edit_rounded, size: 20), icon: const Icon(Icons.edit_rounded, size: 20),
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), color: Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.6),
onPressed: () => setState(() => _isEditing = true), onPressed: () => setState(() => _isEditing = true),
), ),
], ],
@@ -103,14 +108,32 @@ class _BudgetSectionState extends ConsumerState<BudgetSection> {
children: [ children: [
TextFormField( TextFormField(
controller: _budgetController, controller: _budgetController,
keyboardType: const TextInputType.numberWithOptions(decimal: true), keyboardType: const TextInputType.numberWithOptions(
decimal: true,
),
inputFormatters: [ inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,2}')), FilteringTextInputFormatter.allow(
RegExp(r'^\d+\.?\d{0,2}'),
),
], ],
decoration: InputDecoration( decoration: InputDecoration(
prefixText: currencyInfo.symbol == 'Br' || currencyInfo.symbol == '' prefix: currencyInfo.code == 'BYN'
? Row(
mainAxisSize: MainAxisSize.min,
children: [
BynSign(
fontSize: 16,
color: Theme.of(context).colorScheme.onSurface,
),
const SizedBox(width: 4),
],
)
: null,
prefixText: currencyInfo.code != 'BYN'
? (currencyInfo.symbol == ''
? '${currencyInfo.symbol} ' ? '${currencyInfo.symbol} '
: currencyInfo.symbol, : currencyInfo.symbol)
: null,
hintText: '0.00', hintText: '0.00',
helperText: s.leaveEmptyToRemove, helperText: s.leaveEmptyToRemove,
), ),
@@ -123,7 +146,8 @@ class _BudgetSectionState extends ConsumerState<BudgetSection> {
TextButton( TextButton(
onPressed: () { onPressed: () {
final budget = ref.read(budgetProvider); final budget = ref.read(budgetProvider);
_budgetController.text = budget?.toStringAsFixed(2) ?? ''; _budgetController.text =
budget?.toStringAsFixed(2) ?? '';
setState(() => _isEditing = false); setState(() => _isEditing = false);
}, },
child: Text(s.cancel), child: Text(s.cancel),
@@ -144,12 +168,34 @@ class _BudgetSectionState extends ConsumerState<BudgetSection> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
budget != null && currencyInfo.code == 'BYN'
? Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
BynSign(fontSize: 24, color: AppColors.accent),
const SizedBox(width: 2),
Text( Text(
formatAmount('', budget, fmt),
style: Theme.of(context).textTheme.headlineSmall
?.copyWith(
color: AppColors.accent,
fontWeight: FontWeight.w700,
),
),
],
)
: Text(
budget != null budget != null
? formatAmount(currencyInfo.symbol, budget, fmt) ? formatAmount(currencyInfo.symbol, budget, fmt)
: s.budgetNone, : s.budgetNone,
style: Theme.of(context).textTheme.headlineSmall?.copyWith( style: Theme.of(context).textTheme.headlineSmall
color: budget != null ? AppColors.accent : Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ?.copyWith(
color: budget != null
? AppColors.accent
: Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.6),
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
), ),
), ),
@@ -159,7 +205,9 @@ class _BudgetSectionState extends ConsumerState<BudgetSection> {
? s.yourMonthlySpendingLimit ? s.yourMonthlySpendingLimit
: s.setMonthlySpendingLimit, : s.setMonthlySpendingLimit,
style: Theme.of(context).textTheme.bodySmall?.copyWith( style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), color: Theme.of(
context,
).colorScheme.onSurface.withOpacity(0.6),
), ),
), ),
], ],
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/constants.dart'; import '../../../core/constants.dart';
import '../../../core/l10n/locale_provider.dart'; import '../../../core/l10n/locale_provider.dart';
import '../../../shared/widgets/byn_sign.dart';
import '../provider.dart'; import '../provider.dart';
class CurrencySection extends ConsumerWidget { class CurrencySection extends ConsumerWidget {
@@ -18,7 +19,9 @@ class CurrencySection extends ConsumerWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: isDark ? null : Border.all(color: const Color(0xFFDDDDEE), width: 1), border: isDark
? null
: Border.all(color: const Color(0xFFDDDDEE), width: 1),
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -61,7 +64,9 @@ class CurrencySection extends ConsumerWidget {
onTap: () { onTap: () {
final oldCode = ref.read(currencyProvider).code; final oldCode = ref.read(currencyProvider).code;
final rates = ref.read(exchangeRateServiceProvider); final rates = ref.read(exchangeRateServiceProvider);
ref.read(budgetProvider.notifier).onCurrencyChanged(oldCode, code, rates); ref
.read(budgetProvider.notifier)
.onCurrencyChanged(oldCode, code, rates);
ref.read(currencyProvider.notifier).setCurrency(code); ref.read(currencyProvider.notifier).setCurrency(code);
}, },
child: Container( child: Container(
@@ -73,23 +78,50 @@ class CurrencySection extends ConsumerWidget {
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: isSelected border: isSelected
? Border.all(color: AppColors.accent, width: 1.5) ? Border.all(color: AppColors.accent, width: 1.5)
: (isDark ? null : Border.all(color: const Color(0xFFDDDDEE), width: 1)), : (isDark
? null
: Border.all(
color: const Color(0xFFDDDDEE),
width: 1,
)),
), ),
child: Column( child: Column(
children: [ children: [
Text( code == 'BYN'
? BynSign(
fontSize: 28,
color: isSelected
? AppColors.accent
: Theme.of(context).colorScheme.onSurface
.withOpacity(0.6),
)
: Text(
info.symbol, info.symbol,
style: Theme.of(context).textTheme.titleLarge?.copyWith( style: Theme.of(context).textTheme.titleLarge
color: isSelected ? AppColors.accent : Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ?.copyWith(
fontWeight: isSelected ? FontWeight.w700 : FontWeight.normal, color: isSelected
? AppColors.accent
: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.6),
fontWeight: isSelected
? FontWeight.w700
: FontWeight.normal,
), ),
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
Text( Text(
code, code,
style: Theme.of(context).textTheme.bodySmall?.copyWith( style: Theme.of(context).textTheme.bodySmall
color: isSelected ? AppColors.accent : Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ?.copyWith(
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, color: isSelected
? AppColors.accent
: Theme.of(context).colorScheme.onSurface
.withOpacity(0.6),
fontWeight: isSelected
? FontWeight.w600
: FontWeight.normal,
), ),
), ),
], ],
+4 -2
View File
@@ -3,6 +3,8 @@ import '../../core/constants.dart';
String formatAmount(String symbol, double amount, AmountFormat fmt) { String formatAmount(String symbol, double amount, AmountFormat fmt) {
const spaceAfter = {'Br'}; const spaceAfter = {'Br'};
final formatted = fmt.format(amount); final formatted = fmt.format(amount);
final sep = spaceAfter.contains(symbol) ? ' ' : ''; // For BYN, symbol is empty string, so we use 'Br' for text-only contexts like CSV
return '$symbol$sep$formatted'; final displaySymbol = symbol.isEmpty ? 'Br' : symbol;
final sep = spaceAfter.contains(displaySymbol) ? ' ' : '';
return '$displaySymbol$sep$formatted';
} }
+25
View File
@@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
class BynSign extends StatelessWidget {
final double fontSize;
final Color color;
const BynSign({super.key, required this.fontSize, required this.color});
@override
Widget build(BuildContext context) {
return Transform.translate(
offset: Offset(0, fontSize * -0.12),
child: Text(
'\uE901',
style: TextStyle(
fontFamily: 'BynSymbol',
fontSize: fontSize,
color: color,
fontWeight: FontWeight.w700,
height: 1.0,
),
),
);
}
}
+3
View File
@@ -59,6 +59,9 @@ dev_dependencies:
flutter: flutter:
uses-material-design: true uses-material-design: true
fonts: fonts:
- family: BynSymbol
fonts:
- asset: assets/fonts/nbrb.ttf
- family: NunitoCyrillic - family: NunitoCyrillic
fonts: fonts:
- asset: assets/fonts/nunito/Nunito-Medium.ttf - asset: assets/fonts/nunito/Nunito-Medium.ttf