💻
Gürkan Fikret Günak - Personal
  • 👨‍💻About me
    • 🌊Journey
  • 🎯Dart
    • 🔬What's Dart Algorithms?
    • 🔬What's Dart Structures?
    • 🧮#01 Algorithm Guidance: Implementing Calculation Algorithms
    • 🧮#02 Algorithm Guidance: Two Sum
  • 📄Guidances
    • Flutter MVVM Guidance
    • Dart Programming Guidance
    • E-Commerce Use Cases
    • E-Commerce Applications
    • Flutter App Color Palette Usage Guidance
    • Flutter Custom AppBar Usage Guidance
    • Flutter Network Image Cache Usage Guidance
    • Flutter Project Bitbucket SSH Guidance
    • Flutter Project GitHub SSH Guidance
    • Flutter SliverAppBar Usage Guidance
    • The Importance of BuildContext in Flutter Tests Guidance
    • Internship Basic Guidance v0.1.0
    • The Importance of Type Casting in Flutter
    • Effective and Detailed Pull Request Guide
    • Flutter Naming Conventions Guidance
    • Flutter Widget Guidance
    • Semantic Commit Guidance
    • Being Part of a Mobile Software Team and Working on a Shared Architecture
    • Understanding Deep Links for Any Development Platform
    • The Journey of a Developer: Stories of Becoming Junior, Middle, and Senior Developer
    • Becoming a Team Leader: Growing in Sync with Your Team
    • Why IP Changes Are Important for Mobile Applications in Flutter
    • Why Your Growing Mobile Team Needs CI/CD and How to Build a Winning Strategy
    • Dart in 2024: 20 Features You Need to Know With Code Examples and Scenarios
    • Remote Theme Management with API (JSON): Implementing a Helper in Flutter SDK
    • Understanding and Implementing Force Upgrade in Your Flutter Project
    • Life Lessons from the Bald Eagle: A Metaphor for Growth, Change, and Leadership
    • The Beauty of Imperfection: Why Today Doesn’t Need to Be Perfect
    • # The Reverse Curve of Productivity: When Social Cohesion in Software Teams Starts to Hurt **How str
    • 📱 Mobil Uygulamalarda GraphQL Tercihi: Bakım ve Maliyet Etkiler
    • 📉 Türkiye’de Yazılım Projelerinde Süreç Yönetimi ve Ekonomik Kayıp: Bir Bekâ Sorunu mu?
  • 📹VIDEOS
    • Introduction to Flutter Boilerplate! ( Turkish )
    • Flutter APIs effective using ( English )
    • Understand to SDK ( English )
  • Links
    • 💼 | Linkedin
    • 🆇 | x.com
    • 📧 | Mail me
Powered by GitBook
On this page
  • Flutter MVVM Guidance
  • Introduction
  • 1. What is Model?
  • 2. What is View?
  • 3. What is ViewModel?
  • 4. How to Implement MVVM?
  • MVVM Use Cases - Banking Application
  • 1. View User Account Information
  • 2. Perform Money Transfer
  • 3. View Account Transaction History
  • 4. Deposit and Withdraw Money
  • 5. Apply for a Loan
  • 6. Approve and Reject Loan Applications
  • 7. Loan Calculator
  • 8. Wallet Application
  • 9. Filter Account History
  • 10. Sort Account History
  1. Guidances

Flutter MVVM Guidance

Flutter MVVM Guidance

Introduction

MVVM (Model-View-ViewModel) is a design pattern that organizes the logical structure of software applications. MVVM consists of three main components: Model, View, and ViewModel. In this guidance, we will explore how to implement MVVM architecture in Flutter and provide 10 example use cases.

1. What is Model?

The Model represents the data structure and business logic of your application. It typically contains only the data structure and business logic, without any direct connection to the UI. For example, in a to-do app, you can create a "Task" class that holds data like task name, description, and completion status:

class Task {
  final String name;
  final String description;
  bool isCompleted;

  Task({required this.name, required this.description, this.isCompleted = false});
}

2. What is View?

The View represents the user interface. It includes visual components, interface design, and user interaction. The View is independent of the ViewModel and doesn't directly communicate with the Model. For example, you can create a Widget using ListView.builder to list tasks as follows:

