From ceea63812b54528bcc6ab60ff1353b2ab52c4e7d Mon Sep 17 00:00:00 2001 From: kolo Date: Wed, 25 Mar 2026 01:01:40 +0300 Subject: [PATCH] update --- .../widgets/balance_card_carousel.dart | 76 ++++++++++------- .../dashboard/widgets/transaction_tile.dart | 84 +++++++++++++++++-- 2 files changed, 121 insertions(+), 39 deletions(-) diff --git a/lib/features/dashboard/widgets/balance_card_carousel.dart b/lib/features/dashboard/widgets/balance_card_carousel.dart index 0c8ebff..7b50549 100644 --- a/lib/features/dashboard/widgets/balance_card_carousel.dart +++ b/lib/features/dashboard/widgets/balance_card_carousel.dart @@ -42,7 +42,11 @@ class _BalanceCardCarouselState extends ConsumerState { void initState() { super.initState(); // 0.92 позволяет видеть края предыдущей/следующей карточки - _pageController = PageController(viewportFraction: 0.92); + final savedIndex = ref.read(activeAccountIndexProvider); + _pageController = PageController( + viewportFraction: 0.92, + initialPage: savedIndex, + ); } @override @@ -70,7 +74,8 @@ class _BalanceCardCarouselState extends ConsumerState { maxWidth: MediaQuery.of(context).size.width, child: PageView.builder( controller: _pageController, - clipBehavior: Clip.none, // Не обрезает карточку при 3D-наклоне + clipBehavior: + Clip.none, // Не обрезает карточку при 3D-наклоне itemCount: totalPages, onPageChanged: (index) { ref.read(activeAccountIndexProvider.notifier).state = index; @@ -92,28 +97,40 @@ class _BalanceCardCarouselState extends ConsumerState { ); } else if (index <= accounts.length) { final account = accounts[index - 1]; - final accountColors = ref.watch(accountCardColorsProvider(account.id)); - + final accountColors = ref.watch( + accountCardColorsProvider(account.id), + ); + // Calculate this specific account's balance - final txs = ref.watch(transactionsProvider).valueOrNull ?? []; - final accountTxs = txs.where((t) => t.accountId == account.id).toList(); - final exchangeService = ref.watch(exchangeRateServiceProvider); + final txs = + ref.watch(transactionsProvider).valueOrNull ?? []; + final accountTxs = txs + .where((t) => t.accountId == account.id) + .toList(); + final exchangeService = ref.watch( + exchangeRateServiceProvider, + ); final accountBalance = accountTxs.fold(0.0, (sum, t) { final converted = exchangeService.convert( t.amount, t.currencyCode, - account.currency, // target is the account's own currency + account + .currency, // target is the account's own currency ); - return t.type == TransactionType.income ? sum + converted : sum - converted; + return t.type == TransactionType.income + ? sum + converted + : sum - converted; }); - + cardWidget = BalanceCard( - balance: accountBalance, // Use the dynamically calculated balance! + balance: + accountBalance, // Use the dynamically calculated balance! currencyInfo: CurrencyInfo( currencyMap[account.currency]?.symbol ?? '\$', account.currency, ), - onLongPress: () => widget.onAccountLongPress?.call(account), + onLongPress: () => + widget.onAccountLongPress?.call(account), accountName: account.name, accountColors: accountColors, ); @@ -133,10 +150,7 @@ class _BalanceCardCarouselState extends ConsumerState { ), ), const SizedBox(height: 12), - _DotIndicators( - count: totalPages, - activeIndex: activeIndex, - ), + _DotIndicators(count: totalPages, activeIndex: activeIndex), ], ); }, @@ -159,10 +173,7 @@ class _BalanceCardCarouselState extends ConsumerState { ), ), const SizedBox(height: 12), - const _DotIndicators( - count: 1, - activeIndex: 0, - ), + const _DotIndicators(count: 1, activeIndex: 0), ], ); }, @@ -180,7 +191,10 @@ class AddAccountCard extends StatelessWidget { return GestureDetector( onTap: onTap, child: Container( - margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), // Reduced margins for larger size + margin: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 10, + ), // Reduced margins for larger size child: CustomPaint( painter: _DashedBorderPainter(), child: Container( @@ -196,14 +210,18 @@ class AddAccountCard extends StatelessWidget { Icon( Icons.add_rounded, size: 36, // Slightly bigger icon - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5), + color: Theme.of( + context, + ).colorScheme.onSurface.withOpacity(0.5), ), const SizedBox(height: 8), Text( 'Add account', style: TextStyle( fontSize: 15, - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5), + color: Theme.of( + context, + ).colorScheme.onSurface.withOpacity(0.5), fontWeight: FontWeight.w500, ), ), @@ -240,10 +258,7 @@ class _DashedBorderPainter extends CustomPainter { for (final metric in pathMetrics) { double distance = 0; while (distance < metric.length) { - final segment = metric.extractPath( - distance, - distance + dashWidth, - ); + final segment = metric.extractPath(distance, distance + dashWidth); canvas.drawPath(segment, paint); distance += dashWidth + dashSpace; } @@ -258,10 +273,7 @@ class _DotIndicators extends StatelessWidget { final int count; final int activeIndex; - const _DotIndicators({ - required this.count, - required this.activeIndex, - }); + const _DotIndicators({required this.count, required this.activeIndex}); @override Widget build(BuildContext context) { @@ -284,4 +296,4 @@ class _DotIndicators extends StatelessWidget { }), ); } -} \ No newline at end of file +} diff --git a/lib/features/dashboard/widgets/transaction_tile.dart b/lib/features/dashboard/widgets/transaction_tile.dart index 49e3e38..7c86a05 100644 --- a/lib/features/dashboard/widgets/transaction_tile.dart +++ b/lib/features/dashboard/widgets/transaction_tile.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; @@ -8,6 +9,7 @@ import '../../../core/l10n/locale_provider.dart'; import '../../../shared/models/transaction.dart'; import '../../../shared/providers/amount_format_provider.dart'; import '../../../shared/utils/currency_utils.dart'; +import '../provider.dart'; class TransactionTile extends ConsumerWidget { final Transaction transaction; @@ -32,6 +34,21 @@ class TransactionTile extends ConsumerWidget { final catIcon = AppCategories.icons[transaction.category] ?? Icons.category_rounded; + // Check if we're on Total Balance page + final activeAccount = ref.watch(activeAccountProvider); + + // Look up the account name by matching transaction.accountId + final accounts = ref.watch(accountsProvider).valueOrNull ?? []; + final txAccount = accounts.firstWhereOrNull( + (a) => a.id == transaction.accountId, + ); + + // Build account label with 10-character limit + String accountLabel = txAccount?.name ?? ''; + if (accountLabel.length > 10) { + accountLabel = '${accountLabel.substring(0, 10)}...'; + } + return GestureDetector( onTap: () => context.push('/add', extra: transaction), child: Container( @@ -56,12 +73,24 @@ class TransactionTile extends ConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - s.categoryLabel(transaction.category), - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - color: Theme.of(context).colorScheme.onSurface, - ), + Row( + children: [ + Flexible( + child: Text( + s.categoryLabel(transaction.category), + style: Theme.of(context).textTheme.bodyMedium + ?.copyWith( + fontWeight: FontWeight.w600, + color: Theme.of(context).colorScheme.onSurface, + ), + overflow: TextOverflow.ellipsis, + ), + ), + if (activeAccount == null && accountLabel.isNotEmpty) ...[ + const SizedBox(width: 6), + _AccountTag(label: accountLabel), + ], + ], ), if (transaction.note != null && transaction.note!.isNotEmpty) Text( @@ -75,7 +104,10 @@ class TransactionTile extends ConsumerWidget { ) else Text( - DateFormat('d MMM yyyy · HH:mm', s.dateLocale).format(transaction.date), + DateFormat( + 'd MMM yyyy · HH:mm', + s.dateLocale, + ).format(transaction.date), style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of( context, @@ -99,6 +131,44 @@ class TransactionTile extends ConsumerWidget { } } +class _AccountTag extends StatelessWidget { + final String label; + const _AccountTag({required this.label}); + + @override + Widget build(BuildContext context) { + final isDark = Theme.of(context).brightness == Brightness.dark; + return IntrinsicWidth( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2), + decoration: BoxDecoration( + color: isDark + ? Colors.white.withOpacity(0.08) + : const Color(0xFFF0EFFE), + borderRadius: BorderRadius.circular(6), + border: Border.all( + color: isDark + ? Colors.white.withOpacity(0.12) + : const Color(0xFFD0CAFF), + width: 1, + ), + ), + child: Text( + label, + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.w500, + color: isDark + ? Colors.white.withOpacity(0.55) + : const Color(0xFF7C6DED), + letterSpacing: 0.1, + ), + ), + ), + ); + } +} + class EmptyState extends StatelessWidget { final AppStrings strings; const EmptyState({super.key, required this.strings});