refactor: Use ListTile for TransactionListItem layout
This commit is contained in:
@@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user