class TaskListView extends StatelessWidget {
  final List<Task> tasks;

  TaskListView({required this.tasks});

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: tasks.length,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text(tasks[index].name),
          subtitle: Text(tasks[index].description),
          leading: Checkbox(
            value: tasks[index].isCompleted,
            onChanged: (value) {
              // Notify the ViewModel
            },
          ),
        );
      },
    );
  }
}

3. What is ViewModel?

The ViewModel acts as an intermediary between the Model and View. It retrieves data from the Model to fulfill the View's needs and processes user interactions from the View to update the Model. The ViewModel should be completely independent of the View, making application logic easy to test and modify. For instance, in the above task list, the ViewModel can be defined as:

import 'package:flutter/material.dart';

class TaskListViewModel extends ChangeNotifier {
  List<Task> tasks = [
    Task(name: 'Go shopping', description: 'Buy bread and milk', isCompleted: false),
    Task(name: 'Do exercise', description: 'Run for 30 minutes', isCompleted: true),
    Task(name: 'Read a book', description: 'Finish the last book you read', isCompleted: false),
    // Add more tasks here
  ];

  void updateTaskCompletion(Task task, bool isCompleted) {
    task.isCompleted = isCompleted;
    notifyListeners();
  }
}

4. How to Implement MVVM?

To implement MVVM in Flutter, follow these steps:

a. Create the Model: Define classes that represent your data structure and business logic.

b. Create the ViewModel: Build a ViewModel class or functions that provide the connection between the Model and View.

c. Create the View: Design the user interface using Flutter widgets.

d. Connect ViewModel and View: Establish the connection between the View and ViewModel using data binding mechanisms.

e. Services and Repositories: Manage data sources, API calls, and connections between Model and View using services and repositories.


MVVM Use Cases - Banking Application

1. View User Account Information

In this use case, we will use MVVM to display user account information.

Model:

class BankAccount {
  final String accountNumber;
  final String accountHolderName;
  double balance;

  BankAccount({required this.accountNumber, required this.accountHolderName, this.balance = 0.0});
}

ViewModel:

import 'package:flutter/material.dart';

class AccountViewModel extends ChangeNotifier {
  BankAccount _account = BankAccount(accountNumber: '123456789', accountHolderName: 'John Doe', balance: 5000.0);

  BankAccount get account => _account;
}

View:

class AccountView extends StatelessWidget {
  final AccountViewModel viewModel;

  AccountView({required this.viewModel});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Account Information')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Account Number: ${viewModel.account.accountNumber}'),
            Text('Account Holder: ${viewModel.account.accountHolderName}'),
            Text('Balance: \$${viewModel.account.balance.toStringAsFixed(2)}'),
          ],
        ),
      ),
    );
  }
}

2. Perform Money Transfer

In this use case, we will use MVVM to perform money transfers between users.

Model:

class Transaction {
  final String fromAccount;
  final String toAccount;
  double amount;

  Transaction({required this.fromAccount, required this.toAccount, required this.amount});
}

ViewModel:

import 'package:flutter/material.dart';

class TransactionViewModel extends ChangeNotifier {
  List<Transaction> _transactions = [];

  List<Transaction> get transactions => _transactions;

  void performTransaction(Transaction transaction) {
    _transactions.add(transaction);
    notifyListeners();
  }
}

View:

class TransactionView extends StatelessWidget {
  final TransactionViewModel viewModel;

  TransactionView({required this.viewModel});

  final TextEditingController _fromAccountController = TextEditingController();
  final TextEditingController _toAccountController = TextEditingController();
  final TextEditingController _amountController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Perform Transaction')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextField(controller: _fromAccountController, decoration: InputDecoration(labelText: 'From Account')),
            TextField(controller: _toAccountController, decoration: InputDecoration(labelText: 'To Account')),
            TextField(controller: _amountController, keyboardType: TextInputType.number, decoration: InputDecoration(labelText: 'Amount')),
            ElevatedButton(
              onPressed: () {
                double amount = double.parse(_amountController.text);
                Transaction transaction = Transaction(
                  fromAccount: _fromAccountController.text,
                  toAccount: _toAccountController.text,
                  amount: amount,
                );
                viewModel.performTransaction(transaction);
                _fromAccountController.clear();
                _toAccountController.clear();
                _amountController.clear();
              },
              child: Text('Transfer'),
            ),
          ],
        ),
      ),
    );
  }
}

