코드코도

[코독하구만 2] 2주차 - Flutter : Personal Expense App 본문

Act/2021_여름_모각코_개인

[코독하구만 2] 2주차 - Flutter : Personal Expense App

고도고도 고도고도 2021. 7. 15. 20:19
728x90

Udemy 강의를 듣으면서 Flutter를 학습하고 있다.

이번 주는 Builder, Container, Column, Row, CrossAxisAlignment, ListView, Button 등을 활용하여

가계부 어플을 만들었다. 아니 만드는 중이다.

 

전체적인 구조는 AppBar 우측 상단과 하단에 기록을 추가할 수 있는 버튼이 있고

버튼을 클릭하면 새로운 소비항목을 등록할 수 있다.

또한 AppBar 아래에는 1주일 간의 소비 금액을 한 눈에 확인할 수 있는 차트를 구현하고 있다.

 

이번 어플의 전체적인 위젯 트리 구조는 아래와 같다.

 

assets 폴더에는 어플에서 사용할 폰트와 이미지가 저장되어있다.

models에는 소비항목과 관련된 변수가 선언되어있고,

widgets에는 소비항목을 UI에 표시할 메소드들이 선언되어있다.

main에서는 widgets을 이용하여 최종적으로 화면에 표현한다.

 

우선, models > transaction.dart 를 살펴보자.

import 'package:flutter/foundation.dart';

class Transaction {
  final String id;
  final String title;
  final double amount;
  final DateTime date;

  Transaction({
    required this.id,
    required this.title,
    required this.amount,
    required this.date,
  });
}

위에서 말했듯 소비항목에 관련된 변수들이 선언되어있다. 클래스 하단의 코드는 생성자이다.

좀 특이하게 생겼지만 id = this.id 이런 식의 형태를 암시적 선언이라고 한다.

 

두 번째는 widgets > new_transaction.dart 이다.

파일명에서 볼 수 있듯 새로운 소비항목이 입력되는 것을 처리한다.

import 'package:flutter/material.dart';

class NewTransaction extends StatefulWidget {
  final Function addTx;

  NewTransaction(this.addTx);

  @override
  State<NewTransaction> createState() => _NewTransactionState();
}

class _NewTransactionState extends State<NewTransaction> {
  final titleController = TextEditingController();
  final amountController = TextEditingController();

  void submitData(){
    final enteredTitle = titleController.text;
    final enteredAmount = double.parse(amountController.text);

    if (enteredTitle.isEmpty || enteredAmount <=0) {
      return;
    }
    widget.addTx(
        enteredTitle,
        enteredAmount,
    );

    Navigator.of(context).pop();
  }

  @override
  Widget build(BuildContext context) {
    return Card(
      elevation: 5,
      child: Container(
        padding: EdgeInsets.all(10),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.end,
          children: <Widget>[
            TextField(
              decoration: InputDecoration(labelText: 'Title'),
              // 1번 방법을 사용하면 아래 1줄처럼 코드를 작성
              // onChanged: (val) => titleInput = val,
              controller: titleController,
              onSubmitted: (_) => submitData,
            ),
            TextField(
              decoration: InputDecoration(labelText: 'Amount'),
              // 1번 방법을 사용하면 아래 1줄처럼 코드를 작성
              // onChanged: (val) => amountInput = val,
              controller: amountController,
              onSubmitted: (_) => submitData,
            ),
            FlatButton(
              child: Text('Add Transaction'),
              textColor: Colors.purple,
              onPressed: submitData,
            ),
          ],
        ),
      ),
    );
  }
}

Flutter를 처음 접하면 가장 신기한 것 중에 하나가 StatefulWidgetStatelessWidget이다.

뭐 간단하게 설명하자면 해당 클래스에서 변수의 변화(?), 데이터의 변경이 있는 경우 StatefulWidget이고 그렇지 않은 경우는 StatelessWidget이다.

예를 들어 while () => {  i += 1 } 이런 경우는 StatefulWidget이라고 생각하면 된다.

물론 이렇게 단순하게 나눠지는 것은 아니지만 깊숙히 들어가면 머리아프니까...

 

아무튼 새로운 소비 항목이 추가되는 과정이 일어나니까 결국엔 데이터의 변경이 있는 것이고,

따라서 StatefulWidget으로 선언해줬다.

 

이렇게 StatefulWidget으로 선언하면 아래처럼 하나의 클래스가 따라온다.

class NewTransaction extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _NewTransactionState();
}

class _NewTransactionState extends State<NewTransaction> {
  }

이것도 간단하게 설명하자면 WidgetClass에서 createState를 @override 하여 State Class를 Link 한다는 뜻이다.

이후 WidgetClass에서 @override를 통해 새로운 소비항목을 처리하는 코드를 구현한다.

@override
  Widget build(BuildContext context) {
    return Card(
      elevation: 5,
      child: Container(
        padding: EdgeInsets.all(10),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.end,
          children: <Widget>[
            TextField(
              decoration: InputDecoration(labelText: 'Title'),
              // 1번 방법을 사용하면 아래 1줄처럼 코드를 작성
              // onChanged: (val) => titleInput = val,
              controller: titleController,
              onSubmitted: (_) => submitData,
            ),
            TextField(
              decoration: InputDecoration(labelText: 'Amount'),
              // 1번 방법을 사용하면 아래 1줄처럼 코드를 작성
              // onChanged: (val) => amountInput = val,
              controller: amountController,
              onSubmitted: (_) => submitData,
            ),
            FlatButton(
              child: Text('Add Transaction'),
              textColor: Colors.purple,
              onPressed: submitData,
            ),
          ],
        ),
      ),
    );
  }

js와 비슷한 형태를 띄고있는데 Flutter는 Widget으로 시작해서 Widget으로 끝난다고 생각하면 된다.

Widget에 관해서 조금 정리해 놓은게 있어서 첨부했다.

https://codekodo.notion.site/21-07-08-Flutter-Widget-2eac1931561e46f8857fc0d51792cc3c

 

21.07.08(목) Flutter - Widget

플러터는 위젯으로 시작해서 위젯으로 끝난다.

codekodo.notion.site

 

아무튼 Card > Container, Column > TextField 등으로 각 요소들을 구현했다.

중간에 child와 children이 있는 걸 볼 수 있는데 둘의 차이는 영어 단어 뜻이랑 똑같다.

해당 구조에서 자녀가 한 명 존재하는 경우 child, 둘 이상인 경우 children으로 선언한다.

실제로 코드를 보면 Card의 child는 Container 1개, Container의 child는 Column으로 구현한 것을 볼 수 있다.

Column에는 소비항목에서 제품명과 가격 그리고 등록 버튼, 이렇게 총 3개의 Layout이 필요해서 Children으로 구현했다.

아래는 실제 애뮬레이터에서 어플을 캡쳐한 것이다.

 

 

이번 주 모각코는 여기까지였다.

이번 주내로 어플 구현을 마무리하고 다음 주 모각코 때 나머지 Widget에 대해 설명을 하고 어플에 대한 리뷰를 해야겠다.

728x90
0 Comments
댓글쓰기 폼
Prev 1 2 3 4 5 6 7 Next