Skip to content

Transaction Tracking

DWallet provides comprehensive transaction tracking with categories, wallets, notes, and full CRUD operations.

Users can record financial transactions with:

  • Amount (income or expense)
  • Category (food, transport, salary, etc.)
  • Wallet (which account)
  • Date and time
  • Optional notes

File: packages/dwallet_types/lib/src/abstractions/_transaction.dart

abstract class Transaction {
String get id;
double get amount;
TransactionType get type; // income or expense
Category get category;
String? get note;
DateTime get date;
Wallet get wallet;
}
enum TransactionType {
income,
expense,
}
class TransactionModel implements Transaction {
final String id;
final double amount;
final TransactionType type;
final CategoryModel category;
final String? note;
final DateTime date;
final WalletModel wallet;
const TransactionModel({
required this.id,
required this.amount,
required this.type,
required this.category,
this.note,
required this.date,
required this.wallet,
});
}

File: packages/dwallet_types/lib/src/abstractions/_category.dart

Predefined categories for both income and expense:

CategoryIconColor
Food & Dining🍔Orange
Transport🚗Blue
Shopping🛍️Pink
Entertainment🎬Purple
Bills & Utilities💡Yellow
Health🏥Red
Education📚Green
Other📦Gray
CategoryIconColor
Salary💰Green
Freelance💻Blue
Investment📈Purple
Gift🎁Pink
Other💵Gray

File: lib/app/widgets/_transaction_card.dart

TransactionCardWidget(
transaction: TransactionModel(
id: '1',
amount: 50.00,
type: TransactionType.expense,
category: CategoryMock.food,
note: 'Lunch with friends',
date: DateTime.now(),
wallet: wallet,
),
)

Features:

  • Category icon with color background
  • Transaction name/note
  • Formatted amount with + or - sign
  • Date display
  • Swipe to edit/delete

File: lib/app/pages/client/components/_manage_transaction_modal_sheet.dart

Bottom sheet for creating/editing transactions:

showModalBottomSheet(
context: context,
builder: (_) => ManageTransactionModalSheet(
transaction: existingTransaction, // null for new
initialType: TransactionType.expense,
),
);

Form Fields:

  • Amount (numeric input with keypad)
  • Type toggle (Income/Expense)
  • Category selector (grid of icons)
  • Wallet selector
  • Date picker
  • Note (optional text)
1. Tap FAB (+) on any screen
2. ManageTransactionModalSheet opens
3. User enters amount
4. Selects type (income/expense)
5. Chooses category
6. Selects wallet
7. Picks date
8. Adds optional note
9. Taps Save
10. Transaction added, wallet balance updated
void _saveTransaction() {
final transaction = TransactionModel(
id: DateTime.now().toString(),
amount: double.parse(_amountController.text),
type: _selectedType,
category: _selectedCategory,
wallet: _selectedWallet,
date: _selectedDate,
note: _noteController.text.isEmpty ? null : _noteController.text,
);
// Add transaction
transactions.add(transaction);
// Update wallet balance
if (transaction.type == TransactionType.income) {
_selectedWallet.balance += transaction.amount;
} else {
_selectedWallet.balance -= transaction.amount;
}
// Save to storage
_saveTransactions();
}

Shows 5-10 most recent transactions:

final recentTransactions = transactions
.sorted((a, b) => b.date.compareTo(a.date))
.take(5)
.toList();

Shows transactions filtered by wallet:

final walletTransactions = transactions
.where((t) => t.wallet.id == wallet.id)
.sorted((a, b) => b.date.compareTo(a.date))
.toList();

File: lib/app/pages/client/all_transaction_list/all_transaction_list_view.dart

Complete transaction history with filtering:

  • Filter by type (income/expense)
  • Filter by category
  • Filter by wallet
  • Filter by date range
  • Search by note

File: lib/app/pages/client/all_transaction_list/components/_filter_modal.dart

class TransactionFilter {
final TransactionType? type;
final Category? category;
final Wallet? wallet;
final DateTime? fromDate;
final DateTime? toDate;
List<Transaction> apply(List<Transaction> transactions) {
return transactions.where((t) {
if (type != null && t.type != type) return false;
if (category != null && t.category.id != category.id) return false;
if (wallet != null && t.wallet.id != wallet.id) return false;
if (fromDate != null && t.date.isBefore(fromDate!)) return false;
if (toDate != null && t.date.isAfter(toDate!)) return false;
return true;
}).toList();
}
}
void _editTransaction(Transaction transaction) {
// Show modal with existing data
showModalBottomSheet(
context: context,
builder: (_) => ManageTransactionModalSheet(
transaction: transaction,
),
);
}
void _deleteTransaction(Transaction transaction) {
// Reverse wallet balance change
if (transaction.type == TransactionType.income) {
transaction.wallet.balance -= transaction.amount;
} else {
transaction.wallet.balance += transaction.amount;
}
// Remove from list
transactions.remove(transaction);
// Save changes
_saveTransactions();
}
  1. Edit packages/dwallet_types/lib/src/mocks/_category.dart:
static const travel = CategoryModel(
id: 'travel',
name: 'Travel',
icon: 'airplane',
color: Colors.teal,
type: TransactionType.expense,
);
  1. Add to categories list:
static const List<Category> expenseCategories = [
food,
transport,
travel, // Add here
// ...
];
String formatDate(DateTime date) {
if (date.isToday) return 'Today';
if (date.isYesterday) return 'Yesterday';
return DateFormat('MMM d, y').format(date);
}
  1. Validate amounts: Ensure positive numbers
  2. Require category: Every transaction needs a category
  3. Default wallet: Pre-select primary wallet
  4. Default date: Use current date
  5. Confirm deletes: Show confirmation dialog
  6. Undo option: Allow undo for recent deletes
  7. Receipt photos: Optional image attachment
test('Transaction creation updates wallet balance', () {
final wallet = WalletMock.cash();
final initialBalance = wallet.balance;
final transaction = TransactionModel(
amount: 100,
type: TransactionType.expense,
wallet: wallet,
// ...
);
// Process transaction
wallet.balance -= transaction.amount;
expect(wallet.balance, initialBalance - 100);
});