diff --git a/lib/features/dashboard/screens/dashboard_screen.dart b/lib/features/dashboard/screens/dashboard_screen.dart index 8ae880c..7d93790 100644 --- a/lib/features/dashboard/screens/dashboard_screen.dart +++ b/lib/features/dashboard/screens/dashboard_screen.dart @@ -2,6 +2,7 @@ import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:kmobile/data/models/user.dart'; import 'package:kmobile/data/repositories/transaction_repository.dart'; import 'package:kmobile/di/injection.dart'; import 'package:kmobile/features/accounts/screens/account_info_screen.dart'; @@ -35,13 +36,167 @@ class DashboardScreen extends StatefulWidget { class _DashboardScreenState extends State with SingleTickerProviderStateMixin { int selectedAccountIndex = 0; - bool isVisible = false; + Map _visibilityMap = {}; bool isRefreshing = false; bool isBalanceLoading = false; bool _biometricPromptShown = false; bool _txLoading = false; List _transactions = []; bool _txInitialized = false; + PageController? _pageController; + + @override + void dispose() { + _pageController?.dispose(); + super.dispose(); + } + + Widget _buildAccountCard(User user, bool isSelected) { + final theme = Theme.of(context); + final bool isCardVisible = _visibilityMap[user.accountNo] ?? false; + // Animated scale for the selected card + final scale = isSelected ? 1.0 : 0.9; + return AnimatedScale( + duration: const Duration(milliseconds: 200), + scale: scale, + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 8), + padding: const EdgeInsets.symmetric( + horizontal: 18, + vertical: 10, + ), + decoration: BoxDecoration( + color: const Color(0xFF01A04C), + borderRadius: BorderRadius.circular(16), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + getFullAccountType(user.accountType), + style: TextStyle( + color: theme.colorScheme.onPrimary, + fontSize: 16, + fontWeight: FontWeight.w700, + ), + ), + Text( + user.accountNo ?? 'N/A', + style: TextStyle( + color: theme.colorScheme.onPrimary, + fontSize: 14, + fontWeight: FontWeight.w700, // Changed to bold + ), + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + if (isSelected) // Only show refresh for selected card + IconButton( + icon: isRefreshing + ? SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + color: theme.colorScheme.onPrimary, + strokeWidth: 2, + ), + ) + : Icon( + Symbols.refresh, + color: theme.colorScheme.onPrimary, + ), + onPressed: isRefreshing + ? null + : () => _refreshAccountData(context), + tooltip: 'Refresh', + ), + ], + ), + const Spacer(), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + "₹ ", + style: TextStyle( + color: theme.colorScheme.onPrimary, + fontSize: 32, // Adjusted for space + fontWeight: FontWeight.w700, + ), + ), + if (isRefreshing || (isBalanceLoading && isSelected)) + Expanded(child: _buildBalanceShimmer()) + else + Expanded( + child: FittedBox( + fit: BoxFit.scaleDown, + alignment: Alignment.centerLeft, + child: Text( + isCardVisible ? user.currentBalance ?? '0.00' : '*****', + style: TextStyle( + color: theme.colorScheme.onPrimary, + fontSize: 32, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + const Spacer(), + if (isSelected) // Only show toggle for selected card + InkWell( + onTap: () async { + if (isBalanceLoading) return; + final accountNo = user.accountNo; + if (accountNo == null) return; + + final bool currentVisibility = + _visibilityMap[accountNo] ?? false; + + if (!currentVisibility) { + // If it's currently hidden, we show it after a delay + setState(() { + isBalanceLoading = true; + }); + await Future.delayed( + const Duration(milliseconds: 500)); // Shorter delay + setState(() { + _visibilityMap[accountNo] = true; + isBalanceLoading = false; + }); + } else { + // If it's currently visible, we hide it immediately + setState(() { + _visibilityMap[accountNo] = false; + }); + } + }, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Icon( + isCardVisible + ? Symbols.visibility_lock + : Symbols.visibility, + color: theme.scaffoldBackgroundColor, + weight: 800, + ), + ), + ), + ], + ), + const Spacer(), + ], + ), + ), + ); + } Future _loadTransactions(String accountNo) async { setState(() { @@ -104,7 +259,7 @@ class _DashboardScreenState extends State baseColor: theme.colorScheme.primary, highlightColor: theme.colorScheme.onPrimary, child: Container( - width: 200, height: 42, color: theme.scaffoldBackgroundColor), + height: 36, color: theme.scaffoldBackgroundColor), ); } @@ -286,6 +441,10 @@ class _DashboardScreenState extends State _loadTransactions(currAccount.accountNo!); }); } + _pageController ??= PageController( + initialPage: selectedAccountIndex, + viewportFraction: 0.85, + ); final firstName = getProcessedFirstName(currAccount.name); return SingleChildScrollView( @@ -307,162 +466,34 @@ class _DashboardScreenState extends State ), const SizedBox(height: 16), - // Account Info Card - Container( - padding: const EdgeInsets.symmetric( - horizontal: 18, - vertical: 10, - ), - decoration: BoxDecoration( - color: Color(0xFF01A04C), - borderRadius: BorderRadius.circular(16), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Text( - "${getFullAccountType(currAccount.accountType)}: ", - style: TextStyle( - color: theme.colorScheme.onPrimary, - fontSize: 18, - fontWeight: FontWeight.w700, - ), - ), - DropdownButton( - value: selectedAccountIndex, - dropdownColor: theme.colorScheme.primary, - underline: const SizedBox(), - icon: const Icon(Icons.keyboard_arrow_down), - iconEnabledColor: theme.colorScheme.onPrimary, - style: TextStyle( - color: theme.colorScheme.onPrimary, - fontSize: 18, - ), - items: List.generate(users.length, (index) { - return DropdownMenuItem( - value: index, - child: Text( - users[index].accountNo ?? 'N/A', - style: TextStyle( - color: theme.colorScheme.onPrimary, - fontSize: 18, - fontWeight: FontWeight.w700, - ), - ), - ); - }), - onChanged: (int? newIndex) async { - if (newIndex == null || - newIndex == selectedAccountIndex) { - return; - } - if (isBalanceLoading) return; - if (isVisible) { - setState(() { - isBalanceLoading = true; - selectedAccountIndex = newIndex; - }); - await Future.delayed( - const Duration(milliseconds: 200), - ); - setState(() { - isBalanceLoading = false; - }); - } else { - setState(() { - selectedAccountIndex = newIndex; - }); - } - await _loadTransactions( - users[newIndex].accountNo!, - ); - }, - ), - const Spacer(), - IconButton( - icon: isRefreshing - ? SizedBox( - width: 20, - height: 20, - child: CircularProgressIndicator( - color: theme.colorScheme.onPrimary, - strokeWidth: 2, - ), - ) - : Icon( - Symbols.refresh, - color: theme.colorScheme.onPrimary, - ), - onPressed: isRefreshing - ? null - : () => _refreshAccountData(context), - tooltip: 'Refresh', - ), - ], - ), - const SizedBox(height: 15), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - "₹ ", - style: TextStyle( - color: theme.colorScheme.onPrimary, - fontSize: 40, - fontWeight: FontWeight.w700, - ), - ), - isRefreshing || isBalanceLoading - ? _buildBalanceShimmer() - : Text( - isVisible - ? currAccount.currentBalance ?? - '0.00' - : '*****', - style: TextStyle( - color: theme.colorScheme.onPrimary, - fontSize: 40, - fontWeight: FontWeight.w700, - ), - ), - const Spacer(), - InkWell( - onTap: () async { - if (isBalanceLoading) return; - if (!isVisible) { - setState(() { - isBalanceLoading = true; - }); - await Future.delayed( - const Duration(seconds: 1), - ); - setState(() { - isVisible = true; - isBalanceLoading = false; - }); - } else { - setState(() { - isVisible = false; - }); - } - }, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Icon( - isVisible - ? Symbols.visibility_lock - : Symbols.visibility, - color: theme.scaffoldBackgroundColor, - weight: 800, - ), - ), - ), - ], - ), - const SizedBox(height: 15), - ], + // Account Info Cards + SizedBox( + height: 160, // Adjusted height + child: PageView.builder( + controller: _pageController, + itemCount: users.length, + onPageChanged: (int newIndex) async { + if (newIndex == selectedAccountIndex) return; + + // Hide the balance of the old card when scrolling away + final oldAccountNo = users[selectedAccountIndex].accountNo; + if (oldAccountNo != null) { + _visibilityMap[oldAccountNo] = false; + } + + setState(() { + selectedAccountIndex = newIndex; + }); + + await _loadTransactions( + users[newIndex].accountNo!, + ); + }, + itemBuilder: (context, index) { + final user = users[index]; + final isSelected = index == selectedAccountIndex; + return _buildAccountCard(user, isSelected); + }, ), ), const SizedBox(height: 18),