Flutter로 앱만들기(3일 과정) 경험 기록 - 공부~2일차

0일차 - 공부

사실 3일 만에 만든 건 어느정도 공부가 끝난 이후이다. 1월 11일부터 시작하여 2월 22일에 검토까지 갔으니 42일만이다. 게임도 냈겠다, Flutter가 핫하다는 소식을 듣자마자 앱도 출시하기위해 바로 공부해봤다.

Do it! 플러터 앱 프로그래밍

마침 밀리의 서재에 'Do it!플러터 앱 프로그래밍' 책이 있어서 바로 다운받아 읽어봤다.
당연히 끝까지 안 봤다. 둘째 마당정도까지 읽으면 플러터의 구조는 대충 파악되기 때문에 끝까지 볼 필요 없다. 뒷쪽은 차례를 통해 '아 이런 기능까지도 구현할 수 있구나' 하고 넘어갔다.
해당 책이 2.x 버전을 기반으로 쓰여있기 때문에 글을 쓰고 있는 현재의 버전인 3.7버전으로 다시 배우기 위해 공식 홈페이지로 넘어갔다.

공식 홈페이지

공식 홈페이지는 역시 배우기 좋게 Itroduction 과정을 포함하고 있었다. 무거운 Android Studio를 설치하지 않고 내가 애용하는 VSC로도 빌드까지 할 수 있게 알려줘서 좋았다.
다 읽고 보니 한글판 안내페이지도 있는 걸 알았지만 1.9버전을 최신으로 가르키고 있으니 주의해야 한다.

1일차 - 기획과 간단 제작

기획

Notion을 통해 개요와 기능을 짰다.

플러터 개발 기획
Notion으로 플러터 앱 개발 기획

Figma를 이용해서 개략적인 화면 구성도 짰다. 화면구성이 있어서 Flutter로 코딩할 떄 이것저것 만지느라고 시간을 허비하지 않을 수 있었던 것 같다.
Figma로 Flutter 앱 UI 제작
Figma로 앱 기획

마지막으로 기능 명세. 최대한 간단하게 해서 기능 자꾸 추가한다고 시간낭비하지 않도록 했다. 
목표는 어디까지나 최소 기능으로 '출시'이다.

결과물

Value Calculator Scaffold

정렬도 하나 없이, Widget으로 포함관계만 만들어서 위 형태를 제작했다.

처음에는 Button의 나열로 기간을 선택하게 하려고 했는데 검색 도중 Toggle Button이라는 게 있어서 적용시켰다.
이 링크의 코드를 참고하였다.
위젯 카탈로그.  특히 레이아웃 카탈로그가 처음에 디자인 짜는데 많이 도움이 됐다.

조금 신경쓴 부분이 있다면, Income, Term, InterestRate, Service Valuation 모두 제목이 있고, 컨텐츠가 있다는 공통점이 있는데, 이를 블럭처럼 한 위젯으로 묶어서 디자인을 관리하려고 했다는 것이다.


1
2
3
4
5
6
7
8
child: Column(
          children: [
            InputContainer(title:"Income", contents: InputIncome()),
            InputContainer(title:"Term", contents: SelectTerm()),
            InputContainer(title:"Interest Rate", contents: InputRate()),
            InputContainer(title:"Service Valuation", contents: Result()),
          ]
        ),
cs

위와 같이 InputContainer 안에 title과 안에 들어갈 contents 위젯을 받아서 배치한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Widget build(BuildContext context) {
    return Container(
      padding:const EdgeInsets.all(5.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Padding(
            padding: const EdgeInsets.all(5.0),
            child: Text(
              title,
              style: Theme.of(context).textTheme.labelLarge
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(10.0),
            child: Center(child: contents), //이부분
          )
        ]
      ),
    );
  }

cs

2일차 - 디자인 수정 및 기획 손보기

사실 출시까지 2일차에 하려다가 많은 난관에 부딪혀서 여기까지만 했다.
디자인 수정이라고 해봤자 몇 개 한 게 없다.

디자인 수정

VSC의 오른쪽 버튼, Refactor - Wrap with.. 기능을 통해 위젯들을 많이 감싸줬다. Padding 과 Container의 차이를 잘 모르지만 일단 넣어줬다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Widget build(BuildContext context) {
    return MaterialApp(
      title:_title,
      theme: ThemeData(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.red),
        textTheme: TextTheme(
          labelLarge: TextStyle(fontSize:20.0, fontWeight:FontWeight.bold),
          displayLarge: TextStyle(fontSize:20.0, fontWeight:FontWeight.bold)
        )
      ),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Value Calculator'),
        ),
        body: MainPage()
      ),
      debugShowCheckedModeBanner: false,
    );
  }
cs

Tutorial 에서 배운대로 MaterialApp에 theme,  colorScheme, textTheme도 넣어줬다. Tutorial처럼 멋진 색깔들이 나오지는 않았다. fontSize를 조절할 수 있는 거에 만족했다.

Theme을 Provider처럼 Theme.of(context)를 통해 접근할 수 있는 것이 흥미로웠다.

1
Theme.of(context).textTheme.displayLarge,
cs

기능추가

state관리

가장 중요한 기능에서, setState로는 각 Container간의 state를 연동하기가 영 불편했다. 공식 홈페이지에서 소개된 State관리의 여러 방법중에 Provider가 가장 만만해보여서 사용해보았다.

요약하자면 ChangeNotifier를 상속하는 Model을 만들고(이 모델 안에서 state 관련 get, set 메소드들도 만든다.), 그 모델을 Generic으로 하는 ChangeNotifierProvider로 State를 공유할 Widget Tree를 감싸고, 그 Widget Tree안에 있는 widget은 어느 곳에서든  Consumer widget, Provider.of (package문서에는 context.watch, context.read, context.select도 소개됨)를 통해 접근할 수 있다.

TextField

textField에 숫자(소숫점 포함)만 들어올 수 있게 처리, 클릭 시 지워지게 처리, keyboard클릭시 숫자만 나오게 처리 등은 ChatGPT의 도움을 받았다.

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
class InputIncome extends StatelessWidget {
  InputIncome({super.key});
 
  final TextEditingController _controller = TextEditingController();
 
  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Text("\$"),
        Flexible(
          child: TextField(
            controller: _controller,
            style: Theme.of(context).textTheme.displayLarge,
            keyboardType: TextInputType.number,
            inputFormatters: <TextInputFormatter>[
              FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,2}'))
            ],
            decoration: InputDecoration(
              labelText: 'Input',
            ),
            onTap : () {
              _controller.text = "";
            },
            onSubmitted: (text) {
              Provider.of<InputModel>(context, listen:false).income = text.isNotEmpty ? double.parse(text) : 0.0;
            },
          ),
        )
      ],
    );
  }
}
cs

에러수정

 A RenderFlex overflowed by * pixels 관련 에러가 나서 검색을 통하여 해결하였다.
키보드를 누르면 화면이 가려져서 화면에 overflow되는 에러였는데, 간단하게 scroll이 가능하게 해주는 SingleChildScrollView() 위젯을 넣어서 해결하였다.
키보드를 누르면 입력화면이 가리는 문제도 해결되어서 일석이조였다.

출시

기능을 다 만들어서 다음날에는 무슨일이 있어도 플레이 스토어에 올려야지! 하고 다음날을 기약했다. (출시를 하는 도중에 자꾸 에러가 생겨 강제로 미뤄졌다.)

3일차는 다음 글에서 계속..

댓글

이 블로그의 인기 게시물

Box Tengo. Private Policy (iOS)

Value Calculator Privacy Policy

Unity meta파일 필요성과, VSC에서 숨기기