Project Goal
Build a banking system simulation with accounts, deposits, withdrawals, transfers, and transaction history. This project demonstrates OOP design, custom exceptions, encapsulation, and date-stamped audit trails.
Full Code
import 'dart:math';
class InsufficientFundsException implements Exception {
final double required, available;
InsufficientFundsException(this.required, this.available);
@override String toString() =>
'InsufficientFunds: need \$$${required.toStringAsFixed(2)}, have \$$${available.toStringAsFixed(2)}';
}
class AccountFrozenException implements Exception {
@override String toString() => 'AccountFrozenException: account is frozen';
}
enum TransactionType { deposit, withdrawal, transfer }
class Transaction {
final TransactionType type;
final double amount;
final String description;
final DateTime timestamp;
final double balanceAfter;
Transaction({required this.type, required this.amount, required this.description,
required this.balanceAfter}) : timestamp = DateTime.now();
@override String toString() {
var t = timestamp.toIso8601String().substring(0, 16);
var sign = type == TransactionType.deposit ? '+' : '-';
return '[$t] $sign\$$${amount.toStringAsFixed(2).padLeft(9)} | Bal: \$$${balanceAfter.toStringAsFixed(2).padLeft(10)} | $description';
}
}
class BankAccount {
final String accountNumber;
final String ownerName;
double _balance;
bool _frozen = false;
final List<Transaction> _transactions = [];
BankAccount({required this.ownerName, double initialBalance = 0})
: accountNumber = 'ACC$${Random().nextInt(900000) + 100000}',
_balance = initialBalance;
double get balance => _balance;
bool get isFrozen => _frozen;
List<Transaction> get history => List.unmodifiable(_transactions);
void _guard() {
if (_frozen) throw AccountFrozenException();
}
void deposit(double amount, {String note = 'Deposit'}) {
_guard();
if (amount <= 0) throw ArgumentError('Amount must be positive');
_balance += amount;
_transactions.add(Transaction(
type: TransactionType.deposit, amount: amount,
description: note, balanceAfter: _balance));
}
void withdraw(double amount, {String note = 'Withdrawal'}) {
_guard();
if (amount <= 0) throw ArgumentError('Amount must be positive');
if (amount > _balance) throw InsufficientFundsException(amount, _balance);
_balance -= amount;
_transactions.add(Transaction(
type: TransactionType.withdrawal, amount: amount,
description: note, balanceAfter: _balance));
}
void transferTo(BankAccount target, double amount) {
withdraw(amount, note: 'Transfer to $${target.accountNumber}');
target.deposit(amount, note: 'Transfer from $accountNumber');
}
void freeze() => _frozen = true;
void unfreeze() => _frozen = false;
void printStatement() {
print('\n===== Account Statement =====');
print('Account: $accountNumber | Owner: $ownerName');
print('Balance: \$$${_balance.toStringAsFixed(2)} | Status: $${_frozen ? "FROZEN" : "Active"}');
print('\nTransaction History ($${_transactions.length} transactions):');
_transactions.forEach(print);
}
}
class Bank {
final String name;
final Map<String, BankAccount> _accounts = {};
Bank(this.name);
BankAccount openAccount(String owner, {double initial = 0}) {
var account = BankAccount(ownerName: owner, initialBalance: initial);
_accounts[account.accountNumber] = account;
print('Opened account $${account.accountNumber} for $owner');
return account;
}
BankAccount? findAccount(String number) => _accounts[number];
double get totalDeposits => _accounts.values.fold(0, (s, a) => s + a.balance);
}
void main() {
var bank = Bank('Dart National Bank');
var alice = bank.openAccount('Alice Johnson', initial: 1000.0);
var bob = bank.openAccount('Bob Smith', initial: 500.0);
alice.deposit(250.0, note: 'Salary');
alice.withdraw(100.0, note: 'ATM withdrawal');
alice.transferTo(bob, 200.0);
try {
bob.withdraw(1000.0);
} on InsufficientFundsException catch (e) {
print('Caught: $e');
}
alice.freeze();
try {
alice.deposit(50.0);
} on AccountFrozenException catch (e) {
print('Caught: $e');
}
alice.unfreeze();
alice.printStatement();
bob.printStatement();
print('\nBank total deposits: \$$${bank.totalDeposits.toStringAsFixed(2)}');
}
Challenges
- Add savings accounts with interest calculation
- Add loan accounts with repayment schedules
- Persist accounts to JSON between sessions
- Add PIN/password authentication