1 Commits
Author SHA1 Message Date
Sanders 42046b4e87 fix 2025-05-04 14:58:40 +03:00
@@ -1,12 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import '../database/database.dart' as db; // Import database with prefix 'db' import '../database/database.dart' as db;
import '../models/transaction_record.dart'; import '../models/transaction_record.dart';
import '../utils/category_utils.dart'; // Import category utils import '../utils/category_utils.dart';
class TransactionListItem extends StatelessWidget { class TransactionListItem extends StatelessWidget {
final TransactionRecord transaction; // Use the Drift-generated Transaction class final TransactionRecord transaction;
final Animation<double> animation; // Keep animation for potential future use final Animation<double> animation;
const TransactionListItem({ const TransactionListItem({
Key? key, Key? key,
@@ -19,10 +19,10 @@ class TransactionListItem extends StatelessWidget {
final isDark = Theme.of(context).brightness == Brightness.dark; final isDark = Theme.of(context).brightness == Brightness.dark;
final theme = Theme.of(context); final theme = Theme.of(context);
// Get category details (icon, color) using the utility // Get category details
final categoryDetails = CategoryUtils.getCategoryDetails(transaction.categoryName); final categoryDetails = CategoryUtils.getCategoryDetails(transaction.categoryName);
// Format the date and time using intl package // Format dates
final dateFormatter = DateFormat.MMMd(); // e.g., Sep 10 final dateFormatter = DateFormat.MMMd(); // e.g., Sep 10
final timeFormatter = DateFormat.jm(); // e.g., 5:08 PM final timeFormatter = DateFormat.jm(); // e.g., 5:08 PM
@@ -34,87 +34,146 @@ class TransactionListItem extends StatelessWidget {
String displayDate; String displayDate;
if (transactionDay == today) { if (transactionDay == today) {
displayDate = 'Today, ${timeFormatter.format(transaction.date)}'; displayDate = 'Today';
} else if (transactionDay == yesterday) { } else if (transactionDay == yesterday) {
displayDate = 'Yesterday, ${timeFormatter.format(transaction.date)}'; displayDate = 'Yesterday';
} else { } else {
// Format for other dates (e.g., "Sep 10, 5:08 PM") displayDate = dateFormatter.format(transaction.date);
displayDate = '${dateFormatter.format(transaction.date)}, ${timeFormatter.format(transaction.date)}';
} }
String displayTime = timeFormatter.format(transaction.date);
// Determine text colors based on theme // Determine text colors based on theme
Color primaryTextColor = theme.textTheme.bodyLarge?.color ?? (isDark ? Colors.white : Colors.black87); Color primaryTextColor = theme.textTheme.bodyLarge?.color ?? (isDark ? Colors.white : Colors.black87);
Color secondaryTextColor = theme.textTheme.bodyMedium?.color ?? (isDark ? Colors.white70 : Colors.grey.shade600); Color secondaryTextColor = theme.textTheme.bodyMedium?.color ?? (isDark ? Colors.white70 : Colors.grey.shade600);
Color amountColor = isDark ? Colors.red.shade200 : Colors.red.shade700; // Expense color Color amountColor = isDark ? Colors.red.shade200 : Colors.red.shade700;
// Use FadeTransition for item appearance (works with ListView.builder) // Use FadeTransition for item appearance
return FadeTransition( return FadeTransition(
opacity: animation, // Apply fade animation opacity: animation,
child: InkWell( // Make the item tappable child: Card(
onTap: () { margin: const EdgeInsets.only(bottom: 6, top: 2),
// TODO: Implement navigation to transaction details screen or edit action shape: RoundedRectangleBorder(
print('Tapped transaction: ${transaction.id}'); borderRadius: BorderRadius.circular(12.0),
}, ),
child: Padding( child: SizedBox(
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 16.0), // Consistent padding height: 65, // Fixed height for consistency
child: Row( child: InkWell(
children: [ borderRadius: BorderRadius.circular(12.0),
// Icon container with category color/icon onTap: () {
Container( // TODO: Implement navigation to transaction details
padding: const EdgeInsets.all(10), print('Tapped transaction: ${transaction.id}');
decoration: BoxDecoration( },
color: categoryDetails.colorCode.withOpacity(isDark ? 0.25 : 0.15), // Use category color with opacity child: Padding(
borderRadius: BorderRadius.circular(12), // Rounded corners padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
), child: Row(
child: Icon( children: [
categoryDetails.iconCode, // Use category icon // Leading icon
color: categoryDetails.colorCode, // Use category color for icon Container(
size: 20, // Icon size width: 36,
), height: 36,
), decoration: BoxDecoration(
const SizedBox(width: 12), // Spacing color: categoryDetails.colorCode.withOpacity(isDark ? 0.2 : 0.15),
shape: BoxShape.circle,
// Transaction details (Merchant/Category and Date/Time)
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Display Merchant if available, otherwise Category Name
Text(
transaction.merchant.isNotEmpty ? transaction.merchant : transaction.categoryName,
style: theme.textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.w500, // Medium weight for primary text
color: primaryTextColor,
),
maxLines: 1, // Prevent wrapping
overflow: TextOverflow.ellipsis, // Handle long text
), ),
const SizedBox(height: 4), // Spacing between lines child: Icon(
// Display formatted date/time categoryDetails.iconCode,
Text( color: categoryDetails.colorCode.withOpacity(isDark ? 0.9 : 1.0),
displayDate, size: 16
style: theme.textTheme.bodyMedium?.copyWith(
color: secondaryTextColor, // Lighter color for secondary text
fontSize: 12, // Smaller font size
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
), ),
], ),
),
),
const SizedBox(width: 12), // Spacing before amount
// Transaction Amount // Content
Text( Expanded(
// Format amount as currency (negative for expense) child: Padding(
NumberFormat.currency(symbol: '-\$', decimalDigits: 2).format(transaction.amount), padding: const EdgeInsets.symmetric(horizontal: 10.0),
style: theme.textTheme.bodyLarge?.copyWith( child: Column(
fontWeight: FontWeight.w600, // Bold weight for amount crossAxisAlignment: CrossAxisAlignment.start,
color: amountColor, // Use expense color mainAxisAlignment: MainAxisAlignment.center,
), children: [
// Category and merchant on same line
Row(
children: [
Text(
transaction.categoryName,
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 13
),
),
const SizedBox(width: 4),
Text(
'·', // Bullet separator
style: TextStyle(
color: isDark ? Colors.grey.shade400 : Colors.grey.shade700,
fontSize: 13,
),
),
const SizedBox(width: 4),
Expanded(
child: Text(
transaction.merchant,
style: TextStyle(
fontSize: 12,
color: isDark ? Colors.grey.shade400 : Colors.grey.shade700,
overflow: TextOverflow.ellipsis,
),
),
),
],
),
const SizedBox(height: 4),
// Date and time
Row(
children: [
Icon(
Icons.access_time,
size: 10,
color: isDark ? Colors.grey.shade400 : Colors.grey.shade600,
),
const SizedBox(width: 4),
Text(
displayDate,
style: TextStyle(
fontSize: 10,
color: isDark ? Colors.grey.shade400 : Colors.grey.shade600,
),
),
],
),
],
),
),
),
// Amount and Time
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'- \$${transaction.amount.toStringAsFixed(2)}',
style: TextStyle(
color: isDark ? Colors.redAccent.shade100 : Colors.red.shade700,
fontWeight: FontWeight.w600,
fontSize: 13,
),
),
const SizedBox(height: 4),
Text(
displayTime,
style: TextStyle(
fontSize: 10,
color: isDark ? Colors.grey.shade400 : Colors.grey.shade600,
),
),
],
),
],
), ),
], ),
), ),
), ),
), ),