3. View Account Transaction History

In this use case, we will use MVVM to view the account transaction history.

Model:

class AccountHistory {
  final String description;
  double amount;
  DateTime date;

  AccountHistory({required this.description, required this.amount, required this.date});
}

ViewModel:

import 'package:flutter/material.dart';

class AccountHistoryViewModel extends ChangeNotifier {
  List<AccountHistory> _history = [
    AccountHistory(description: 'Deposit', amount: 1000.0, date: DateTime.now().subtract(Duration(days: 5))),
    AccountHistory(description: 'Withdrawal', amount: -500.0, date: DateTime.now().subtract(Duration(days: 3))),
    AccountHistory(description: 'Transfer', amount: -200.0, date: DateTime.now().subtract(Duration(days: 2))),
  ];

  List<AccountHistory> get history => _history;
}

View:

class AccountHistoryView extends StatelessWidget {
  final AccountHistoryViewModel viewModel;

  AccountHistoryView({required this.viewModel});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Account History')),
      body: ListView.builder(
        itemCount: viewModel.history.length,
        itemBuilder: (context, index) {
          final historyItem = viewModel.history[index];
          return ListTile(
            leading: historyItem.amount > 0 ? Icon(Icons.arrow_downward, color: Colors.green) : Icon(Icons.arrow_upward, color: Colors.red),
            title: Text(historyItem.description),
            subtitle: Text('\$${historyItem.amount.toStringAsFixed(2)}'),
            trailing: Text('${historyItem.date.day}/${historyItem.date.month}/${historyItem.date.year}'),
          );
        },
      ),
    );
  }
}

4. Deposit and Withdraw Money

In this use case, we will use MVVM to deposit and withdraw money from the account.

Model:

class DepositWithdrawal {
  double amount;

  DepositWithdrawal({required this.amount});
}

ViewModel:

import 'package:flutter/material.dart';

class DepositWithdrawalViewModel extends ChangeNotifier {
  BankAccount _account = BankAccount(accountNumber: '123456789', accountHolderName: 'John Doe', balance: 5000.0);

  BankAccount get account => _account;

  void performDeposit(DepositWithdrawal deposit) {
    _account.balance += deposit.amount;
    notifyListeners();
  }

  void performWithdrawal(DepositWithdrawal withdrawal) {
    if (_account.balance >= withdrawal.amount) {
      _account.balance -= withdrawal.amount;
      notifyListeners();
    } else {
      // Not enough balance, show error message or take appropriate action
    }
  }
}

View:

class DepositWithdrawalView extends StatelessWidget {
  final DepositWithdrawalViewModel viewModel;

  DepositWithdrawalView({required this.viewModel});

  final TextEditingController _amountController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Deposit or Withdraw')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextField(controller: _amountController, keyboardType: TextInputType.number, decoration: InputDecoration(labelText: 'Amount')),
            ElevatedButton(
              onPressed: () {
                double amount = double.parse(_amountController.text);
                DepositWithdrawal deposit = DepositWithdrawal(amount: amount);
                viewModel.performDeposit(deposit);
                _amountController.clear();
              },
              child: Text('Deposit'),
            ),
            ElevatedButton(
              onPressed: () {
                double amount = double.parse(_amountController.text);
                DepositWithdrawal withdrawal = DepositWithdrawal(amount: amount);
                viewModel.performWithdrawal(withdrawal);


                _amountController.clear();
              },
              child: Text('Withdraw'),
            ),
          ],
        ),
      ),
    );
  }
}

5. Apply for a Loan

In this use case, we will use MVVM to apply for a loan.

Model:

class LoanApplication {
  final String applicantName;
  double amount;
  bool approved;

  LoanApplication({required this.applicantName, required this.amount, this.approved = false});
}

ViewModel:

import 'package:flutter/material.dart';

class LoanApplicationViewModel extends ChangeNotifier {
  List<LoanApplication> _applications = [];

  List<LoanApplication> get applications => _applications;

  void applyForLoan(LoanApplication application) {
    _applications.add(application);
    notifyListeners();
  }
}

