import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import 'package:google_fonts/google_fonts.dart'; // Importações originais mantidas import 'package:kzeduca_app/models/transaction.dart'; import 'package:kzeduca_app/widgets/transaction_calculator.dart'; import 'package:kzeduca_app/services/i18n_service.dart'; import 'package:kzeduca_app/services/user_state_service.dart'; import 'package:kzeduca_app/services/transaction_list_notifier.dart'; // KzEduca Palette const _kGradientStart = Color(0xFF512DA8); // Roxo vibrante const _kGradientEnd = Color(0xFF000000); // Preto profundo const _kAccent = Color(0xFF00BFA5); // Verde água const _kAction = Color(0xFFFFD600); // Amarelo/dourado const _kExpenseColor = Color(0xFFFF5252); // Vermelho de erro const _kIncomeColor = Color(0xFF69F0AE); // Verde de sucesso class AddTransactionScreen extends StatefulWidget { const AddTransactionScreen({super.key}); @override State createState() => _AddTransactionScreenState(); } class _AddTransactionScreenState extends State { TransactionType _selectedType = TransactionType.expense; double _currentAmount = 0.0; TransactionCategory _selectedCategory = TransactionCategory.food; DateTime _selectedDate = DateTime.now(); final TextEditingController _noteController = TextEditingController(); final TextEditingController _titleController = TextEditingController(); bool _showCalculator = false; final Map _expenseCategoryNames = { TransactionCategory.food: 'food', TransactionCategory.transport: 'transport', TransactionCategory.social: 'social', TransactionCategory.education: 'education', TransactionCategory.medical: 'medical', TransactionCategory.shopping: 'shopping', TransactionCategory.others: 'others', TransactionCategory.house: 'house', TransactionCategory.utilities: 'utilities', TransactionCategory.subscriptions: 'subscriptions', TransactionCategory.leisure: 'leisure', TransactionCategory.gym: 'gym', TransactionCategory.gifts: 'gifts', TransactionCategory.pets: 'pets', TransactionCategory.tax: 'tax', }; final Map _incomeCategoryNames = { TransactionCategory.salary: 'salary', TransactionCategory.invest: 'invest', TransactionCategory.business: 'business', TransactionCategory.freelance: 'freelance', TransactionCategory.dividends: 'dividends', TransactionCategory.loan: 'loan', TransactionCategory.refund: 'refund', TransactionCategory.others: 'others', TransactionCategory.allowance: 'allowance', TransactionCategory.bonus: 'bonus', }; final Map _categoryIcons = { TransactionCategory.food: Icons.restaurant_menu_rounded, TransactionCategory.transport: Icons.directions_car_rounded, TransactionCategory.social: Icons.people_rounded, TransactionCategory.education: Icons.school_rounded, TransactionCategory.medical: Icons.medical_services_rounded, TransactionCategory.shopping: Icons.shopping_bag_rounded, TransactionCategory.house: Icons.home_rounded, TransactionCategory.utilities: Icons.lightbulb_rounded, TransactionCategory.subscriptions: Icons.subscriptions_rounded, TransactionCategory.leisure: Icons.sports_esports_rounded, TransactionCategory.gym: Icons.fitness_center_rounded, TransactionCategory.gifts: Icons.card_giftcard_rounded, TransactionCategory.pets: Icons.pets_rounded, TransactionCategory.salary: Icons.monetization_on_rounded, TransactionCategory.invest: Icons.trending_up_rounded, TransactionCategory.business: Icons.work_rounded, TransactionCategory.freelance: Icons.laptop_chromebook_rounded, TransactionCategory.dividends: Icons.show_chart_rounded, TransactionCategory.loan: Icons.account_balance_rounded, TransactionCategory.refund: Icons.undo_rounded, TransactionCategory.others: Icons.category_rounded, TransactionCategory.allowance: Icons.card_giftcard_rounded, TransactionCategory.bonus: Icons.stars_rounded, TransactionCategory.tax: Icons.account_balance_wallet_rounded, }; void _onAmountChanged(double newAmount) { setState(() { _currentAmount = newAmount; // UX: Garante que a calculadora esteja visível se o valor for alterado. if (newAmount > 0.0) { _showCalculator = true; } }); } Future _selectDate(BuildContext context) async { final DateTime? picked = await showDatePicker( context: context, initialDate: _selectedDate, firstDate: DateTime(2000), lastDate: DateTime(2101), locale: const Locale('pt', 'BR'), builder: (context, child) { return Theme( data: ThemeData.dark().copyWith( colorScheme: ColorScheme.dark( primary: _kAccent, onPrimary: Colors.white, surface: Colors.grey.shade900, onSurface: Colors.white, ), textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( foregroundColor: _kAccent, ), ), ), child: child!, ); }, ); if (picked != null && picked != _selectedDate) { setState(() { _selectedDate = picked; }); } } void _saveTransaction() async { // Usamos 'listen: false' para evitar rebuilds desnecessários no método. final i18n = Provider.of(context, listen: false); if (_currentAmount <= 0) { _showSnackBar(i18n.t('error campo de entrada valor monetario'), isError: true); return; } if (_titleController.text.trim().isEmpty) { _showSnackBar(i18n.t('erro campo entrada titulo '), isError: true); return; } // A classe Transaction deve vir do seu arquivo 'models/transaction.dart' final newTransaction = Transaction( title: _titleController.text.trim(), amount: _currentAmount, type: _selectedType, category: _selectedCategory, date: _selectedDate, note: _noteController.text.isEmpty ? null : _noteController.text.trim(), ); try { final txnList = Provider.of(context, listen: false); await txnList.addTransaction(newTransaction); final userStateService = Provider.of(context, listen: false); await userStateService.recalculateBalanceFromHive(); _showSnackBar(i18n.t('transaction salva com success')); Navigator.pop(context, true); } catch (e) { _showSnackBar('${i18n.t('erro ao salvar transaction')}: $e', isError: true); } } void _showSnackBar(String message, {bool isError = false}) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( message, style: GoogleFonts.montserrat(color: Colors.white, fontWeight: FontWeight.w600), ), // UX: Usar a cor de destaque para sucesso e a cor de erro para falha backgroundColor: isError ? _kExpenseColor.withOpacity(0.9) : _kIncomeColor.withOpacity(0.9), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), behavior: SnackBarBehavior.floating, ), ); } @override Widget build(BuildContext context) { final i18n = Provider.of(context); return Scaffold( extendBodyBehindAppBar: true, backgroundColor: Colors.transparent, appBar: AppBar( title: Text( i18n.t('add_transaction_title'), style: GoogleFonts.montserrat( color: Colors.white, fontWeight: FontWeight.w600, ), ), backgroundColor: Colors.transparent, foregroundColor: Colors.white, elevation: 0, centerTitle: true, ), body: Stack( children: [ Container( decoration: const BoxDecoration( gradient: LinearGradient( colors: [_kGradientStart, _kGradientEnd], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), ), Center( child: ClipRRect( borderRadius: BorderRadius.circular(25.0), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15), child: Container( width: MediaQuery.of(context).size.width * 0.9, height: MediaQuery.of(context).size.height * 0.85, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), decoration: BoxDecoration( color: Colors.white.withOpacity(0.1), borderRadius: BorderRadius.circular(25.0), border: Border.all(color: Colors.white.withOpacity(0.2)), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.4), blurRadius: 20, offset: const Offset(0, 10), ), ], ), child: Column( children: [ _buildHeader(i18n), const SizedBox(height: 20), Expanded( child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildFormFields(i18n), const SizedBox(height: 20), _buildCategorySection(i18n), const SizedBox(height: 20), ], ), ), ), // MELHORIA UX: Usar AnimatedSize para transição mais suave da Calculadora AnimatedSize( duration: const Duration(milliseconds: 300), curve: Curves.easeOut, child: _showCalculator ? Column( children: [ TransactionCalculator( // A calculadora é responsável por chamar _onAmountChanged onAmountChanged: _onAmountChanged, onSave: _saveTransaction, ), const SizedBox(height: 15), ], ) : const SizedBox(height: 0), // Oculta a calculadora ), // O botão Salvar aparece sempre, exceto se for integrado na calculadora _buildSaveButton(i18n), ], ), ), ), ), ), ], ), ); } Widget _buildSaveButton(I18nService i18n) { // Se a calculadora estiver visível, ela deve ter seu próprio botão de salvar // A implementação do seu código original a colocava fora do bloco da calculadora, mantive assim para consistência // No entanto, se o TransactionCalculator tiver um botão "Salvar" interno, este aqui pode ser oculto // Se você não quiser o botão de salvar duplicado quando a calculadora estiver visível: // if (_showCalculator) return const SizedBox.shrink(); return ElevatedButton( onPressed: _saveTransaction, style: ElevatedButton.styleFrom( backgroundColor: _kAccent, foregroundColor: Colors.black87, padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 15), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), elevation: 8, shadowColor: _kAccent.withOpacity(0.5), ), child: Text( i18n.t('save_transaction'), style: GoogleFonts.montserrat(fontSize: 16, fontWeight: FontWeight.bold), ), ); } Widget _buildHeader(I18nService i18n) { return Column( children: [ _buildTypeSelection(i18n), const SizedBox(height: 30), _buildAmountDisplay(i18n), ], ); } Widget _buildTypeSelection(I18nService i18n) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _buildTypeButton(i18n.t('expense'), TransactionType.expense, Icons.arrow_downward_rounded), const SizedBox(width: 15), _buildTypeButton(i18n.t('income'), TransactionType.income, Icons.arrow_upward_rounded), ], ); } Widget _buildTypeButton(String label, TransactionType type, IconData icon) { final isSelected = _selectedType == type; final Color selectedColor = type == TransactionType.expense ? _kExpenseColor : _kIncomeColor; final Color textColor = isSelected ? Colors.white : Colors.white.withOpacity(0.7); return Expanded( child: GestureDetector( onTap: () { setState(() { _selectedType = type; // Garante que a categoria inicial do novo tipo seja selecionada _selectedCategory = type == TransactionType.expense ? _expenseCategoryNames.keys.first : _incomeCategoryNames.keys.first; // UX: Zera o valor ao mudar o tipo de transação _currentAmount = 0.0; _showCalculator = false; }); }, child: AnimatedContainer( duration: const Duration(milliseconds: 300), padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8), decoration: BoxDecoration( gradient: isSelected ? LinearGradient(colors: [selectedColor.withOpacity(0.7), selectedColor.withOpacity(0.5)]) : null, color: isSelected ? null : Colors.white.withOpacity(0.1), borderRadius: BorderRadius.circular(15), border: Border.all(color: isSelected ? selectedColor : Colors.white.withOpacity(0.2)), boxShadow: [ BoxShadow( color: isSelected ? selectedColor.withOpacity(0.4) : Colors.black.withOpacity(0.1), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(icon, color: textColor), const SizedBox(width: 8), Text( label, style: GoogleFonts.montserrat( fontSize: 16, color: textColor, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), ), ], ), ), ), ); } Widget _buildAmountDisplay(I18nService i18n) { return GestureDetector( // UX: Clicar no valor alterna a visibilidade da calculadora onTap: () { setState(() { _showCalculator = !_showCalculator; }); }, child: Center( child: Text( '${i18n.t('kwanza_symbol')} ${NumberFormat.currency(locale: 'pt_BR', symbol: '').format(_currentAmount)}', style: GoogleFonts.montserrat( fontSize: 50, fontWeight: FontWeight.w900, color: _selectedType == TransactionType.expense ? _kExpenseColor : _kIncomeColor, ), ), ), ); } Widget _buildFormFields(I18nService i18n) { return Column( children: [ _buildCustomTextField( controller: _titleController, label: i18n.t('transaction_title'), hint: i18n.t('example_title'), icon: Icons.description_rounded, ), const SizedBox(height: 20), _buildCustomTextField( controller: _noteController, label: i18n.t('notes_optional'), hint: i18n.t('example_notes'), icon: Icons.notes_rounded, maxLines: 3, ), const SizedBox(height: 20), _buildDatePickerField(i18n), ], ); } Widget _buildCustomTextField({ required TextEditingController controller, required String label, required String hint, required IconData icon, int maxLines = 1, }) { // MELHORIA UX: Cores de texto, ícone e hint ajustadas para o tema escuro. const Color lightTextColor = Colors.white; final Color iconColor = lightTextColor.withOpacity(0.7); final Color hintColor = lightTextColor.withOpacity(0.5); return Container( decoration: BoxDecoration( color: Colors.white.withOpacity(0.1), borderRadius: BorderRadius.circular(15), // MELHORIA UX: Borda consistente com o restante da interface border: Border.all(color: Colors.white.withOpacity(0.2)), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: TextField( controller: controller, maxLines: maxLines, textCapitalization: TextCapitalization.sentences, // MELHORIA UX: Cor do texto digitado ajustada para branco style: GoogleFonts.montserrat(color: lightTextColor, fontWeight: FontWeight.w500), decoration: InputDecoration( labelText: label, hintText: hint, // MELHORIA UX: Cor do ícone ajustada para o tema escuro prefixIcon: Icon(icon, color: iconColor), // MELHORIA UX: Cor do label e hint ajustada para o tema escuro labelStyle: GoogleFonts.montserrat(color: iconColor), hintStyle: GoogleFonts.montserrat(color: hintColor), border: InputBorder.none, contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), ), ), ); } Widget _buildDatePickerField(I18nService i18n) { return GestureDetector( onTap: () => _selectDate(context), child: Container( padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 20), decoration: BoxDecoration( color: Colors.white.withOpacity(0.1), borderRadius: BorderRadius.circular(15), border: Border.all(color: Colors.white.withOpacity(0.2)), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: Row( children: [ Icon(Icons.calendar_today_rounded, color: Colors.white.withOpacity(0.7)), const SizedBox(width: 15), Expanded( child: Text( DateFormat('dd \'de\' MMM \'de\' yyyy', 'pt_BR').format(_selectedDate), style: GoogleFonts.montserrat(fontSize: 16, color: Colors.white, fontWeight: FontWeight.w500), ), ), ], ), ), ); } Widget _buildCategorySection(I18nService i18n) { final categories = _selectedType == TransactionType.expense ? _expenseCategoryNames : _incomeCategoryNames; return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( i18n.t('select_category'), style: GoogleFonts.montserrat( fontSize: 18, fontWeight: FontWeight.w600, color: Colors.white, ), ), const SizedBox(height: 15), Wrap( spacing: 12.0, runSpacing: 12.0, children: categories.entries.map((entry) { return _buildCategoryButton(i18n, entry.key, i18n.t(entry.value)); }).toList(), ), ], ); } Widget _buildCategoryButton(I18nService i18n, TransactionCategory category, String label) { final isSelected = _selectedCategory == category; final selectedColor = _selectedType == TransactionType.expense ? _kExpenseColor : _kIncomeColor; final textColor = isSelected ? Colors.white : Colors.white.withOpacity(0.7); return GestureDetector( onTap: () { setState(() { _selectedCategory = category; }); }, child: AnimatedContainer( duration: const Duration(milliseconds: 300), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( color: isSelected ? selectedColor.withOpacity(0.2) : Colors.white.withOpacity(0.1), borderRadius: BorderRadius.circular(20), border: Border.all(color: isSelected ? selectedColor : Colors.white.withOpacity(0.2)), boxShadow: isSelected ? [ BoxShadow( color: selectedColor.withOpacity(0.4), blurRadius: 10, offset: const Offset(0, 4), ), ] : [], ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( _categoryIcons[category], color: textColor, ), const SizedBox(width: 8), Text( label, style: GoogleFonts.montserrat( color: textColor, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), ), ], ), ), ); } }