Front-End/Flutter_Project_02_Expense Tracker

2. Udemy Flutter 강의를 통한 Project - Expense Tracker

sd4beatles 2024. 12. 5. 04:34

1. Introduction

지금까지, 위의 화면을 만들었으며,  widgets을 저장할 수 있는 폴더를 생성해서 담아두도록 하자.

- lib
|-models
|-widgets
|-expense\_item.dart
|-expenses\_list.dart

 

 

현재까지 우리는 아래의 class를 정의내리고 살펴보았다.   그리고 이번 시간에 우리가 정의내릴 class는 바로 Expense Item이다. 자세한 설명은 아래의 내용을 참조할 것. 

 

Expense (일반 Class)   각 Expense가 가져야할 목록들을 지정하는 클래스
   
   - title
   - amount
   - date
   - category
    - uuid
Expenses (StatefulWidget) - State class에 현재까지 user가 등록한 expense를 모아놓은 list가 존재함. 
- Build-> body -> Column ->children option에 ExpenseList class를 가지고옴

ExpenseList (StatelessWidget) ExpenseList는 Expenses class에서 보내온 List<Expense>을 매개변수로 받으면서, 각각의 Expense 목록을 화면에 보여주는 역할을 함. (ListView.builder가 이 역할을 수행함)


ExpenseItem(StatelessWidget) ExpenseList는 각 expense의 목록을 변수로 받는다. 다만 추후에 더 자세하게 알아볼 예정이지만, ExpesenList에서 보내지는 각 expense widget class 객체를, 
각각의 item들을 더 stylish하게 Design하는  Widget class라고 생각하면 된다. 
   

 

 

expense_item의 widget class는 아래같이 정의내려 진다.

import 'package:flutter/material.dart';  
import 'package:expense\_tracker/models/expense.dart';

class ExpenseItem extends StatelessWidget{  
const ExpenseItem(this.expense,{super.key});


final Expense expense;

@override
Widget build(BuildContext context) {  
return Card(child:Text(expense.title));  
}  
}  

이전에 정의내렸던 expense_list.dart 또한 widgets folder로 이동을 시킨다. 그리고 아래와 같이, 약간의 수정을 해준다. expense object를 ExpenseItem의 매개변수로 전달해주면, 아래의 이미지와 같이 아래의 'Card'모양이 생성된다.

import 'package:expense_tracker/widgets/expense_item.dart';
import 'package:flutter/material.dart';
import 'package:expense_tracker/models/expense.dart';

class ExpensesList extends StatelessWidget{
  //initialize constructor
  const ExpensesList({super.key,required this.expenses,});


  final List<Expense>expenses;



  @override
  Widget build(BuildContext context){

    return ListView.builder(itemCount:expenses.length ,
    itemBuilder: (ctx,index)=>ExpenseItem(expenses[index])
    );
  }
}

2. Additional Modifications to 'expense_item' widget

2.1 Padding

위의 Card 구조의 padding을 담당하는 부분은 Card 가 가지고 있지 않기때문에, 우리는 refractor을 통해서, padding을 조정해줘야 한다.

class ExpenseItem extends StatelessWidget{
    const ExpenseItem(this.expense,{super.key});

    final Expense expense;

    @override
  Widget build(BuildContext context) {
    return Card(

      child:Padding(
        padding: const EdgeInsets.symmetric(
          horizontal: 20,
          vertical: 15
        ),
        child: Text(expense.title),
      )
      );
  }
}

2. Row and Column

이전 프로젝트에서도 살펴보왔듯이, Column에 space를 주는 것은 const SiedBox였다.그렇다면 Row는 어떻게 사용해야 할까? Row는 Spacer를 사용해서 띄어쓰기를 사용할 수 있다.

*한 가지 추가사항으로 Row() 항목을 추가하고 Spacer 다음에 또 다른 항목을 추가하고 싶을 때는 Row() 사용해서 추가한다. *

import 'package:flutter/material.dart';  
import 'package:expense\_tracker/models/expense.dart';

class ExpenseItem extends StatelessWidget{  
const ExpenseItem(this.expense,{super.key});


final Expense expense;

@override

Widget build(BuildContext context) {  
return Card(

  child:Padding(
    padding: const EdgeInsets.symmetric(
      horizontal: 20,
      vertical: 15
    ),
    child: Column(children: [
      Text(expense.title),
      const SizedBox(height:4),
      Row(children: [
        //toStringsAsFixed acting to limit the decimal size
        Text(expense.amount.toStringAsFixed(2)),
        const Spacer(),
        #추가사항시 Row를 추가해서 사용한다. 
        Row(children: [
          const Icon(Icons.alarm),
          const SizedBox(width:8),
          Text(expense.date.toString()),
        ],)

      ],)
    ],)
  )
  );


}  
}

3. Using Icons and Formatting Dates

3.1 Icons

expense.dart를 돌아가보도록 하자. 우리는 이전에 Category항목에 'food,travel,leisure,work'를 작성했었다. 그럼 이번에는 각각의 항목에다가 icond를 집어넣는것은 어떨까? 이는 Icons class를 통해서 구현된다.

import 'package:flutter/material.dart';
import 'package:uuid/uuid.dart';



// make uuid unresulable 
const  uuid= Uuid();

enum Category {food,travel,leisure,work}

const categoryIcon={
  Category.food:Icons.lunch_dining,
  Category.travel:Icons.flight_takeoff,
  Category.leisure:Icons.movie,
  Category.work:Icons.work,
};

3.2 Formatting Dates

위의 화면을 보면, Date 정보에서 초단위가 너무 길게 나오는 모습을 볼 수 있다. 이건 보기에도 그리고 사용자의 app 사용경험에 큰 지장을 줄 수 있기에, 우리는 Date를 조정하는 방법을 여기서 알아보도록 하자.

flutter에서 Date를 조정하는 방법은 상당히 귀찮을 수 있어서, 우린 외부 library를 사용해서, Date를 조작해보도록 한다."

flutter  pub add intl

Intl에서 제공하는 DateFormat과 그에 해당되는 option을 통해서 Date를 조작할 수 있는데, 우리는 "yMd"를 사용하도록 하자.

//formatter
final formatter=DateFormat.yMd();

class Expense{

  //constructor;retrieve arguments
  Expense({
    required this.title,
    required this.amount,
    required this.date,
    required this.category,
  }): id=uuid.v4();

  final String id; 
  final String title;
  final double amount;
  final DateTime date;
  final Category category;

  String get formattedDate{
    return formatter.format(date);
  }

}

위와 같이 정의를 내린 후에, expense_item.dart에 가서 Icon,Date를 조정하도록 하자.


class ExpenseItem extends StatelessWidget{
    const ExpenseItem(this.expense,{super.key});

    final Expense expense;

    @override
  Widget build(BuildContext context) {
    return Card(

      child:Padding(
        padding: const EdgeInsets.symmetric(
          horizontal: 20,
          vertical: 15
        ),
        child: Column(children: [
          Text(expense.title),
          const SizedBox(height:4),
          Row(children: [
            //toStringsAsFixed acting to limit the decimal size
            Text(expense.amount.toStringAsFixed(2)),
            const Spacer(),
            Row(children: [
              Icon(categoryIcons[expense.category]),
              const SizedBox(width:8),
              Text(expense.formattedDate),
            ],)