View:

class LoanApplicationView extends StatelessWidget {
  final LoanApplicationViewModel viewModel;

  LoanApplicationView({required this.viewModel});

  final TextEditingController _nameController = TextEditingController();
  final TextEditingController _amountController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Apply for Loan')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextField(controller: _nameController, decoration: InputDecoration(labelText: 'Applicant Name')),
            TextField(controller: _amountController, keyboardType: TextInputType.number, decoration: InputDecoration(labelText: 'Loan Amount')),
            ElevatedButton(
              onPressed: () {
                String name = _nameController.text;
                double amount = double.parse(_amountController.text);
                LoanApplication application = LoanApplication(applicantName: name, amount: amount);
                viewModel.applyForLoan(application);
                _nameController.clear();
                _amountController.clear();
              },
              child: Text('Apply'),
            ),
          ],
        ),
      ),
    );
  }
}

6. Approve and Reject Loan Applications

In this use case, we will use MVVM to approve and reject loan applications.

ViewModel:

import 'package:flutter/material.dart';

enum LoanApplicationStatus {
  Pending,
  Approved,
  Rejected,
}

class LoanApprovalViewModel extends ChangeNotifier {
  List<LoanApplication> _applications = [
    LoanApplication(applicantName: 'Jane Doe', amount: 5000.0),
    LoanApplication(applicantName: 'Michael Smith', amount: 8000.0),
  ];

  List<LoanApplication> get applications => _applications;

  void approveApplication(LoanApplication application) {
    application.approved = true;
    notifyListeners();
  }

  void rejectApplication(LoanApplication application) {
    application.approved = false;
    notifyListeners();
  }

  LoanApplicationStatus getApplicationStatus(LoanApplication application) {
    return application.approved ? LoanApplicationStatus.Approved : LoanApplicationStatus.Rejected;
  }
}

View:

class LoanApprovalView extends StatelessWidget {
  final LoanApprovalViewModel viewModel;

  LoanApprovalView({required this.viewModel});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Loan Applications')),
      body: ListView.builder(
        itemCount: viewModel.applications.length,
        itemBuilder: (context, index) {
          final application = viewModel.applications[index];
          return ListTile(
            title: Text(application.applicantName),
            subtitle: Text('\$${application.amount.toStringAsFixed(2)}'),
            trailing: application.approved
                ? Icon(Icons.check_circle, color: Colors.green)
                : Icon(Icons.cancel, color: Colors.red),
            onTap: () {
              showDialog(
                context: context,
                builder: (context) => AlertDialog(
                  title: Text('Loan Application Status'),
                  content: Text(viewModel.getApplicationStatus(application).toString().split('.').last),
                  actions: [
                    TextButton(
                      onPressed: () {
                        Navigator.pop(context);
                      },
                      child: Text('Close'),
                    ),
                  ],
                ),
              );
            },
          );
        },
      ),
    );
  }
}

7. Loan Calculator

In this use case, we will use MVVM to calculate loan EMIs.

Model:

class LoanCalculator {
  double calculateEMI(double principal, double interestRate, int loanTerm) {
    double monthlyInterestRate = interestRate / 12 / 100;
    double monthlyPayment = principal * monthlyInterestRate / (1 - (pow(1 + monthlyInterestRate, -loanTerm)));
    return monthlyPayment;
  }
}

ViewModel:

import 'package:flutter/material.dart';

class LoanCalculatorViewModel extends ChangeNotifier {
  LoanCalculator _calculator = LoanCalculator();
  double _principal = 0.0;
  double _interestRate = 0.0;
  int _loanTerm = 1;
  double _monthlyPayment = 0.0;

  double get principal => _principal;
  set principal(double value) {
    _principal = value;
    calculateEMI();
  }

  double get interestRate => _interestRate;
  set interestRate(double value) {
    _interestRate = value;
    calculateEMI();
  }

  int get loanTerm => _loanTerm;
  set loanTerm(int value) {
    _loanTerm = value;
    calculateEMI();
  }

  double get monthlyPayment => _monthlyPayment;

  void calculateEMI() {
    _monthlyPayment = _calculator.calculateEMI(_principal, _interestRate, _loanTerm);
    notifyListeners();
  }
}

