// lib/screens/dashboard_screen.dart import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:intl/intl.dart'; import 'package:flutter/painting.dart'; import 'dart:ui'; import 'dart:math' as math; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; // ✅ Importação do Font Awesome import 'package:kzeduca_app/services/i18n_service.dart'; import 'package:kzeduca_app/screens/add_transaction_screen.dart'; import 'package:kzeduca_app/screens/savings_simulator_screen.dart'; import 'package:kzeduca_app/screens/lessons_screen.dart'; import 'package:kzeduca_app/screens/settings_screen.dart'; import 'package:kzeduca_app/screens/budget_screen.dart'; import 'package:kzeduca_app/screens/statistics_screen.dart'; import 'package:kzeduca_app/screens/challenges_screen.dart'; import 'package:kzeduca_app/screens/quiz_screen.dart'; import 'package:kzeduca_app/screens/receive_history_screen.dart'; import 'package:kzeduca_app/screens/notification_settings_screen.dart'; import 'package:kzeduca_app/screens/user_profile_screen.dart'; import 'package:kzeduca_app/services/user_state_service.dart'; import 'package:kzeduca_app/models/app_models.dart' as app_models; import 'package:kzeduca_app/models/transaction.dart'; import 'package:kzeduca_app/services/hive_service.dart'; import 'package:kzeduca_app/services/transaction_list_notifier.dart'; import 'package:firebase_auth/firebase_auth.dart'; class DashboardScreen extends StatefulWidget { const DashboardScreen({super.key}); @override State createState() => _DashboardScreenState(); } class _DashboardScreenState extends State { int _selectedIndex = 0; @override void initState() { super.initState(); } void _handleCurrencyChange(String newCurrency) { print("Moeda alterada para: $newCurrency"); } void _onItemTapped(int index) { setState(() { _selectedIndex = index; }); } // ✅ _buildNavItem modificado para aceitar IconData ou FaIcon Widget _buildNavItem(int index, dynamic icon, String label) { bool isSelected = _selectedIndex == index; return Material( color: Colors.transparent, child: InkWell( onTap: () => _onItemTapped(index), child: Padding( padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 2.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( padding: const EdgeInsets.all(6.0), decoration: isSelected ? BoxDecoration( shape: BoxShape.circle, gradient: const LinearGradient( colors: [Color(0xFF8A2BE2), Color(0xFFDA70D6)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), boxShadow: [ BoxShadow( color: const Color(0xFFDA70D6).withOpacity(0.5), spreadRadius: 1, blurRadius: 5, offset: const Offset(0, 3), ), ], ) : null, child: icon is IconData ? Icon( icon, color: isSelected ? Colors.white : Colors.white70, size: 22, ) : icon, // ✅ Aceita o widget FaIcon diretamente ), const SizedBox(height: 2), Text( label, style: TextStyle( color: isSelected ? Colors.white : Colors.white70, fontSize: 8, ), ), ], ), ), ), ); } @override Widget build(BuildContext context) { final i18n = Provider.of(context); final List widgetOptions = [ const _DashboardMainContent(), const BudgetScreen(), const StatisticsScreen(), const UserProfileScreen(), ]; return Scaffold( backgroundColor: const Color(0xFF1E1C3A), appBar: PreferredSize( preferredSize: const Size.fromHeight(kToolbarHeight), child: Container( decoration: BoxDecoration( gradient: const LinearGradient( colors: [Color(0xFF1E1C3A), Color(0xFF1E1C3A)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), spreadRadius: 2, blurRadius: 8, offset: const Offset(0, 4), ), ], ), child: AppBar( backgroundColor: Colors.transparent, elevation: 0, leading: Builder( builder: (context) => IconButton( icon: const Icon(Icons.menu, color: Colors.white), onPressed: () { Scaffold.of(context).openDrawer(); }, ), ), actions: [ IconButton( // ✅ Ícone Font Awesome para Idioma icon: const Icon(FontAwesomeIcons.globe, color: Colors.white, size: 20), tooltip: i18n.t('change_language'), onPressed: () { final i18nService = Provider.of(context, listen: false); final nextLocale = i18nService.locale.languageCode == 'pt' ? const Locale('en') : const Locale('pt'); i18nService.setLocale(nextLocale); }, ), IconButton( // ✅ Ícone Font Awesome para Notificações icon: const Icon(FontAwesomeIcons.bell, color: Colors.white, size: 20), tooltip: i18n.t('notifications'), onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (_) => const NotificationSettingsScreen()), ); }, ), IconButton( // ✅ Ícone Font Awesome para Perfil icon: const Icon(FontAwesomeIcons.solidUser, color: Colors.white, size: 20), tooltip: i18n.t('profile'), onPressed: () { setState(() { _selectedIndex = 3; }); }, ), ], ), ), ), drawer: Drawer( backgroundColor: const Color(0xFF1E1C3A), child: ListView( padding: EdgeInsets.zero, children: [ const DrawerHeader( decoration: BoxDecoration( gradient: LinearGradient( colors: [Color(0xFF8A2BE2), Color(0xFFDA70D6)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), ), child: Text( 'Menu', style: TextStyle( color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold, ), ), ), ListTile( // ✅ Ícone Font Awesome para Configurações leading: const Icon(FontAwesomeIcons.gear, color: Colors.white), title: Text(i18n.t('settings_menu_item'), style: const TextStyle(color: Colors.white)), onTap: () { Navigator.pop(context); Navigator.push( context, MaterialPageRoute( builder: (_) => SettingsScreen( onCurrencyChanged: _handleCurrencyChange, ), ), ); }, ), ListTile( // ✅ Ícone Font Awesome para Notificações leading: const Icon(FontAwesomeIcons.bell, color: Colors.white), title: Text(i18n.t('notifications'), style: const TextStyle(color: Colors.white)), onTap: () { Navigator.pop(context); Navigator.push( context, MaterialPageRoute(builder: (_) => const NotificationSettingsScreen()), ); }, ), ListTile( // ✅ Ícone Font Awesome para Perfil leading: const Icon(FontAwesomeIcons.solidUser, color: Colors.white), title: Text(i18n.t('profile'), style: const TextStyle(color: Colors.white)), onTap: () { Navigator.pop(context); setState(() { _selectedIndex = 3; }); }, ), ], ), ), body: SafeArea( child: widgetOptions.elementAt(_selectedIndex), ), floatingActionButton: FloatingActionButton( onPressed: () async { final result = await Navigator.push( context, MaterialPageRoute(builder: (_) => const AddTransactionScreen()), ); if (result == true) { setState(() {}); } }, shape: const CircleBorder(), backgroundColor: Colors.transparent, elevation: 0, child: Container( width: 60, height: 60, decoration: BoxDecoration( shape: BoxShape.circle, gradient: const LinearGradient( colors: [Color(0xFF8A2BE2), Color(0xFFDA70D6)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), boxShadow: [ BoxShadow( color: const Color(0xFFDA70D6).withOpacity(0.5), spreadRadius: 2, blurRadius: 10, offset: const Offset(0, 5), ), ], ), child: const Icon(Icons.add, color: Colors.white, size: 30), ), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, bottomNavigationBar: BottomAppBar( color: const Color(0xFF1E1C3A), shape: const CircularNotchedRectangle(), notchMargin: 8.0, child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ // ✅ Ícones Font Awesome para a BottomNavigationBar _buildNavItem(0, const Icon(FontAwesomeIcons.house, color: Colors.white, size: 22), i18n.t('home')), _buildNavItem(1, const Icon(FontAwesomeIcons.wallet, color: Colors.white, size: 22), i18n.t('transactions_menu_item')), _buildNavItem(2, const Icon(FontAwesomeIcons.chartBar, color: Colors.white, size: 22), i18n.t('statistics_menu_item')), _buildNavItem(3, const Icon(FontAwesomeIcons.solidUser, color: Colors.white, size: 22), i18n.t('profile')), ], ), ), ); } } class _DashboardMainContent extends StatelessWidget { const _DashboardMainContent(); @override Widget build(BuildContext context) { final i18n = Provider.of(context); return Consumer( builder: (context, userState, child) { if (userState.status == UserStateStatus.loading) { return const Center( child: CircularProgressIndicator(), ); } if (userState.status == UserStateStatus.error) { return Center( child: Text( userState.errorMessage ?? 'Não foi possível carregar os dados do usuário.', style: const TextStyle(color: Colors.white), textAlign: TextAlign.center, ), ); } final user = userState.currentUser; if (user == null) { return Center( child: Text( i18n.t('user_not_found'), style: const TextStyle(color: Colors.white), ), ); } final locale = Localizations.localeOf(context).languageCode; final currencySymbol = 'Kz'; final formattedBalance = NumberFormat.currency( locale: locale, symbol: currencySymbol, decimalDigits: 2, ).format(user.currentBalance); final formattedDate = DateFormat.yMMMd(locale).format(DateTime.now()); return SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ _KzeducaCardRealistic(user: user), const SizedBox(height: 24), const SizedBox(height: 16), GridView.count( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), crossAxisCount: MediaQuery.of(context).size.width < 600 ? 4 : 4, mainAxisSpacing: 16, crossAxisSpacing: 16, children: [ // ✅ Ícones Font Awesome para os atalhos _QuickActionIcon( icon: FontAwesomeIcons.rightLeft, // ✅ Ícone para Transferir/Trocar label: i18n.t('transfer'), onTap: () => Navigator.push( context, MaterialPageRoute( builder: (_) => const AddTransactionScreen()))), _QuickActionIcon( icon: FontAwesomeIcons.arrowDown, // ✅ Ícone para Receber label: i18n.t('receive'), onTap: () => Navigator.push( context, MaterialPageRoute(builder: (_) => const ReceiveHistoryScreen()), )), _QuickActionIcon( icon: FontAwesomeIcons.gamepad, // ✅ Ícone para Agente label: "Jogo", onTap: () => Navigator.pushNamed(context, '/financial_agent')), _QuickActionIcon( icon: FontAwesomeIcons.sackDollar, // ✅ Ícone para Poupança label: i18n.t('simulator'), onTap: () => Navigator.push( context, MaterialPageRoute( builder: (_) => const SavingsSimulatorScreen()))), _QuickActionIcon( icon: FontAwesomeIcons.book, // ✅ Ícone para Aulas label: i18n.t('lessons'), onTap: () => Navigator.push( context, MaterialPageRoute( builder: (_) => const LessonsScreen()))), _QuickActionIcon( icon: FontAwesomeIcons.trophy, // ✅ Ícone para Desafios label: i18n.t('challenges'), onTap: () => Navigator.push( context, MaterialPageRoute( builder: (_) => const ChallengesScreen()))), _QuickActionIcon( icon: FontAwesomeIcons.question, // ✅ Ícone para Quiz label: i18n.t('quiz'), onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const QuizScreen()))), _QuickActionIcon( icon: FontAwesomeIcons.moneyBillTrendUp, // ✅ Ícone para Orçamento label: i18n.t('budget'), onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const BudgetScreen()))), ], ), const SizedBox(height: 32), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( i18n.t('Historic'), style: const TextStyle( color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold, ), overflow: TextOverflow.ellipsis, ), ), ], ), const SizedBox(height: 16), Consumer( builder: (context, txnList, _) { final transactions = txnList.transactions; if (transactions.isEmpty) { return const Text('Nenhuma transação encontrada.', style: TextStyle(color: Colors.white70)); } return ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: transactions.length < 3 ? transactions.length : 3, itemBuilder: (context, index) { final transaction = transactions[transactions.length - 1 - index]; final color = transaction.type == TransactionType.income ? Colors.green : Colors.red; final sign = transaction.type == TransactionType.income ? '+' : '-'; final formattedAmount = NumberFormat.currency( locale: locale, symbol: currencySymbol, decimalDigits: 2, ).format(transaction.amount); final formattedDate = DateFormat.MMMMd(locale).format(transaction.date); return Padding( padding: const EdgeInsets.only(bottom: 16.0), child: Container( decoration: BoxDecoration( color: const Color(0xFF2A284B), borderRadius: BorderRadius.circular(16), ), child: ListTile( // ✅ Ícone Font Awesome para a transação leading: Icon(FontAwesomeIcons.moneyBillTransfer, color: color), title: Text(transaction.title, style: const TextStyle(color: Colors.white)), subtitle: Text(formattedDate, style: const TextStyle(color: Colors.white70)), trailing: Text('$sign$formattedAmount', style: TextStyle(color: color, fontWeight: FontWeight.bold)), ), ), ); }, ); }, ), ], ), ), ); }, ); } } // ✅ NOVO WIDGET para os ícones de ação rápida, para evitar repetição de código. class _QuickActionIcon extends StatelessWidget { final IconData? icon; final String label; final VoidCallback onTap; const _QuickActionIcon({ Key? key, required this.icon, required this.label, required this.onTap, }) : super(key: key); @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: Column( children: [ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( gradient: const LinearGradient( colors: [Color(0xFF2A284B), Color(0xFF1E1C3A)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.3), spreadRadius: 2, blurRadius: 10, offset: const Offset(0, 5), ), ], ), child: Icon( icon, color: const Color(0xFFDA70D6), size: 28, ), ), const SizedBox(height: 8), Text( label, style: const TextStyle(color: Colors.white70, fontSize: 12), textAlign: TextAlign.center, ), ], ), ); } } // ... (Restante do código _KzeducaCardRealistic, CardPatternPainter, etc.) class _KzeducaCardRealistic extends StatefulWidget { final app_models.AppUser user; const _KzeducaCardRealistic({Key? key, required this.user}) : super(key: key); @override State<_KzeducaCardRealistic> createState() => _KzeducaCardRealisticState(); } class _KzeducaCardRealisticState extends State<_KzeducaCardRealistic> { bool _isBalanceVisible = true; String _cardNumber = ''; @override void initState() { super.initState(); _cardNumber = _generateCardNumber(); } String _generateCardNumber() { final math.Random random = math.Random(); String number = ''; for (int i = 0; i < 12; i++) { number += random.nextInt(10).toString(); } return '${number.substring(0, 4)} ${number.substring(4, 8)} ${number.substring(8, 12)} ${number.substring(12, 12)}'; } @override Widget build(BuildContext context) { final locale = Localizations.localeOf(context).languageCode; final currencySymbol = 'Kz'; final formattedBalance = NumberFormat.currency( locale: locale, symbol: currencySymbol, decimalDigits: 2, ).format(widget.user.currentBalance); final String displayBalance = _isBalanceVisible ? formattedBalance : 'Kz ****'; final balanceIcon = _isBalanceVisible ? Icons.visibility : Icons.visibility_off; final now = DateTime.now(); final expiryDate = DateFormat('MM/yy').format(DateTime(now.year + 5, now.month)); return Container( height: 220, width: double.infinity, decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), gradient: const RadialGradient( colors: [ Color(0xFF6A1B9A), Color(0xFF8E24AA), Color(0xFF4A148C), Color(0xFF263238), ], center: Alignment.centerRight, radius: 1.5, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.6), spreadRadius: 3, blurRadius: 15, offset: const Offset(0, 8), ), BoxShadow( color: const Color(0xFFDA70D6).withOpacity(0.3), spreadRadius: 1, blurRadius: 5, offset: const Offset(0, 2), ), ], ), child: ClipRRect( borderRadius: BorderRadius.circular(20), child: CustomPaint( painter: CardPatternPainter(), child: Padding( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'KzEduca', style: TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold, letterSpacing: 1.2, ), ), Text( 'Aprende. Poupa. Progride.', style: TextStyle( color: Colors.white.withOpacity(0.8), fontSize: 10, fontStyle: FontStyle.italic), ), ], ), Text( expiryDate, style: TextStyle( color: Colors.white.withOpacity(0.8), fontSize: 12, fontWeight: FontWeight.w500, ), ), ], ), const Spacer(flex: 1), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox( width: 50, height: 35, child: CustomPaint( painter: ChipEMVPainter(), ), ), GestureDetector( onTap: () { setState(() { _isBalanceVisible = !_isBalanceVisible; }); }, child: Row( children: [ Text( displayBalance, style: const TextStyle( color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold, ), ), const SizedBox(width: 8), //Icon(balanceIcon, color: Colors.white, size: 20), ], ), ), ], ), const Spacer(flex: 1), Text( _cardNumber, style: const TextStyle( color: Colors.white, fontSize: 20, letterSpacing: 4, fontWeight: FontWeight.w500, ), ), const Spacer(flex: 1), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.user.username?.toUpperCase() ?? 'UTILIZADOR', style: const TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.w600, letterSpacing: 1.0, ), ), Text( 'UID: ${widget.user.uid}', style: const TextStyle( color: Colors.white70, fontSize: 10, ), ), ], ), SizedBox( width: 40, height: 30, child: CustomPaint( painter: CardLogoPainter(), ), ), ], ), ], ), ), ), ), ); } } class CardPatternPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = Colors.white.withOpacity(0.08) ..style = PaintingStyle.stroke ..strokeWidth = 0.8; for (int i = -20; i < size.width + 20; i += 30) { canvas.drawLine(Offset(i.toDouble(), 0), Offset(i.toDouble() - size.height, size.height), paint); } for (int i = 0; i < size.height; i += 30) { canvas.drawLine(Offset(0, i.toDouble()), Offset(size.width, i.toDouble() - size.width), paint); } final circlePaint = Paint() ..color = Colors.white.withOpacity(0.04) ..style = PaintingStyle.fill; final random = math.Random(); for (int i = 0; i < 30; i++) { final x = random.nextDouble() * size.width; final y = random.nextDouble() * size.height; canvas.drawCircle(Offset(x, y), random.nextDouble() * 1.5 + 0.5, circlePaint); } } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; } class ChipEMVPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final shadowPaint = Paint()..color = Colors.black.withOpacity(0.3); final rrectShadow = RRect.fromRectAndRadius( Rect.fromLTWH(2, 2, size.width - 2, size.height - 2), const Radius.circular(6)); canvas.drawRRect(rrectShadow, shadowPaint); final paintBase = Paint() ..shader = const LinearGradient( colors: [Color(0xFFD4AF37), Color(0xFFC0992D), Color(0xFFD4AF37)], begin: Alignment.topLeft, end: Alignment.bottomRight, ).createShader(Rect.fromLTWH(0, 0, size.width, size.height)); final rrectBase = RRect.fromRectAndRadius( Rect.fromLTWH(0, 0, size.width, size.height), const Radius.circular(6)); canvas.drawRRect(rrectBase, paintBase); final paintHighlight = Paint() ..shader = const LinearGradient( colors: [Color(0xFFFFF7AD), Color(0xFFD4AF37)], begin: Alignment.topLeft, end: Alignment.bottomRight, ).createShader(Rect.fromLTWH(1, 1, size.width - 2, size.height - 2)); final rrectHighlight = RRect.fromRectAndRadius( Rect.fromLTWH(1, 1, size.width - 2, size.height - 2), const Radius.circular(5)); canvas.drawRRect(rrectHighlight, paintHighlight); final paintDetails = Paint() ..color = const Color(0xFF8B6B18) ..style = PaintingStyle.fill; canvas.drawRRect(RRect.fromRectAndRadius(Rect.fromLTWH(size.width * 0.2, size.height * 0.15, size.width * 0.6, size.height * 0.2), const Radius.circular(2)), paintDetails); canvas.drawRRect(RRect.fromRectAndRadius(Rect.fromLTWH(size.width * 0.2, size.height * 0.65, size.width * 0.6, size.height * 0.2), const Radius.circular(2)), paintDetails); canvas.drawRect(Rect.fromLTWH(size.width * 0.1, size.height * 0.35, size.width * 0.1, size.height * 0.3), paintDetails); canvas.drawRect(Rect.fromLTWH(size.width * 0.8, size.height * 0.35, size.width * 0.1, size.height * 0.3), paintDetails); canvas.drawRect(Rect.fromLTWH(size.width * 0.3, size.height * 0.45, size.width * 0.4, size.height * 0.1), paintDetails); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; } class CardLogoPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint() ..shader = const LinearGradient( colors: [Color(0xFFFFA000), Color(0xFFFFC107)], begin: Alignment.topLeft, end: Alignment.bottomRight, ).createShader(Rect.fromLTWH(0, 0, size.width, size.height)); final path = Path(); path.addOval(Rect.fromLTWH(0, 0, size.width * 0.6, size.height)); path.addOval(Rect.fromLTWH(size.width * 0.4, 0, size.width * 0.6, size.height)); canvas.drawPath(path, paint); final wifiPaint = Paint() ..color = Colors.white.withOpacity(0.7); final Path wifiPath = Path(); wifiPath.moveTo(size.width * 0.7, size.height * 0.3); wifiPath.arcTo( Rect.fromCircle(center: Offset(size.width * 0.8, size.height * 0.7), radius: 10), -math.pi, math.pi, false, ); canvas.drawPath(wifiPath, wifiPaint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; }