[flutter] 플러터로 첫번째 앱 만들기
kr·@wonsama·
0.000 HBD[flutter] 플러터로 첫번째 앱 만들기
 > 출처 : https://codelabs.developers.google.com/codelabs/first-flutter-app-pt1/#0 # 1. 소개 * 플러터는 iOS 및 Android 둘다 개발 할 수 있는 구글의 모바일 SDK 입니다. * 플러터는 무료며 오픈소스 입니다. * 객체 지향 코드 및 기본 프로그래밍에 대해 알고 있다면 좀 더 손쉽게 접근 할 수 잇습니다. * 다트 또는 모바일 프로그래밍에 대한 경험이 없어도 시작하실 수 있습니다. ## 1.1. part 1 에서 배우는 것들 * 플러터 앱을 iOS / Android 처럼 보이게 작성하는 방법 * 플러터 앱의 기본 구조 * 기능 확장을 위한 패키지 검색 및 사용방법 * 핫 리로드를 활용하여 개발 주기를 단축하는 방법 * 스테이트풀 위젯 구현하기 * 데이터가 느리게 로딩되는 무한 목록 만드는 방법 ## 1.2. 만드려는 플러터 앱의 목표 * 무한 스크롤 * 스크롤 시 하위 데이터가 로딩이 된 이후 목록을 표시해 주도록 함 ## 1.3. 개발환경 설정 > 개발 환경이 구성되지 않은 경우, 링크를 클릭하여 관련 정보를 확인 바랍니다. * [Flutter SDK](https://flutter.io/get-started/install/) 설치 * [Editor](https://flutter.io/get-started/editor/) 설치 * 디바이스 ( [Android](https://flutter.io/setup-macos/#set-up-your-android-device), [iOS](https://flutter.io/setup-macos/#deploy-to-ios-devices) ) 준비하기 * [iOS](https://flutter.io/setup-macos/#set-up-the-ios-simulator) 시뮬레이터 준비하기 ( XCode 설치를 필요로 합니다 ) * [Android](https://flutter.io/setup-macos/#set-up-the-android-emulator) 시뮬레이터 준비하기 ( Android Studio 설치를 필요로 합니다 ) ## 1.4. 플러터 앱 만들기 > IDE 에서 "New Flutter Project" 메뉴가 보이지 않는 경우 [plugin](https://flutter.io/get-started/editor/#androidstudio) 을 설치 하시기 바랍니다. * vscode 에서는 플러그인 설치 후 shift + cmd + p 버튼을 누른 이후 타이핑 하면 `New Flutter Project` 를 확인 할 수 있습니다. * AndroidStudio 에서는 플러그인을 설치 후 메뉴가 생성됨 #### 기본 소스코드 (Hello world) > 아래 코드를 변형하면서 작업을 진행할 예정 입니다. #### TIP : 코드 정리하기 > 개발하다 보면 글의 간격이(줄맞춤) 어긋나서 코드의 가독성이 떨어지는데, 이때 Android Studio/IntelliJ IDEA 에서는 우클릭 후 `Reformat Code with dartfmt`, vscode 에서는 우클릭 후 `Format Document`, terminal 에서는 `flutter format <filename>` 을 입력하면 코드가 정렬되는 것을 확인할 수 있습니다. ## 1.5. 기본 소스 ```dart import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Welcome to Flutter', home: Scaffold( appBar: AppBar( title: const Text('Welcome to Flutter'), ), body: const Center( child: const Text('Hello World'), ), ), ); } ```  ## 1.6. 살펴보기 * [Material](https://material.io/guidelines/)은 모바일 웹 표준 디자인 언어 입니다. 플러터는 다양한 Material 위젯을 제공 합니다. * `=>` 화살표 함수를 활용하여 코드를 간결하게 줄일 수 있습니다. * 플러터에서 대부분의 위젯은 정렬, 패딩, 레이아웃 속성을 포함하고 있습니다. * 머티리얼 라이브러리의 `Scaffold` 위젯은 기본 앱 바, 제목 및 홈 스크린의 위젯 트리를 포함하는 본문 속성을 제공합니다. 위젯 하위 트리는 상당히 복잡 할 수 있습니다. (우선적으로 필요 속성부터 배워나가면 좋아요) * 위젯의 주된 임무는 다른 하위 레벨 위젯의 관점에서, 위젯을 표시하는 방법을 설명하는 `build` 메소드를 제공하는 것입니다. # 2. 외부 패키지 사용하기 * [english_words](https://pub.dartlang.org/packages/english_words) 라는 영단어를 제공해주는 무료 오픈소스 패키지를 사용해 보겠습니다. * [Pub Site](https://pub.dartlang.org/flutter/) 에 접속해서 다양한 flutter 기반 패키지를 검색 할 수 있습니다. ## 2.1. 패키지 정보를 추가 > `pubspec.yaml` 파일을 열어 패키지 정보를 추가 합니다. ```yaml dependencies: flutter: sdk: flutter cupertino_icons: ^0.1.2 english_words: ^3.1.0 # 이 라인을 추가하면 됩니다. ``` ## 2.2. 의존성 다운로드 > `packages get` 명령을 통해 추가된 의존성(dependencies) 정보를 다운로드 합니다. ```sh flutter packages get Running "flutter packages get" in startup_namer... Process finished with exit code 0 ``` ## 2.3. 소스 import 추가 > 소스(`lib/main.dart`) 상단에 패키지 정보를 추가 합니다. ```dart import 'package:flutter/material.dart'; import 'package:english_words/english_words.dart'; // 이 라인 추가 ``` ## 2.4. 최종 소스 > 실행 할 때 마다 (hot deploy) 중앙의 단어가 변경되는 것을 확인할 수 있습니다 ```dart import 'package:flutter/material.dart'; import 'package:english_words/english_words.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { final wordPair = WordPair.random(); // Add this line. return MaterialApp( title: 'Welcome to Flutter', home: Scaffold( appBar: AppBar( title: Text('Welcome to Flutter'), ), body: Center( //child: Text('Hello World'), // Replace this text... child: Text(wordPair.asPascalCase), // With this text. ), ), ); } } ```  # 3. StatefulWidget(상태 변화 있는 위젯) 추가하기 * `StatelessWidget` 은 변경할 수 없으므로 해당 속성을 변경할 수 없습니다. * `StatefulWidget` 은 위젯의 수명 기간 동안 변경 될 수 있는 상태를 유지 합니다. * 이번엔 Stateful 클래스 인 RandomWordsState를 생성 및 활용해 보도록 하겠습니다. * State 만들기 => StatefulWidget 만들기 ## 3.1. 상태 클래스 최소 버전 > `State<RandomWords>` RandomWords 클래스에서 사용하는 상태(State)를 구현한 클래스 입니다. ```dart class RandomWordsState extends State<RandomWords> { // TODO Add build method } ``` ## 3.2. StatefulWidget 만들기 > `StatefulWidget` 위젯을 상속받아 클래스를 만들고, 상태변화를 담당할 클래스(`RandomWordsState`)를 지정 합니다. ```dart class RandomWords extends StatefulWidget { @override RandomWordsState createState() => RandomWordsState(); } ``` ## 3.3. 상태 구현 > `RandomWords` 클래스에서 상태가 변화하면 `build` 구문을 자동적으로 수행 ```dart class RandomWordsState extends State<RandomWords> { @override // Add from this line ... Widget build(BuildContext context) { final WordPair wordPair = WordPair.random(); return Text(wordPair.asPascalCase); } // ... to this line. } ``` ## 3.4. 최종 소스 > 이전과 비교해 보면 main에서 랜덤 단어를 생성한 것을 `StatefulWidget` RandomWords 에 위임하여 값을 생성 및 관리하도록 하여, 좀더 유연한(확장하기 쉬운) 소스로 되었습니다. ```dart import 'package:flutter/material.dart'; import 'package:english_words/english_words.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Welcome to Flutter', home: Scaffold( appBar: AppBar( title: Text('Welcome to Flutter'), ), body: Center( child: RandomWords(), ), ), ); } } class RandomWordsState extends State<RandomWords>{ @override Widget build(BuildContext context) { final WordPair wordPair = WordPair.random(); return Text(wordPair.asPascalCase); } } class RandomWords extends StatefulWidget { @override RandomWordsState createState() => RandomWordsState(); } ``` # 4. 무한 스크롤 만들기 > ListView 의 factory build 생성자를 사용하여, 스크롤 할 때 목록을 느리게 빌드 할 수 있습니다. (필요 시 목록을 동적으로 생성하므로 효율적) * [참조] `_` (언더스코어) 로 시작하면 dart 언어에서는 private 으로 인식하게 됩니다. ## 4.1. 값 추가 > 단어 목록 정보와, 텍스트 스타일을 추가 합니다 ```dart class RandomWordsState extends State<RandomWords> { // 아래 두 라인 추가 final List<WordPair> _suggestions = <WordPair>[]; final TextStyle _biggerFont = const TextStyle(fontSize: 18); ... } ``` ## 4.2. 목록(ListView.builder) 만들기 ```dart Widget _buildSuggestions() { return ListView.builder( padding: const EdgeInsets.all(16), // itemBuilder는 대상 아이템을 만들어주는 역할을 합니다. // 인덱스가 홀수냐 짝수냐에 따라 대상 항목(ListTile)을 보여주거나 구분선(Divider)을 보여줍니다. itemBuilder: (BuildContext _context, int i) { // 인덱스가 홀수인 경우 구분선을 보여준다 if (i.isOdd) { return Divider(); } final int index = i ~/ 2; // 나누기를 하면 double 임 하지만 ~/ 연산자를 통해 int로 바꿔준다 == (a / b).truncate().toInt() 와 동일한 값 임. // i : itemBuilder 내부에 들어가는 실제 항목의 index 값 // index : i를 2로 나눈 값 ( 홀수 라인에 구분선이 들어가기 때문 ) // 목록이 증가한 경우에만 값을 더 추가하는 엑션을 처리, 이미 추가한 값은 더이상 추가하지 않음 // print('$i ::: $index'); if (index >= _suggestions.length) { // index 값이 _suggestions 길이보다 커지는 경우 _suggestions.addAll(generateWordPairs() .take(10)); // 단어 목록에서 10개를 _suggestions 목록 정보에 추가한다 } // item 인덱스(i)가 짝수인 경우에는 해당 _suggestions 인덱스(index)에 값을 보여주도록 한다 return _buildRow(_suggestions[index]); }); } ``` ## 4.3. Row (ListTile) 만들기 ```dart Widget _buildRow(WordPair pair) { return ListTile( title: Text( pair.asPascalCase, style: _biggerFont, ), ); } ```  # 5. 기타 참조 * [vs code 소개 동영상](https://code.visualstudio.com/docs/getstarted/introvideos) # 맺음말 * 4개로 나눠서 하려다가 그냥 한개로 만들었네요 * 실제 하나하나 따라가면서 학습하면서 하니 시간이 좀 걸리네요 후... * 궁금한 것은 언제든지 댓글 문의 바랍니다.
