From ca5bfa7258739374896ef4e0bb0a713cd83624f7 Mon Sep 17 00:00:00 2001 From: kolo Date: Sun, 22 Mar 2026 01:35:21 +0300 Subject: [PATCH] stableee --- lib/core/l10n/app_strings.dart | 2 + lib/features/categories/provider.dart | 11 ++++ lib/features/categories/screen.dart | 93 +++++++++++++++++++++++++-- 3 files changed, 100 insertions(+), 6 deletions(-) diff --git a/lib/core/l10n/app_strings.dart b/lib/core/l10n/app_strings.dart index e893bbc..15b4af9 100644 --- a/lib/core/l10n/app_strings.dart +++ b/lib/core/l10n/app_strings.dart @@ -98,6 +98,8 @@ class AppStrings { String get noCategoriesYet => _ru ? 'Нет категорий' : 'No categories yet'; String get noExpenseData => _ru ? 'Нет данных о расходах' : 'No expense data'; String get addExpensesToSeeBreakdown => _ru ? 'Добавьте расходы, чтобы увидеть разбивку' : 'Add some expenses to see the breakdown'; + String get noIncomeData => _ru ? 'Нет данных о доходах' : 'No income data'; + String get addIncomeToSeeBreakdown => _ru ? 'Добавьте доходы, чтобы увидеть разбивку' : 'Add some income to see the breakdown'; String get total => _ru ? 'Всего' : 'Total'; String get lastSixMonths => _ru ? 'Последние 6 месяцев' : 'Last 6 Months'; diff --git a/lib/features/categories/provider.dart b/lib/features/categories/provider.dart index 90fb1fd..176cc86 100644 --- a/lib/features/categories/provider.dart +++ b/lib/features/categories/provider.dart @@ -13,6 +13,17 @@ final categoryExpenseProvider = Provider>((ref) { return map; }); +final categoryIncomeProvider = Provider>((ref) { + final txs = ref.watch(transactionsProvider) + .where((t) => t.type == TransactionType.income); + + final map = {}; + for (final t in txs) { + map[t.category] = (map[t.category] ?? 0) + t.amount; + } + return map; +}); + final monthlyBreakdownProvider = Provider>((ref) { final txs = ref.watch(transactionsProvider) .where((t) => t.type == TransactionType.expense); diff --git a/lib/features/categories/screen.dart b/lib/features/categories/screen.dart index 2f307b6..76f90f7 100644 --- a/lib/features/categories/screen.dart +++ b/lib/features/categories/screen.dart @@ -21,11 +21,14 @@ class CategoriesScreen extends ConsumerStatefulWidget { class _CategoriesScreenState extends ConsumerState { int _touchedIndex = -1; ChartType _chartType = ChartType.pie; + bool _showIncome = false; @override Widget build(BuildContext context) { final s = ref.watch(stringsProvider); - final data = ref.watch(categoryExpenseProvider); + final data = _showIncome + ? ref.watch(categoryIncomeProvider) + : ref.watch(categoryExpenseProvider); final monthlyData = ref.watch(monthlyBreakdownProvider); final total = data.values.fold(0.0, (a, b) => a + b); final currencyInfo = ref.watch(currencyProvider); @@ -57,8 +60,82 @@ class _CategoriesScreenState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + Row( + children: [ + Expanded( + child: GestureDetector( + onTap: () => setState(() { + _showIncome = false; + _touchedIndex = -1; + }), + child: Container( + padding: const EdgeInsets.symmetric(vertical: 10), + decoration: BoxDecoration( + color: !_showIncome + ? AppColors.accent + : Colors.transparent, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: !_showIncome + ? AppColors.accent + : Theme.of(context).colorScheme.onSurface.withOpacity(0.2), + width: 1.5, + ), + ), + child: Text( + s.expenses, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: !_showIncome + ? Colors.white + : Theme.of(context).colorScheme.onSurface.withOpacity(0.6), + ), + ), + ), + ), + ), + const SizedBox(width: 12), + Expanded( + child: GestureDetector( + onTap: () => setState(() { + _showIncome = true; + _touchedIndex = -1; + }), + child: Container( + padding: const EdgeInsets.symmetric(vertical: 10), + decoration: BoxDecoration( + color: _showIncome + ? AppColors.accent + : Colors.transparent, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: _showIncome + ? AppColors.accent + : Theme.of(context).colorScheme.onSurface.withOpacity(0.2), + width: 1.5, + ), + ), + child: Text( + s.income, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: _showIncome + ? Colors.white + : Theme.of(context).colorScheme.onSurface.withOpacity(0.6), + ), + ), + ), + ), + ), + ], + ), + const SizedBox(height: 16), if (data.isEmpty) - const Expanded(child: _EmptyState()) + Expanded(child: _EmptyState(isIncome: _showIncome)) else Expanded( child: ListView( @@ -94,6 +171,7 @@ class _CategoriesScreenState extends ConsumerState { amount: amount, total: total, currency: currencyInfo.symbol, + isIncome: _showIncome, ), ); }), @@ -390,12 +468,14 @@ class _CategoryRow extends ConsumerWidget { final double amount; final double total; final String currency; + final bool isIncome; const _CategoryRow({ required this.rank, required this.category, required this.amount, required this.total, required this.currency, + required this.isIncome, }); @override @@ -457,7 +537,7 @@ class _CategoryRow extends ConsumerWidget { Text( formatAmount(currency, amount, fmt), style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: AppColors.expense, + color: isIncome ? AppColors.income : AppColors.expense, fontWeight: FontWeight.w700, ), ), @@ -488,7 +568,8 @@ class _CategoryRow extends ConsumerWidget { } class _EmptyState extends ConsumerWidget { - const _EmptyState(); + final bool isIncome; + const _EmptyState({required this.isIncome}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -511,7 +592,7 @@ class _EmptyState extends ConsumerWidget { ), const SizedBox(height: 16), Text( - s.noExpenseData, + isIncome ? s.noIncomeData : s.noExpenseData, style: Theme.of(context).textTheme.titleMedium?.copyWith( color: Theme.of(context).colorScheme.onSurface, fontWeight: FontWeight.w600, @@ -519,7 +600,7 @@ class _EmptyState extends ConsumerWidget { ), const SizedBox(height: 6), Text( - s.addExpensesToSeeBreakdown, + isIncome ? s.addIncomeToSeeBreakdown : s.addExpensesToSeeBreakdown, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ),