diff --git a/lib/app/theme.dart b/lib/app/theme.dart index ca5eb03..6d3e7f3 100644 --- a/lib/app/theme.dart +++ b/lib/app/theme.dart @@ -104,38 +104,41 @@ class AppTheme { static ThemeData get lightTheme { final base = ThemeData.light(useMaterial3: true); final textTheme = GoogleFonts.poppinsTextTheme(base.textTheme).apply( - bodyColor: const Color(0xFF1A1A24), - displayColor: const Color(0xFF1A1A24), + bodyColor: const Color(0xFF1A1A2E), + displayColor: const Color(0xFF1A1A2E), ); return base.copyWith( textTheme: textTheme, - scaffoldBackgroundColor: const Color(0xFFF5F5F5), + scaffoldBackgroundColor: const Color(0xFFF0F0F7), colorScheme: const ColorScheme.light( surface: Colors.white, primary: AppColors.accent, secondary: AppColors.accent, onPrimary: Colors.white, - onSurface: Color(0xFF1A1A24), + onSurface: Color(0xFF1A1A2E), + onBackground: Color(0xFF1A1A2E), + background: Color(0xFFF0F0F7), ), cardTheme: CardThemeData( color: Colors.white, - elevation: 0, + elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), margin: EdgeInsets.zero, ), appBarTheme: AppBarTheme( - backgroundColor: const Color(0xFFF5F5F5), + backgroundColor: Colors.white, + foregroundColor: const Color(0xFF1A1A2E), elevation: 0, centerTitle: false, titleTextStyle: GoogleFonts.poppins( - color: const Color(0xFF1A1A24), + color: const Color(0xFF1A1A2E), fontSize: 20, fontWeight: FontWeight.w600, ), - iconTheme: const IconThemeData(color: Color(0xFF1A1A24)), + iconTheme: const IconThemeData(color: AppColors.accent), ), navigationBarTheme: NavigationBarThemeData( backgroundColor: Colors.white, @@ -149,7 +152,7 @@ class AppTheme { ); } return GoogleFonts.poppins( - color: const Color(0xFF666666), + color: const Color(0xFF9999BB), fontSize: 12, ); }), @@ -157,26 +160,26 @@ class AppTheme { if (states.contains(WidgetState.selected)) { return const IconThemeData(color: AppColors.accent); } - return const IconThemeData(color: Color(0xFF666666)); + return const IconThemeData(color: Color(0xFF9999BB)); }), ), inputDecorationTheme: InputDecorationTheme( filled: true, - fillColor: Colors.white, + fillColor: const Color(0xFFEEEEF8), border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: const BorderSide(color: Color(0xFFE0E0E0)), + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none, ), enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: const BorderSide(color: Color(0xFFE0E0E0)), + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none, ), focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), + borderRadius: BorderRadius.circular(8), borderSide: const BorderSide(color: AppColors.accent, width: 1.5), ), - labelStyle: const TextStyle(color: Color(0xFF666666)), - hintStyle: const TextStyle(color: Color(0xFF999999)), + labelStyle: const TextStyle(color: Color(0xFF9999BB)), + hintStyle: const TextStyle(color: Color(0xFF9999BB)), ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( @@ -193,9 +196,17 @@ class AppTheme { ), ), dividerTheme: const DividerThemeData( - color: Color(0xFFE0E0E0), + color: Color(0xFFDDDDEE), thickness: 1, ), + iconTheme: const IconThemeData(color: AppColors.accent), + chipTheme: ChipThemeData( + backgroundColor: const Color(0xFFEEEEF8), + selectedColor: AppColors.accent, + labelStyle: GoogleFonts.poppins( + color: const Color(0xFF1A1A2E), + ), + ), ); } } diff --git a/lib/features/add_transaction/screen.dart b/lib/features/add_transaction/screen.dart index 1cd1bbf..a45d5b8 100644 --- a/lib/features/add_transaction/screen.dart +++ b/lib/features/add_transaction/screen.dart @@ -26,10 +26,16 @@ class _AddTransactionScreenState extends ConsumerState { final _formKey = GlobalKey(); final _amountController = TextEditingController(); final _noteController = TextEditingController(); + late FocusNode _amountFocusNode; + bool _amountFocused = false; @override void initState() { super.initState(); + _amountFocusNode = FocusNode(); + _amountFocusNode.addListener(() { + setState(() => _amountFocused = _amountFocusNode.hasFocus); + }); if (widget.initial != null) { _amountController.text = widget.initial!.amount.toString(); _noteController.text = widget.initial!.note ?? ''; @@ -40,6 +46,7 @@ class _AddTransactionScreenState extends ConsumerState { void dispose() { _amountController.dispose(); _noteController.dispose(); + _amountFocusNode.dispose(); super.dispose(); } @@ -100,13 +107,46 @@ class _AddTransactionScreenState extends ConsumerState { final currencyInfo = ref.watch(currencyProvider); return Scaffold( - backgroundColor: AppColors.background, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, appBar: AppBar( title: Text(state.isEditing ? 'Edit Transaction' : 'Add Transaction'), leading: IconButton( icon: const Icon(Icons.close_rounded), onPressed: () => context.pop(), ), + actions: [ + if (state.isEditing) + IconButton( + icon: const Icon(Icons.delete_outline_rounded), + color: const Color(0xFFE05C6B), + onPressed: () { + showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('Delete transaction?'), + content: const Text('This action cannot be undone.'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx), + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + ref.read(transactionsProvider.notifier).delete(widget.initial!.id); + Navigator.pop(ctx); + context.pop(); + }, + style: TextButton.styleFrom( + foregroundColor: const Color(0xFFE05C6B), + ), + child: const Text('Delete'), + ), + ], + ), + ); + }, + ), + ], ), body: SafeArea( child: Form( @@ -125,35 +165,62 @@ class _AddTransactionScreenState extends ConsumerState { // Amount _SectionLabel('Amount'), const SizedBox(height: 8), - TextFormField( - controller: _amountController, - keyboardType: const TextInputType.numberWithOptions(decimal: true), - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,2}')), - ], - style: Theme.of(context).textTheme.headlineSmall?.copyWith( - color: AppColors.textPrimary, - fontWeight: FontWeight.w600, - ), - decoration: InputDecoration( - prefixText: '${currencyInfo.symbol} ', - prefixStyle: const TextStyle( - color: AppColors.textPrimary, - fontSize: 20, - fontWeight: FontWeight.w600, + Container( + decoration: BoxDecoration( + color: Theme.of(context).inputDecorationTheme.fillColor, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: _amountFocused ? Theme.of(context).colorScheme.primary : Colors.transparent, + width: 1.5, ), - hintText: '0.00', ), - onChanged: (v) { - final parsed = double.tryParse(v); - ref.read(addTransactionProvider(widget.initial).notifier).setAmount(parsed); - }, - validator: (v) { - if (v == null || v.isEmpty) return 'Enter an amount'; - if (double.tryParse(v) == null) return 'Invalid amount'; - if (double.parse(v) <= 0) return 'Amount must be > 0'; - return null; - }, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Text( + currencyInfo.symbol, + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w600, + color: Theme.of(context).colorScheme.onSurface, + ), + ), + ), + Expanded( + child: TextFormField( + controller: _amountController, + focusNode: _amountFocusNode, + keyboardType: const TextInputType.numberWithOptions(decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,2}')), + ], + style: Theme.of(context).textTheme.headlineSmall?.copyWith( + color: Theme.of(context).colorScheme.onSurface, + fontWeight: FontWeight.w600, + ), + decoration: const InputDecoration( + hintText: '0.00', + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + filled: false, + contentPadding: EdgeInsets.symmetric(horizontal: 0, vertical: 16), + ), + onChanged: (v) { + final parsed = double.tryParse(v); + ref.read(addTransactionProvider(widget.initial).notifier).setAmount(parsed); + }, + validator: (v) { + if (v == null || v.isEmpty) return 'Enter an amount'; + if (double.tryParse(v) == null) return 'Invalid amount'; + if (double.parse(v) <= 0) return 'Amount must be > 0'; + return null; + }, + ), + ), + ], + ), ), const SizedBox(height: 20), @@ -177,19 +244,19 @@ class _AddTransactionScreenState extends ConsumerState { child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), decoration: BoxDecoration( - color: AppColors.surface, + color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(12), - border: Border.all(color: AppColors.divider), + border: Border.all(color: Theme.of(context).dividerColor), ), child: Row( children: [ - const Icon(Icons.calendar_today_rounded, - color: AppColors.textSecondary, size: 18), + Icon(Icons.calendar_today_rounded, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), size: 18), const SizedBox(width: 10), Text( DateFormat('MMMM d, yyyy').format(state.date), style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: AppColors.textPrimary, + color: Theme.of(context).colorScheme.onSurface, ), ), ], @@ -242,7 +309,7 @@ class _SectionLabel extends StatelessWidget { return Text( text, style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: AppColors.textSecondary, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), fontWeight: FontWeight.w600, letterSpacing: 0.5, ), @@ -259,9 +326,9 @@ class _TypeToggle extends StatelessWidget { Widget build(BuildContext context) { return Container( decoration: BoxDecoration( - color: AppColors.surface, + color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(14), - border: Border.all(color: AppColors.divider), + border: Border.all(color: Theme.of(context).dividerColor), ), child: Row( children: [ @@ -314,12 +381,12 @@ class _TypeOption extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon(icon, color: isSelected ? color : AppColors.textSecondary, size: 18), + Icon(icon, color: isSelected ? color : Theme.of(context).colorScheme.onSurface.withOpacity(0.6), size: 18), const SizedBox(width: 6), Text( label, style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: isSelected ? color : AppColors.textSecondary, + color: isSelected ? color : Theme.of(context).colorScheme.onSurface.withOpacity(0.6), fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, ), ), @@ -356,22 +423,22 @@ class _CategoryPicker extends StatelessWidget { duration: const Duration(milliseconds: 200), padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), decoration: BoxDecoration( - color: isSelected ? color.withOpacity(0.2) : AppColors.surface, + color: isSelected ? color.withOpacity(0.2) : Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(12), border: Border.all( - color: isSelected ? color : AppColors.divider, + color: isSelected ? color : Theme.of(context).dividerColor, width: isSelected ? 1.5 : 1, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ - Icon(icon, color: isSelected ? color : AppColors.textSecondary, size: 16), + Icon(icon, color: isSelected ? color : Theme.of(context).colorScheme.onSurface.withOpacity(0.6), size: 16), const SizedBox(width: 6), Text( cat, style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: isSelected ? color : AppColors.textSecondary, + color: isSelected ? color : Theme.of(context).colorScheme.onSurface.withOpacity(0.6), fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, ), ), diff --git a/lib/features/dashboard/screen.dart b/lib/features/dashboard/screen.dart index 4e60dc4..56b5910 100644 --- a/lib/features/dashboard/screen.dart +++ b/lib/features/dashboard/screen.dart @@ -37,7 +37,7 @@ class _DashboardScreenState extends ConsumerState { final budgetExceeded = budget != null && monthExpense > budget; return Scaffold( - backgroundColor: AppColors.background, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, appBar: AppBar( title: Row( children: [ @@ -49,13 +49,13 @@ class _DashboardScreenState extends ConsumerState { 'My Finances', style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.w700, - color: AppColors.textPrimary, + color: Theme.of(context).colorScheme.onSurface, ), ), Text( DateFormat('MMMM yyyy').format(DateTime.now()), style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: AppColors.textSecondary, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ), ), ], @@ -75,7 +75,7 @@ class _DashboardScreenState extends ConsumerState { ), body: SafeArea( child: CustomScrollView( - cacheExtent: 500, + cacheExtent: 300, slivers: [ SliverToBoxAdapter( child: Padding( @@ -103,7 +103,7 @@ class _DashboardScreenState extends ConsumerState { 'Transactions', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, - color: AppColors.textPrimary, + color: Theme.of(context).colorScheme.onSurface, ), ), const SizedBox(height: 12), @@ -119,15 +119,13 @@ class _DashboardScreenState extends ConsumerState { else SliverPadding( padding: const EdgeInsets.fromLTRB(20, 0, 20, 100), - sliver: SliverList( - delegate: SliverChildBuilderDelegate( - (context, i) => Padding( - padding: const EdgeInsets.only(bottom: 10), - child: _TransactionTile( - transaction: recent[i], - ), + sliver: SliverList.builder( + itemCount: recent.length, + itemBuilder: (context, i) => Padding( + padding: const EdgeInsets.only(bottom: 10), + child: RepaintBoundary( + child: _TransactionTile(transaction: recent[i]), ), - childCount: recent.length, ), ), ), @@ -149,11 +147,11 @@ class _SearchBar extends StatelessWidget { controller: controller, decoration: InputDecoration( hintText: 'Search transactions...', - prefixIcon: const Icon(Icons.search_rounded, color: AppColors.textSecondary), + prefixIcon: Icon(Icons.search_rounded, color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6)), suffixIcon: controller.text.isNotEmpty ? IconButton( icon: const Icon(Icons.clear_rounded, size: 20), - color: AppColors.textSecondary, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), onPressed: () { controller.clear(); ref.read(searchQueryProvider.notifier).state = ''; @@ -221,17 +219,17 @@ class _FilterChip extends StatelessWidget { duration: const Duration(milliseconds: 200), padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), decoration: BoxDecoration( - color: isSelected ? chipColor.withOpacity(0.2) : AppColors.surface, + color: isSelected ? chipColor.withOpacity(0.2) : Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(20), border: Border.all( - color: isSelected ? chipColor : AppColors.divider, + color: isSelected ? chipColor : Theme.of(context).dividerColor, width: isSelected ? 1.5 : 1, ), ), child: Text( label, style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: isSelected ? chipColor : AppColors.textSecondary, + color: isSelected ? chipColor : Theme.of(context).colorScheme.onSurface.withOpacity(0.6), fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, ), ), @@ -258,9 +256,9 @@ class _BudgetProgress extends StatelessWidget { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AppColors.surface, + color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(16), - border: Border.all(color: AppColors.divider), + border: Border.all(color: Theme.of(context).dividerColor), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -271,7 +269,7 @@ class _BudgetProgress extends StatelessWidget { Text( 'Monthly Budget', style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: AppColors.textSecondary, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ), ), Text( @@ -288,7 +286,7 @@ class _BudgetProgress extends StatelessWidget { borderRadius: BorderRadius.circular(8), child: LinearProgressIndicator( value: ratio.clamp(0.0, 1.0), - backgroundColor: AppColors.divider, + backgroundColor: Theme.of(context).dividerColor, valueColor: AlwaysStoppedAnimation(color), minHeight: 8, ), @@ -300,13 +298,13 @@ class _BudgetProgress extends StatelessWidget { Text( 'Spent: ${currencyInfo.symbol}${spent.toStringAsFixed(2)}', style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: AppColors.textSecondary, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ), ), Text( 'Limit: ${currencyInfo.symbol}${budget.toStringAsFixed(2)}', style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: AppColors.textSecondary, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ), ), ], @@ -363,8 +361,11 @@ class _BalanceCard extends StatelessWidget { width: double.infinity, padding: const EdgeInsets.all(24), decoration: BoxDecoration( - gradient: const LinearGradient( - colors: [Color(0xFF7C6DED), Color(0xFF5A4FBF)], + gradient: LinearGradient( + colors: [ + Theme.of(context).colorScheme.primary, + Theme.of(context).colorScheme.primary.withOpacity(0.7), + ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), @@ -383,14 +384,14 @@ class _BalanceCard extends StatelessWidget { Text( 'Total Balance', style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Colors.white70, + color: Theme.of(context).colorScheme.onPrimary.withOpacity(0.7), ), ), const SizedBox(height: 8), Text( '${currencyInfo.symbol}${balance.toStringAsFixed(2)}', style: Theme.of(context).textTheme.headlineMedium?.copyWith( - color: Colors.white, + color: Theme.of(context).colorScheme.onPrimary, fontWeight: FontWeight.w700, letterSpacing: -0.5, ), @@ -399,7 +400,7 @@ class _BalanceCard extends StatelessWidget { Text( 'Converted to ${currencyInfo.symbol}${currencyInfo.code}', style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Colors.white60, + color: Theme.of(context).colorScheme.onPrimary.withOpacity(0.6), fontSize: 11, ), ), @@ -440,9 +441,9 @@ class _SummaryCard extends StatelessWidget { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AppColors.surface, + color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(16), - border: Border.all(color: AppColors.divider), + border: Border.all(color: Theme.of(context).dividerColor), ), child: Row( children: [ @@ -459,7 +460,7 @@ class _SummaryCard extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(label, style: Theme.of(context).textTheme.bodySmall?.copyWith(color: AppColors.textSecondary)), + Text(label, style: Theme.of(context).textTheme.bodySmall?.copyWith(color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6))), const SizedBox(height: 2), Text( '${currencyInfo.symbol}${amount.toStringAsFixed(2)}', @@ -478,107 +479,74 @@ class _SummaryCard extends StatelessWidget { } } -class _TransactionTile extends ConsumerWidget { +class _TransactionTile extends StatelessWidget { final Transaction transaction; const _TransactionTile({required this.transaction}); - void _showUndoSnackBar(BuildContext context, WidgetRef ref, Transaction tx) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: const Text('Transaction deleted'), - duration: const Duration(seconds: 5), - action: SnackBarAction( - label: 'Undo', - onPressed: () { - ref.read(transactionsProvider.notifier).restore(tx); - }, - ), - backgroundColor: AppColors.surface, - ), - ); - } - @override - Widget build(BuildContext context, WidgetRef ref) { + Widget build(BuildContext context) { final isIncome = transaction.type == TransactionType.income; final color = isIncome ? AppColors.income : AppColors.expense; final catColor = AppCategories.colors[transaction.category] ?? AppColors.accent; final catIcon = AppCategories.icons[transaction.category] ?? Icons.category_rounded; - return Dismissible( - key: Key(transaction.id), - direction: DismissDirection.endToStart, - background: Container( - alignment: Alignment.centerRight, - padding: const EdgeInsets.only(right: 20), + return GestureDetector( + onTap: () => context.push('/add', extra: transaction), + child: Container( + padding: const EdgeInsets.all(14), decoration: BoxDecoration( - color: AppColors.expense.withOpacity(0.15), + color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(16), + border: Border.all(color: Theme.of(context).dividerColor), ), - child: const Icon(Icons.delete_outline_rounded, color: AppColors.expense), - ), - onDismissed: (_) { - ref.read(transactionsProvider.notifier).delete(transaction.id); - _showUndoSnackBar(context, ref, transaction); - }, - child: GestureDetector( - onTap: () => context.push('/add', extra: transaction), - child: Container( - padding: const EdgeInsets.all(14), - decoration: BoxDecoration( - color: AppColors.surface, - borderRadius: BorderRadius.circular(16), - border: Border.all(color: AppColors.divider), - ), - child: Row( - children: [ - Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: catColor.withOpacity(0.15), - borderRadius: BorderRadius.circular(12), - ), - child: Icon(catIcon, color: catColor, size: 20), + child: Row( + children: [ + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: catColor.withOpacity(0.15), + borderRadius: BorderRadius.circular(12), ), - const SizedBox(width: 12), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ + child: Icon(catIcon, color: catColor, size: 20), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + transaction.category, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w600, + color: Theme.of(context).colorScheme.onSurface, + ), + ), + if (transaction.note != null && transaction.note!.isNotEmpty) Text( - transaction.category, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - color: AppColors.textPrimary, + transaction.note!, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), + ), + overflow: TextOverflow.ellipsis, + ) + else + Text( + DateFormat('MMM d, yyyy').format(transaction.date), + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ), ), - if (transaction.note != null && transaction.note!.isNotEmpty) - Text( - transaction.note!, - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: AppColors.textSecondary, - ), - overflow: TextOverflow.ellipsis, - ) - else - Text( - DateFormat('MMM d, yyyy').format(transaction.date), - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: AppColors.textSecondary, - ), - ), - ], - ), + ], ), - Text( - '${isIncome ? '+' : '-'}${transaction.currency}${transaction.amount.toStringAsFixed(2)}', - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: color, - fontWeight: FontWeight.w700, - ), - ), - ], - ), + ), + Text( + '${isIncome ? '+' : '-'}${transaction.currency}${transaction.amount.toStringAsFixed(2)}', + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: color, + fontWeight: FontWeight.w700, + ), + ), + ], ), ), ); @@ -596,21 +564,21 @@ class _EmptyState extends StatelessWidget { children: [ Container( padding: const EdgeInsets.all(24), - decoration: const BoxDecoration( - color: AppColors.surface, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, shape: BoxShape.circle, ), - child: const Icon( + child: Icon( Icons.receipt_long_rounded, size: 48, - color: AppColors.textSecondary, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ), ), const SizedBox(height: 16), Text( 'No transactions found', style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: AppColors.textPrimary, + color: Theme.of(context).colorScheme.onSurface, fontWeight: FontWeight.w600, ), ), @@ -618,7 +586,7 @@ class _EmptyState extends StatelessWidget { Text( 'Tap + to add your first transaction', style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: AppColors.textSecondary, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ), ), ], diff --git a/lib/features/settings/provider.dart b/lib/features/settings/provider.dart index 7f6087a..5a105b3 100644 --- a/lib/features/settings/provider.dart +++ b/lib/features/settings/provider.dart @@ -21,6 +21,12 @@ class BudgetNotifier extends StateNotifier { await _storage.saveBudget(budget); state = budget; } + + void onCurrencyChanged(String oldCode, String newCode, ExchangeRateService rates) { + if (state == null) return; + final converted = rates.convert(state!, oldCode, newCode); + setBudget(converted); + } } // Currency info: symbol and code diff --git a/lib/features/settings/screen.dart b/lib/features/settings/screen.dart index 53c8796..15832fe 100644 --- a/lib/features/settings/screen.dart +++ b/lib/features/settings/screen.dart @@ -59,7 +59,7 @@ class _SettingsScreenState extends ConsumerState { _currencyFmt = NumberFormat.currency(symbol: currencyInfo.symbol, decimalDigits: 2); return Scaffold( - backgroundColor: AppColors.background, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, appBar: AppBar( title: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -68,13 +68,13 @@ class _SettingsScreenState extends ConsumerState { 'Settings', style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.w700, - color: AppColors.textPrimary, + color: Theme.of(context).colorScheme.onSurface, ), ), Text( 'Manage your preferences', style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: AppColors.textSecondary, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ), ), ], @@ -98,9 +98,9 @@ class _SettingsScreenState extends ConsumerState { Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( - color: AppColors.surface, + color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(16), - border: Border.all(color: AppColors.divider), + border: Border.all(color: Theme.of(context).dividerColor), ), child: Row( children: [ @@ -125,13 +125,13 @@ class _SettingsScreenState extends ConsumerState { 'Dark Mode', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, - color: AppColors.textPrimary, + color: Theme.of(context).colorScheme.onSurface, ), ), Text( isDarkMode ? 'Enabled' : 'Disabled', style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: AppColors.textSecondary, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ), ), ], @@ -153,9 +153,9 @@ class _SettingsScreenState extends ConsumerState { Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( - color: AppColors.surface, + color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(16), - border: Border.all(color: AppColors.divider), + border: Border.all(color: Theme.of(context).dividerColor), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -180,7 +180,7 @@ class _SettingsScreenState extends ConsumerState { 'Currency', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, - color: AppColors.textPrimary, + color: Theme.of(context).colorScheme.onSurface, ), ), ), @@ -196,6 +196,9 @@ class _SettingsScreenState extends ConsumerState { padding: const EdgeInsets.only(right: 8), child: GestureDetector( onTap: () { + final oldCode = ref.read(currencyProvider).code; + final rates = ref.read(exchangeRateServiceProvider); + ref.read(budgetProvider.notifier).onCurrencyChanged(oldCode, code, rates); ref.read(currencyProvider.notifier).setCurrency(code); }, child: Container( @@ -203,10 +206,10 @@ class _SettingsScreenState extends ConsumerState { decoration: BoxDecoration( color: isSelected ? AppColors.accent.withOpacity(0.2) - : AppColors.background, + : Theme.of(context).scaffoldBackgroundColor, borderRadius: BorderRadius.circular(12), border: Border.all( - color: isSelected ? AppColors.accent : AppColors.divider, + color: isSelected ? AppColors.accent : Theme.of(context).dividerColor, width: isSelected ? 1.5 : 1, ), ), @@ -215,7 +218,7 @@ class _SettingsScreenState extends ConsumerState { Text( info.symbol, style: Theme.of(context).textTheme.titleLarge?.copyWith( - color: isSelected ? AppColors.accent : AppColors.textSecondary, + color: isSelected ? AppColors.accent : Theme.of(context).colorScheme.onSurface.withOpacity(0.6), fontWeight: isSelected ? FontWeight.w700 : FontWeight.normal, ), ), @@ -223,7 +226,7 @@ class _SettingsScreenState extends ConsumerState { Text( code, style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: isSelected ? AppColors.accent : AppColors.textSecondary, + color: isSelected ? AppColors.accent : Theme.of(context).colorScheme.onSurface.withOpacity(0.6), fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, ), ), @@ -244,9 +247,9 @@ class _SettingsScreenState extends ConsumerState { Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( - color: AppColors.surface, + color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(16), - border: Border.all(color: AppColors.divider), + border: Border.all(color: Theme.of(context).dividerColor), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -271,14 +274,14 @@ class _SettingsScreenState extends ConsumerState { 'Monthly Budget', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, - color: AppColors.textPrimary, + color: Theme.of(context).colorScheme.onSurface, ), ), ), if (!_isEditing) IconButton( icon: const Icon(Icons.edit_rounded, size: 20), - color: AppColors.textSecondary, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), onPressed: () => setState(() => _isEditing = true), ), ], @@ -334,7 +337,7 @@ class _SettingsScreenState extends ConsumerState { ? _currencyFmt.format(budget) : 'Not set', style: Theme.of(context).textTheme.headlineSmall?.copyWith( - color: budget != null ? AppColors.accent : AppColors.textSecondary, + color: budget != null ? AppColors.accent : Theme.of(context).colorScheme.onSurface.withOpacity(0.6), fontWeight: FontWeight.w700, ), ), @@ -344,7 +347,7 @@ class _SettingsScreenState extends ConsumerState { ? 'Your monthly spending limit' : 'Set a monthly spending limit to track your budget', style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: AppColors.textSecondary, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ), ), ], @@ -368,7 +371,7 @@ class _SettingsScreenState extends ConsumerState { child: Text( 'Budget tracking shows on the Dashboard with a progress bar and warning when exceeded.', style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: AppColors.textPrimary, + color: Theme.of(context).colorScheme.onSurface, ), ), ),