👍 lumix, votes4minnows, trenz, jadabug, accelerator, ryanhkr, seapy, actomy, uritor, ownitye, gigabodze, oxcal, reayiti, ditluce, egrisp, onsoct, silushu, edredid, cedsitor, dorit7, hasenga, uwalyis, teantofri, peert, amsisis, ilyin, erisederi, amuchon, areror, liemus, ucielyets, sanan, usomugarn, owhen, ocofe, athingily, itincar, ratororer, yoowasees, atotishis, evasashor, dofra, hitalyet, yashed, ucomo, hieteasin, kofes, erdug, ectong, lisong, umilo, ammumpi, tendofile, fospa, adoteyr, dondes, rathuro, ijangonam, doutof, ithing, ofrish, itaryear, drunktrader, sotatav, ipote, suralliti, esito, etada, kabilka, esisc, nathinoff, ezzathash, apowidon, orofore, uculadro, zugs, pinteront, enenede, ederer, nuritedom, ilaprard, rirti, ilfai, itioro, asofiseri, patal, atrainsi, ustaris, sidofl, odseryo, dealides, etadgitu, eheldu, dianna1, noreference, supu, indonger, hototon, ritont, terest, steffenix, xyzashu, mehta, ahruprof, anpigon, vaansteam, songbj, donekim, deer3, skysung, minigame, stylegold, jinuking, y-o-u-t-h-m-e, hodolbak, jjangjjanggirl, skymin, fur2002ks, cyan2017, newbijohn, busy.pay, noisysky, urobotics, xxnoaxx, j-car, smtester, thecards, krnews, marvel.spiderman, wcasino, steemit.jackpot, smcard, guro, yongsan, incheon, mapo, monstersteem, roadmap, lotto645, girlfriends, smilezone, ragingimpaler, spiritforest, goblincaptain, smseller, wonsama, wbot01, wdev, doctor.strange, dead.pool, black.widow, marvel.hulk, marvel.ironman, black.pan.ther, claim7, wcasino.pay, wcasino.holdem, wcasino.jackpot, steemit.holdem, smonsmon, shindorim, shingil, checkname, starterpack, gdragon, sumimasen, showdown, freesale, freefee, testsama, kimch, tongdak, hanbok, jjangjjangman, superguard, yawang, kpay, adultbaby, sneack, gzone, ppororo, alphamonsters, betamonsters, fastway, smonsang, technomart, lastsmon, postme, bearbaby, o0o0o, developments, originals, beanpole, oilbank, iliili, kotlin, flutters, prettyguy, gamemonsters, blueguy, sicbo, yaoi, farmfarm, giantroc, koboldminer, crustaceanking, waterelemental, goblinsorcerer, animatedcorpse, serpentflame, lyannaforest, divineknight, feralwarrior, elementalair, jestertwisted, bansheescreaming, skyselenia, darknesslord, lightangel, naturalyanna, astormbringer, giantfrost, warriorminotaur, golemalric, orcelemental, spiritpriest, lordjester, magifirestorm, muhan, kibumh, honeybeerbear, jerdep, yoon, aaronhong, kwonjs77, lucky2, virus707, happyberrysboy, ssc-token, hyokhyok, ayogom, ioioioioi, bbooaae, spicetrader, skan, bystyx,