Flutter 개발일지 day 4

코드 재구성

  1. monthCal에 너무 방대한 양의 코드가 있어서 가시성이 떨어졌다. 이를테면, 월~일을 표시하는 row가 통째로 나열되어 있어서 더이상 바뀌지 않는 고정된 내용임에도 불구하고 매번 다른 코드를 수정하려면 스크롤을 많이 내려야 했다 « 이거 진짜 큰 문제이다. 지인짜 불편함;

  2. divider, sized box등 const 위젯이 너무 많다. 레이아웃을 구성하는데 필요하니까 어쩔 수 없긴 한데, 거듭 말하지만 바뀌지 않는 위젯들이 dart 파일에서 너무 큰 비중을 차지하고 있다는 것이 문제다.

여기에 대한 해결점으로, calenderElement 파일을 새로 만들었다. 여기서는 calender 레이아웃에 쓰이는 const 위젯들을 통째로 불러오게 시킨다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class DayofWeek extends StatelessWidget {
  const DayofWeek({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Row(
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        Expanded(child: MyText("월", 15)),
        Expanded(child: MyText("화", 15)),
        Expanded(child: MyText("수", 15)),
        Expanded(child: MyText("목", 15)),
        Expanded(child: MyText("금", 15)),
        Expanded(child: MyText("토", 15)),
        Expanded(child: MyText("일", 15)),
      ],
    ); //요일 표시 -> 항상 고정
  }
}

class DivBox extends StatelessWidget {
  const DivBox({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: const [
        Divider(
          indent: 5.0,
          endIndent: 5.0,
          height: 0,
        ),
        SizedBox(
          height: 8.0,
        ),
      ],
    );
  }
}

그러니까 다른 파일에서 stateless widget을 만들고, 원래 파일에서 그 파일을 import 한다음 그 위젯을 불러오면 된다. 이러면 바뀌지 않는 몸집이 큰 위젯을 아주 손쉽게 단어 하나로 치환할 수 있다!

기능 모듈화

엄밀히 말하면 이것도 코드 재구성이긴 한데, 위에 담은 내용은 전반적인 간결화를 위한 것이고, 이건 기능별로 묶기 위함이다. 원래 monthCal에 위치한 특정 일자의 주차를 계산하는 함수를 GenerateDay class가 정의된 파일로 옮겼다. 날짜와 관련된 연산은 daylist.dart 파일에서만 하도록 모아놨다.

달력 레이아웃 완성

현재는 컨테이너 42개가 항상 존재해서, 설령 4주 밖에 없는 달이더라도 텅 빈 container와 divider가 표시되는 형태였다. 1, 5, 6주차는 상황에 따라서 없어질 수도 있는 row이다. 정확히는, 5, 6차는 row 자체가 사라질 수 있고, 1주차는 row는 항상 존재하지만 상황에 따라(예를 들어 그 달이 일요일로 시작하는 경우) 하루를 제외한 나머지 날짜가 모두 텅 빌 수도 있다. 그래서 삼항연산자를 통해 이를 고려하게 만들었다. 플러터에서는 위젯 단위 삼항연산자를 지원해서 편하게 위젯을 조건에 따라 onoff 시킬 수 있다.

1
2
3
4
5
Expanded(child: 
    Container(
        height: 40,
        child: daylist[i].day > 7 ? null : MyText(daylist[i].day.toString(), 15),
    )

Datetime 자료형이 내 생각보다 굉장히 똑똑하다고 느낀 순간이 언제였냐면, Datetime(year, month, 1)은 그 달의 첫 날이고, Datetime(year, month, 0)은 그 전 달의 마지막 날이다… 이런 편리한 기능이 내장된 덕에, 우리는 daylist를 양방향으로 초기화해야하는 수고를 들이지 않아도 됐다 ㅋㅋㅋ 어찌어찌 잘 설정해놓으면, 42개의 컨테이너는 연속된 일자를 모두 가진다. 예를 들어 두 번째 컨테이너가 2월 1일이면 첫 번째 컨테이너는 1월 31일을 할당받는다!

아무튼 이로 인해서, 우리는 첫 주차에 표시되면 안되는 것들은(1일 이전의 값들) 예외 없이 31일 혹은 30일부터 거슬러내려간 6개의 값 정도임을 확신했다. 그래서 삼항연산자로 7보다 큰지 아닌지 비교시켜서, 그거보다 크면 이전 달의 value임으로 판단하고 null을 리턴하게 시킨거다.

5, 6주차도 비슷하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if(daylist[28].day > 8)
Column(
  children: [
    Row(
      children: [
        for(int i = 28; i<35; i++)
          Expanded(child: Container(
            height: 40,
            child: daylist[i].day < 15 ? null : MyText(daylist[i].day.toString(), 15),
          )),
      ],
    ),
    const DivBox(),
  ],
),

5, 6주차는 row가 통째로 사라져야하는 경우가 있기 때문에, 28일(5주차 1일, 즉 월요일)이 다음 달인 경우에는 1일부터 시작하기 때문에 이게 8일 이상인 경우(다음 달이 아닌 경우)에만 위젯이 나타나도록 했다.

여기까지 완성한 결과 daylist를 생성할 때 어떤 년, 월을 넣더라도 아주 정확하게 레이아웃을 구성할 수 있었다.

배운점

  1. 삼항 연산자는 아주 편리하다. 플러터와 같이 위젯을 표시하고 나타내는 영역에서 위젯 단위를 삼항 연산자로 처리하면 아주 편하게 조건에 따라 위젯을 표시할 수도, 끌 수도 있는 경우가 많았다. 그래서 위에 코드에서도 자주 활용했다.

  2. 재언이지만, Datetime 자료형은 생각보다 더 똑똑하다. 위에서 말한대로 0이나 -1을 넣으면 알아서 이전 달로 넘어가기도 하며 month +-1 등 기본적으로 이렇게 하면 되지 않을까 싶은 연산들은 대부분 지원한다.

  3. 변하지 않는 const 위젯들은 다른 파일을 새로 파서 거기 몰아두면 아주 행복하다! 당장 뭐 하나를 찾으려고 기나긴 스크롤을 넘기지 않아도 되어서 아주 만족스럽고, 코드 자체의 구조도 깔끔해진 것 같아서 좋다.