View:

class LoanCalculatorView extends StatelessWidget {
  final LoanCalculatorViewModel viewModel;

  LoanCalculatorView({required this.viewModel});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Loan Calculator')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextField(
              keyboardType: TextInputType.number,
              decoration: InputDecoration(labelText: 'Loan Principal'),
              onChanged: (value) => viewModel.principal = double.parse(value),
            ),
            TextField(
              keyboardType: TextInputType.number,
              decoration: InputDecoration(labelText: 'Interest Rate (%)'),
              onChanged: (value) => viewModel.interestRate = double.parse(value),
            ),
            DropdownButton<int>(
              value: viewModel.loanTerm,
              items: [1, 2, 3, 4

, 5].map((term) {
                return DropdownMenuItem<int>(
                  value: term,
                  child: Text('$term years'),
                );
              }).toList(),
              onChanged: (value) => viewModel.loanTerm = value!,
            ),
            SizedBox(height: 16),
            Text('Monthly Payment: \$${viewModel.monthlyPayment.toStringAsFixed(2)}'),
          ],
        ),
      ),
    );
  }
}

8. Wallet Application

In this use case, we will use MVVM to create a wallet application.

Model:

class WalletTransaction {
  final String transactionType;
  double amount;
  DateTime date;

  WalletTransaction({required this.transactionType, required this.amount, required this.date});
}

ViewModel:

import 'package:flutter/material.dart';

class WalletViewModel extends ChangeNotifier {
  List<WalletTransaction> _transactions = [];

  List<WalletTransaction> get transactions => _transactions;

  void addTransaction(WalletTransaction transaction) {
    _transactions.add(transaction);
    notifyListeners();
  }
}

View:

class WalletView extends StatelessWidget {
  final WalletViewModel viewModel;

  WalletView({required this.viewModel});

