* 플러터를 원문 공식 문서 (https://flutter.dev/docs)를 읽으면서 공부하는 과정에서 정리하고자 작성하는 포스팅으로
저는 플러터 실무 개발 경력이 없는 대학생인 점을 미리 밝힙니다.
* 추가로 오준석의 플러터 생존코딩 및 제 개인 경험을 참고하여 작성하고 있습니다.
* 오역, 오탈자, 잘못된 내용의 지적은 항상 감사히 받겠습니다 :)
이 포스팅 역시, 아래 게시글의 내용을 기본으로 합니다.
https://codelabs.developers.google.com/codelabs/first-flutter-app-pt2#5
한국어로 되어있는 문서이지만, 여러가지 자세한 설명을
https://dart.dev/guides/language
https://api.flutter.dev/index.html
https://flutter.dev/docs/cookbook/navigation/navigation-basics
이 세 사이트를 참고하여 보충 서술하였습니다.
지난 포스팅에서 리스트뷰에 하트 아이콘을 추가해 우리가 원하는 이름을 셋(Set)에 저장했습니다.
이제 우리가 셋에 저장했던 이름들을 따로 모아서 볼 수 있는 페이지를 만들겠습니다.
플러터에서 새로운 화면을 만들 때는 마치 웹사이트의 주소가 변하는 것과 비슷하게
'경로(route)'로 관리합니다.
실제로는 다르겠지만 이해하기 쉽게 본다면, 네이버의 메인 페이지에서
(www.naver.com)
뉴스를 보려고 뉴스 페이지로 이동하면 경로는 다음과 같이 될 것이고
(www.naver.com/news)
웹툰을 보려고 웹툰 페이지로 이동하면 경로는 다음과 같이 될 수 있습니다.
(www.naver.com/webtoon)
이와 유사하게 플러터는 메인화면을 기준으로 삼아 라우트를 사용하여 화면을 이동합니다.
(웹과 굉장히 비슷한데, 사실 플러터는 웹개발에도 사용할 수 있습니다.)
따라서 '하나의 경로'는 '하나의 화면'을 나타냅니다.
마치 www.naver.com/webtoon 이라는 경로가 네이버 웹툰 메인화면을 나타내듯이요.
플러터에서 라우트를 왔다갔다 하기 위해서는 Navigator를 사용해야합니다.
경로를 찾아주는 네비게이션인셈이죠.
기본적으로 화면을 바꾸는 방법은 다음과 같습니다.
1. 라우트를 2개 만듭니다. (= 화면을 2개 만듭니다)
2. Navigator에 라우트를 push 합니다. (= 화면을 이동합니다.)
3. Navigator에 있는 라우트를 pop 합니다. (= 이전 화면으로 돌아갑니다.)
이제 이 방법을 이용해 저장한 이름을 모아보는 화면으로 이동하는 기능을 만들어 보겠습니다.
우선 새로운 창으로 이동할 버튼부터 만들겠습니다.
실제 앱에서 자주 볼 수 있는 버튼 중에 상단 앱바의 좌측/우측 끝에 있는 버튼이 있습니다.
이 버튼을 이용하여 저장한 이름을 볼 수 있는 창으로 이동해보겠습니다.
앱바와 관련된 기능은 Scaffold() 위젯의 appBar 속성에 있는 AppBar() 위젯에서 관리합니다.
다음과 같이 작성합니다.
title 속성 밑에 actions: 속성을 추가했습니다.
앱바에 버튼을 추가할 때는 actions 속성에 추가할 버튼 위젯들을 넣어주면 됩니다.
actions 속성은 기본으로 위젯 리스트 [ ] 로 되어있어 여러 위젯들을 넣을 수 있습니다.
추가할 버튼은 IconButton 입니다. 말 그대로 아이콘 모양의 버튼입니다.
onPressed 속성 : 이 버튼을 클릭했을 때 호출할 콜백함수를 적어줍니다. 역시 'void funtion' 자료형입니다.
icon 속성 : 버튼에 넣을 Icon() 위젯을 넣어주면 됩니다.
우리는 onPressed 속성에 함수 이름으로 대신하여 넣었습니다.
(dart에서는 함수도 값으로 취급할 수 있어 함수를 변수에 담을 수 있습니다.)
이제 이 이름을 가진 함수를 만들어보겠습니다.
이 함수는 새로운 창을 만드는 기능을 하는 함수가 될 것입니다.
build() 메소드 위에 _pushSaved() 함수를 선언합니다.
build메소드에서 _pushSaved() 함수를 이미 호출하고 있기 때문에,
함수를 호출하기 전에 함수는 선언되어 있어야 합니다.
이때 헷갈리기 좋은 것이 저렇게 onPressed() 속성에 함수 이름으로 '함수값'을 넘기면
그 함수는 void를 리턴하는 함수이므로 '함수값'의 자료형은 'void function' 이 됩니다.
하지만 onPressed() 속성에 직접 함수를 구현하여 넣으면 그 함수는 void를 반환하므로
onPressed속성에 void자료형을 넣은 것과 같아 오류가 발생합니다.
이 경우 지난 포스팅에서 본대로 ( ) { } 구조로 익명함수를 이용해 감싸면 됩니다.
함수의 내용을 구체화하기 전에, 이 상태로 앱을 갱신해보겠습니다.
우측 상단을 보면 리스트 아이콘이 추가되어 있음을 볼 수 있습니다.
아직은 onPressed 속성에 넣은 함수가 아무런 기능을 하지 않기 떄문에 버튼도 아무런 동작을 하지 않습니다.
함수에 기능을 추가해보겠습니다.
아까 플러터에서 앱의 화면이동은 '경로'로 관리되어진다고 했고, 이 관리자 역할을 Navigator가 한다고 했습니다.
즉 새로운 창을 열기 위해서는 Navigator를 이용해야 합니다.
다음과 같이 코드를 작성합니다.
정말 긴 코드지만 잘라서 하나하나씩 보겠습니다.
1. Navigator.of(context).push()
기본적으로는 Navigator.push( ) 메소드를 이용해 Navigator가 관리하는 라우트 스택에 새로운 라우트를 push합니다.
이때 보통은 기존에 존재하는 Navigator를 가져다 쓰는 것이 일반적입니다.
앱이 실행되면 보이는 가장 첫번째 위젯은 MaterialApp( ) 입니다. 기억하시나요?
그리고 이 MeterialApp() 위젯의 속성으로 home 속성이 있습니다.
MaterialApp() 위젯은 이 home 속성에 들어온 위젯을 루트라우트로서 Navigator스택에 넣습니다.
즉, 라우트를 관리하는 Navigator가 MaterialApp()에 이미 존재하는 것이죠.
이 Navigator를 가져오기 위해 사용하는 방법이 바로 Navigator.of(context) 입니다.
Navigator.of(context) 는 현재 위젯(context)에서 가장 가까운 Navigator를 의미하는데,
이를 정확히 이해하려면 플러터의 위젯트리 개념을 이해해야 합니다.
일단 저도 MaterialApp()의 Navigator를 가져오는구나 정도로 이해하고 넘어갔습니다.
2. MaterialPageRoute(builder: ~~);
이제 이 Navigator로 새로운 화면(라우트)을 push하면 되는데,
이때 넣을 새로운 화면(route)은 직접 만들 수도 있지만 이 코드에서는 MaterialPageRoute 클래스를 사용했습니다.
이 클래스는 애니메이션과 함께 화면을 바꿔주는 라우트 클래스입니다.
이때 이 클래스의 builder 속성을 설정해야합니다.
builder 속성에는 함수가 들어가는데, 이 함수는 Widget을 반환하고, BuildContext를 인자로 가지는 함수입니다.
전에 void function과 마찬가지로, 직접 위 정의 맞는 함수를 넣으면 안됩니다.
그 함수는 Widget을 반환하지, "Widget을 반환하는 함수"를 반환하지 않기 때문입니다.
따라서 위 코드와 같이 익명함수 형태로 (BuildContext context) { return Widget(); } 의 꼴에 맞춰 써야합니다.
그리고 이 함수가 반환한 위젯이 곧 두번째 화면(라우트)이 됩니다.
그래서 안드로이드 기본 화면을 보여주기 위해 Scaffold() 위젯을 리턴한 것입니다.
(코드 중간에 return 키워드가 많아서 헷갈리지만, 결국 정말 return 하는 것은 Scaffold() 위젯입니다.)
3. ListView, ListView.dividedTiles
이 Scaffold() 위젯의 body에는 ListView가 들어갑니다.
즉 이 화면에는 우리가 저장한 이름들로 만들어진 ListView 위젯이 있습니다.
이 ListView 위젯의 내용물을 구성하기 위해 divided 변수를 사용했습니다.
divided 변수는 ListTile.dividedTiles() 객체입니다.
이 객체는 말 그대로, 각 리스트 타일 사이사이에 구분선이 들어가있는 ListTile의 모음입니다.
하지만 이 클래스의 속성을 보니 tiles 를 따로 받고 있습니다.
즉, tiles로 받은 tiles 사이에 구분선을 추가하여 새로운 ListTIle 모음을 만드는 클래스로 볼 수 있겠네요.
이 ListTile모음은 ListView 위젯의 children 속성에 넣는 List 타입이 아닙니다.
따라서 이 ListTile 모음을 마지막에 toList() 메소드를 이용해 List타입으로 바꿔주고 있습니다.
한편, 아까 말했던 tiles 속성에 tiles 라는 이름의 객체 변수를 넘겨주고 있습니다.
이 tiles 객체 변수에는 우리가 선택한 이름을 저장하는 _saved 리스트에 .map() 메소드를 실행한 결과가 담겨있습니다.
.map() 리스트는 _saved리스트 각각의 요소에 map() 함수로 넘겨준 함수를 실행시킨 결과 모음집입니다.
예를 들어 [1, 2, 3, 4].map( (i) { return i+1; } ); 라고 하면
각각의 요소에 1을 더해 [2, 3, 4, 5] 모음집을 반환합니다. (리스트는 아닙니다.)
다행히도 ListTile.dividedTiles() 객체의 tiles 속성은 이 모음집을 그대로 담을 수 있는 타입이라
따로 list로 변환하지 않아도 이 결과물을 그대로 담을 수 있었습니다.
(이 모음집을 Iterable 타입이라고 합니다.)
설명이 정말 많았지만, 결국 구현한 기능은 하나입니다.
우리가 하트를 눌러 저장했던 목록을 보여주는 새 화면을 만들고, 그 화면으로 이동한다.
이렇게 새 화면으로 이동하는 기능은 앱 개발시 정말 많이 사용될 것이기에,
앞으로도 포스팅하면서 충분히 설명할 기회가 많을 것이라고 생각합니다.
이렇게 플러터로 첫번째 앱을 만드는 공식 튜토리얼을 종료하겠습니다.
다음 포스팅부터는 아마 플러터의 주요 위젯들에 대한 설명과 그 예제들에 대한 설명을 포스팅할 것 같습니다.
'Android > Flutter' 카테고리의 다른 글
[Flutter] 3. TextField 위젯 - 폼(Form) 입력값 유효성 검사하기 (2) | 2021.07.08 |
---|---|
[Flutter] 2. 플러터의 위젯 & MaterialApp, Scaffold, AppBar 위젯 (0) | 2021.07.05 |
[Flutter] 1. 플러터로 만드는 첫 번째 앱 (3) - ListTile에 아이콘을 추가하여 이름 저장하기 (0) | 2021.07.02 |
[Flutter] 1. 플러터로 만드는 첫 번째 앱 (2) - StatefulWidget과 ListView (0) | 2021.07.01 |
[Flutter] 1. 플러터로 만드는 첫 번째 앱 (1) - 플러터 앱의 구조와 외부 라이브러리 사용 (0) | 2021.06.30 |