refactor: Use ListTile for TransactionListItem layout

This commit is contained in:
2025-05-04 14:59:34 +03:00
parent 42046b4e87
commit f4ac8e0e56
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../database/database.dart' as db;
import '../database/database.dart' as db; // Keep alias if needed elsewhere, though not used here directly
import '../models/transaction_record.dart';
import '../utils/category_utils.dart';
@@ -44,136 +44,108 @@ class TransactionListItem extends StatelessWidget {
String displayTime = timeFormatter.format(transaction.date);
// Determine text colors based on theme
Color primaryTextColor = theme.textTheme.bodyLarge?.color ?? (isDark ? Colors.white : Colors.black87);
Color secondaryTextColor = theme.textTheme.bodyMedium?.color ?? (isDark ? Colors.white70 : Colors.grey.shade600);
Color amountColor = isDark ? Colors.red.shade200 : Colors.red.shade700;
Color amountColor = isDark ? Colors.redAccent.shade100 : Colors.red.shade700; // Keep amount color distinct
// Use FadeTransition for item appearance
return FadeTransition(
opacity: animation,
child: Card(
margin: const EdgeInsets.only(bottom: 6, top: 2),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
child: SizedBox(
height: 65, // Fixed height for consistency
child: InkWell(
// Use SizeTransition for a smoother vertical entrance/exit animation
child: SizeTransition(
sizeFactor: animation,
child: Card(
// Reduce vertical margin slightly for tighter packing if desired
margin: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
elevation: 1.5, // Subtle elevation
child: ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 4.0), // Adjust padding
// Leading icon
leading: Container(
width: 40, // Slightly larger icon background
height: 40,
decoration: BoxDecoration(
// Use a slightly less opaque background for better icon visibility
color: categoryDetails.colorCode.withOpacity(isDark ? 0.25 : 0.18),
shape: BoxShape.circle,
),
child: Icon(
categoryDetails.iconCode,
color: categoryDetails.colorCode.withOpacity(isDark ? 0.95 : 1.0),
size: 20, // Slightly larger icon
),
),
// Title: Category Name
title: Text(
transaction.categoryName,
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 14.5, // Slightly larger title font
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
// Subtitle: Merchant and Date
subtitle: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, // Push date to the right
children: [
Expanded( // Allow merchant name to take available space and ellipsis
child: Text(
transaction.merchant,
style: TextStyle(
fontSize: 12.5, // Slightly larger subtitle font
color: secondaryTextColor,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
// Optionally add a small spacer if needed
// const SizedBox(width: 8),
Text(
displayDate, // Show only the date part in the subtitle
style: TextStyle(
fontSize: 11.5, // Smaller date font
color: secondaryTextColor.withOpacity(0.8), // Slightly faded date
),
),
],
),
// Trailing: Amount and Time
trailing: Column(
mainAxisAlignment: MainAxisAlignment.center, // Center vertically
crossAxisAlignment: CrossAxisAlignment.end, // Align text to the right
children: [
Text(
'- \$${transaction.amount.toStringAsFixed(2)}',
style: TextStyle(
color: amountColor,
fontWeight: FontWeight.w600,
fontSize: 14, // Consistent font size with title
),
),
const SizedBox(height: 3), // Small space between amount and time
Text(
displayTime, // Show time below the amount
style: TextStyle(
fontSize: 11.5, // Smaller time font
color: secondaryTextColor.withOpacity(0.8), // Slightly faded time
),
),
],
),
onTap: () {
// TODO: Implement navigation to transaction details
print('Tapped transaction: ${transaction.id}');
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Row(
children: [
// Leading icon
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: categoryDetails.colorCode.withOpacity(isDark ? 0.2 : 0.15),
shape: BoxShape.circle,
),
child: Icon(
categoryDetails.iconCode,
color: categoryDetails.colorCode.withOpacity(isDark ? 0.9 : 1.0),
size: 16
),
),
// Content
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
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,
),
),
],
),
],
),
),
// Optional: Add visual density for tighter spacing
// visualDensity: VisualDensity.compact,
),
),
),