[flutter] 플러터로 첫번째 앱 만들기

View this thread on: d.buzz | hive.blog | peakd.com | ecency.com
·@wonsama·
0.000 HBD
[flutter] 플러터로 첫번째 앱 만들기
![](https://steemitimages.com/640x0/https://cdn.steemitimages.com/DQmdTnqpPybw54uskKRbEqPhWDaZhiyZuimU9J3feyNuGdZ/%E1%84%89%E1%85%B3%E1%84%90%E1%85%B5%E1%86%B7%E1%84%8B%E1%85%B5%E1%86%BA%E1%84%91%E1%85%AD%E1%84%8C%E1%85%B5_190409.002.jpeg)

> 출처 : 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'),
        ),
      ),
    );
  }
```

![](https://cdn.steemitimages.com/DQmTZuAyRzcCcqJzMnotdcLT3TVJXXcQE39ByijKYAuVFSe/image.png)

## 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.
        ),
      ),
    );
  }
}
```

![](https://cdn.steemitimages.com/DQmQtfo8b3MkxVuSo1wLJLFR5taa2zsNa8LVLCwrt1ycVwZ/image.png)

# 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,
      ),
    );
  }
```

![](https://cdn.steemitimages.com/DQmQ8y7aknff4bLSuTkTReLGRWcoSWVVSM2K6Y2QgdHtdom/image.png)

# 5. 기타 참조

* [vs code 소개 동영상](https://code.visualstudio.com/docs/getstarted/introvideos)

# 맺음말 

* 4개로 나눠서 하려다가 그냥 한개로 만들었네요 
* 실제 하나하나 따라가면서 학습하면서 하니 시간이 좀 걸리네요 후...
* 궁금한 것은 언제든지 댓글 문의 바랍니다.
👍 , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,