Files
FinanceApp2/lib/widgets/transaction_list_item.dart
2025-05-12 19:08:22 +03:00

149 lines
5.7 KiB
Dart

import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; // For date formatting
import '../models/transaction_record.dart'; // Use the model class
class TransactionListItem extends StatelessWidget {
final TransactionRecord transaction; // Use the model class
final VoidCallback? onEdit; // Callback for edit action
final VoidCallback? onDelete; // Callback for delete action
const TransactionListItem({
Key? key,
required this.transaction,
this.onEdit, // Make callbacks optional
this.onDelete,
}) : super(key: key);
// Function to show the options menu
void _showOptionsMenu(BuildContext context) {
showModalBottomSheet(
context: context,
builder: (context) {
return SafeArea( // Use SafeArea to avoid system UI
child: Column(
mainAxisSize: MainAxisSize.min, // Take minimum space
children: <Widget>[
ListTile(
leading: const Icon(Icons.edit_outlined),
title: const Text('Редактировать'),
onTap: () {
Navigator.pop(context); // Close the bottom sheet
onEdit?.call(); // Call the edit callback if it exists
},
),
ListTile(
leading: const Icon(Icons.delete_outline, color: Colors.red),
title: const Text('Удалить', style: TextStyle(color: Colors.red)),
onTap: () {
Navigator.pop(context); // Close the bottom sheet
onDelete?.call(); // Call the delete callback if it exists
},
),
// Optional: Add a Cancel button
ListTile(
leading: const Icon(Icons.cancel_outlined),
title: const Text('Отмена'),
onTap: () => Navigator.pop(context),
),
],
),
);
},
);
}
@override
Widget build(BuildContext context) {
final isExpense = transaction.type == 'expense';
final theme = Theme.of(context);
// Wrap the ListTile in an InkWell to handle long press
return InkWell(
onLongPress: () => _showOptionsMenu(context), // Show menu on long press
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0), // Adjusted padding
child: Row(
children: [
// Category Icon (only for expenses)
if (isExpense && transaction.category != null)
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: transaction.category!.colorCode.withOpacity(0.1), // Use category color
shape: BoxShape.circle,
),
child: Icon(
transaction.category!.iconCode, // Use category icon
color: transaction.category!.colorCode,
size: 20,
),
)
else if (!isExpense) // Icon for Income
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.green.shade100, // Specific color for income icon background
shape: BoxShape.circle,
),
child: Icon(
Icons.attach_money_outlined, // Specific icon for income
color: Colors.green.shade700,
size: 20,
),
),
const SizedBox(width: 16), // Space between icon and text
// Transaction Details (Category/Merchant, Date)
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
isExpense ? transaction.merchant : transaction.merchant, // Display merchant for both for now
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w500, // Medium weight
),
maxLines: 1,
overflow: TextOverflow.ellipsis, // Prevent overflow
),
const SizedBox(height: 4),
Text(
// Display category name for expense, or 'Income' for income
isExpense ? transaction.category?.name ?? 'Unknown Category' : 'Income',
style: theme.textTheme.bodySmall?.copyWith(
color: theme.textTheme.bodySmall?.color?.withOpacity(0.7), // Subtle color
),
),
],
),
),
// Amount and Date
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'${isExpense ? '-' : '+'} \$${NumberFormat.currency(symbol: '', decimalDigits: 2).format(transaction.amount)}', // Format amount with sign
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
color: isExpense ? Colors.red.shade600 : Colors.green.shade600, // Color based on type
),
),
const SizedBox(height: 4),
Text(
DateFormat('MMM d, yyyy').format(transaction.date), // Format date
style: theme.textTheme.bodySmall?.copyWith(
color: theme.textTheme.bodySmall?.color?.withOpacity(0.7), // Subtle color
),
),
],
),
],
),
),
);
}
}