  final TextEditingController _amountController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Wallet')),
      body: Column(
        children: [
          Expanded(
            child: ListView.builder(
              itemCount: viewModel.transactions.length,
              itemBuilder: (context, index) {
                final transaction = viewModel.transactions[index];
                return ListTile(
                  title: Text(transaction.transactionType),
                  subtitle: Text('\$${transaction.amount.toStringAsFixed(2)}'),
                  trailing: Text('${transaction.date.day}/${transaction.date.month}/${transaction.date.year}'),
                );
              },
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Row(
              children: [
                Expanded(
                  child: TextField(controller: _amountController, keyboardType: TextInputType.number, decoration: InputDecoration(labelText: 'Amount')),
                ),
                ElevatedButton(
                  onPressed: () {
                    double amount = double.parse(_amountController.text);
                    WalletTransaction transaction = WalletTransaction(
                      transactionType: 'Deposit',
                      amount: amount,
                      date: DateTime.now(),
                    );
                    viewModel.addTransaction(transaction);
                    _amountController.clear();
                  },
                  child: Text('Deposit'),
                ),
                ElevatedButton(
                  onPressed: () {
                    double amount = double.parse(_amountController.text);
                    WalletTransaction transaction = WalletTransaction(
                      transactionType: 'Withdrawal',
                      amount: -amount,
                      date: DateTime.now(),
                    );
                    viewModel.addTransaction(transaction);
                    _amountController.clear();
                  },
                  child: Text('Withdraw'),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

9. Filter Account History

In this use case, we will use MVVM to filter account history by date range.

ViewModel:

import 'package:flutter/material.dart';

class AccountHistoryViewModel extends ChangeNotifier {
  List<AccountHistory> _history = [
    AccountHistory(description: 'Deposit', amount: 1000.0, date: DateTime.now().subtract(Duration(days: 5))),
    AccountHistory(description: 'Withdrawal', amount: -500.0, date: DateTime.now().subtract(Duration(days: 3))),
    AccountHistory(description: 'Transfer', amount: -200.0, date: DateTime.now().subtract(Duration(days: 2))),
  ];

  List<AccountHistory> get history => _history;

  List<AccountHistory> getFilteredHistory(DateTime startDate, DateTime endDate) {
    return _history.where((transaction) => transaction.date.isAfter(startDate) && transaction.date.isBefore(endDate)).toList();
  }
}

View:

class FilteredAccountHistoryView extends StatefulWidget {
  final AccountHistoryViewModel viewModel;

  FilteredAccountHistoryView({required this.viewModel});

  @override
  _FilteredAccountHistoryViewState createState() => _FilteredAccountHistoryViewState();
}

class _FilteredAccountHistoryViewState extends State<FilteredAccountHistoryView> {
  DateTime _startDate = DateTime.now().subtract(Duration(days: 7));
  DateTime _endDate = DateTime.now();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Filtered Account History')),
      body: Column(
        children: [
          Container(
            color: Colors.grey[200],
            child: Column(
              children: [
                Text('Start Date: ${_startDate.day}/${_startDate.month}/${_startDate.year}'),
                ElevatedButton(
                  onPressed: () {
                    showDatePicker(
                      context: context,
                      initialDate: _startDate,
                      firstDate: DateTime(2000),
                      lastDate: DateTime.now(),
                    ).then((selectedDate) {
                      if (selectedDate != null) {
                        setState(() {
                          _startDate = selectedDate;
                        });
                      }
                    });
                  },
                  child: Text('Select Start Date'),
                ),
                Text('End Date: ${_endDate.day}/${_endDate.month}/${_endDate.year}'),
                ElevatedButton(
                  onPressed: () {
                    showDatePicker(
                      context: context,
                      initialDate: _endDate,
                      firstDate: DateTime(2000),
                      lastDate: DateTime.now(),
                    ).then((selectedDate) {
                      if (selectedDate != null) {
                        setState(() {
                          _endDate = selectedDate;
                        });
                      }
                    });
                  },
                  child: Text('Select End Date'),
                ),
              ],
            ),
          ),
          Expanded(
            child: ListView.builder(
              itemCount: widget.viewModel.getFilteredHistory(_startDate, _endDate).length,
              itemBuilder: (context, index) {
                final historyItem = widget.viewModel.getFilteredHistory(_startDate, _endDate)[index];
                return ListTile(
                  leading: historyItem.amount > 0 ? Icon(Icons.arrow_downward, color: Colors.green) : Icon(Icons.arrow_upward, color: Colors.red),
                  title: Text(historyItem.description),
                  subtitle: Text('\$${historyItem.amount.toStringAsFixed(2)}'),
                  trailing: Text('${historyItem.date.day}/${historyItem.date.month}/${historyItem.date.year}'),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

10. Sort Account History

In this use case, we will use MVVM to sort account history by amount.

ViewModel:

import 'package:flutter/material.dart';

class AccountHistoryViewModel extends ChangeNotifier {
  List<AccountHistory> _history = [
    AccountHistory(description: 'Deposit', amount: 1000.0, date: DateTime.now().subtract(Duration(days: 5))),
    AccountHistory(description: 'Withdrawal', amount: -500.0, date: DateTime.now().subtract(Duration(days: 3))),
    AccountHistory(description: 'Transfer', amount: -200.0, date: DateTime.now().subtract(Duration(days: 2))),
  ];

  List<AccountHistory> get history => _history;

  List<AccountHistory> getSortedHistoryByAmount() {
    return List.from(_history)..sort((a, b) => b.amount.compareTo(a.amount));
  }
}

View:

class SortedAccountHistoryView extends StatelessWidget {
  final AccountHistoryViewModel viewModel;

 

 SortedAccountHistoryView({required this.viewModel});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Sorted Account History')),
      body: ListView.builder(
        itemCount: viewModel.getSortedHistoryByAmount().length,
        itemBuilder: (context, index) {
          final historyItem = viewModel.getSortedHistoryByAmount()[index];
          return ListTile(
            leading: historyItem.amount > 0 ? Icon(Icons.arrow_downward, color: Colors.green) : Icon(Icons.arrow_upward, color: Colors.red),
            title: Text(historyItem.description),
            subtitle: Text('\$${historyItem.amount.toStringAsFixed(2)}'),
            trailing: Text('${historyItem.date.day}/${historyItem.date.month}/${historyItem.date.year}'),
          );
        },
      ),
    );
  }
}

Previous#02 Algorithm Guidance: Two SumNextDart Programming Guidance

Last updated 6 months ago

📄