Flutter 강좌 - 플러터 프로젝트 생성, 기본 앱과 구조 알아보기
올해 4월쯤에 플러터 강좌를 처음으로 올렸었다. 그 이후론 바빠서 flutter 강좌를 쳐다보지 못하고 있다가 방학을 맞아 다시 진행해보려 한다. 이번 글에선 플러터 프로젝트를 생성해보고 플러터의 기본 앱과 구조에 대해 알아보겠다.
플러터 프로젝트 생성하기
안드로이드 스튜디오를 실행하고 Start a new Flutter project를 클릭해 새 플러터 프로젝트를 생성하자. 그럼 위와 같은 창이 나온다. 좌측의 Flutter Application을 선택하고 Next 버튼을 클릭하자.
Project name에는 프로젝트명을, Flutter SDK path에는 플러터 sdk 경로를, Project location에는 프로젝트를 생성할 경로를, Description에는 프로젝트 설명을 입력하면 된다. 만약 C:\flutter를 프로젝트 경로로 설정했으면 C:\flutter\flutter_class(프로젝트명)에 프로젝트가 구성된다.
Package name에 패키지명을 입력하자. 이 패키지명은 기기와 구글 플레이스토어에서 각 앱을 고유하게 식별할 수 있도록 도와준다. 자세한 건 안드로이드 개발자 사이트를 참고하자. Finish 버튼을 클릭하면 생성된 프로젝트가 열린다.
플러터 기본 앱, 프로젝트 구조
프로젝트 구조
우선 .idea 폴더는 프로젝트의 관한 설정들이 저장되는 곳이다. IDE가 자동으로 관리하니 직접 건드릴 일은 없다. android, ios 폴더엔 각각 안드로이드 프로젝트와 ios 프로젝트가 있다. 여기서 네이티브적인 기능을 따로 처리하거나 설정할 수 있다(ex. AndroidManifest.xml, build.gradle).
lib 폴더에 dart 파일을 작성해 플러터 앱을 만들 수 있다. 우리는 주로 lib 폴더에서 개발할 것이다.
pubspec.yaml
name: flutter_class
description: A new Flutter application.
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: ">=2.7.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^0.1.3
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
asset 폴더와 폰트를 사용하려면 여기서 따로 설정을 해야 한다. 버전을 지정하는 방법과 외부 패키지를 사용하는 방법, pubspec 파일에 관한 자세한 내용은 dart 홈페이지과 flutter 홈페이지를 참고하자.
pubspec.yaml에는 프로젝트명, 설명 등 프로젝트 정보들이 저장되있다. 버전의 +뒤에 있는 것은 빌드 번호이다. dependencies: 밑에 cupertino_icons: ^0.1.3과 같이 패키지명: 버전을 입력해 pub.dev에 올라온 패키지를 사용할 수 있다.
pub.dev 사이트에서 원하는 패키지를 찾은 뒤 Installing 탭을 클릭하면 dependencies: 밑에 어떻게 입력해야 하는지 나온다.
기본 앱 분석
이제 앱을 실행해보겠다. 에뮬레이터를 실행하거나 직접 휴대폰을 연결한 후 우측의 ▶버튼을 클릭해 기본 앱을 실행해보자.
[Android Studio] 안드로이드 스튜디오 에뮬레이터 설치 및 사용 방법
에뮬레이터를 설치하고 실행하는 법은 위를 참고해봐라.
그러면 위와 같이 기본 프로젝트가 실행된다. Flutter Demo Home Page란 앱바가 있고 중앙엔 You have pushed the button this many times:라 써져있다. 우측 하단의 플로팅 버튼을 클릭하면 중앙에 숫자가 올라간다.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
우선 앱이 시작되며 main 함수가 호출된다. runApp 메소드는 주어진 위젯을 위젯트리의 루트로 만든다. 그리고 플러터 프레임워크는 루트 위젯이 화면을 덮도록 한다. main 함수와 runApp 메소드 둘을 비슷하게 봐도 된다.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
MyApp 클래스는 StatelessWidget를 상속하고 있으며 MyHomePage는 StatefulWidget를 상속하고 있다. StatelessWidget는 상태를 변경할 수 없으며 StatefulWidget는 상태를 변경할 수 있다. 이와 관한 건 다른 글을 통해 더 자세히 다루겠다.
build 메소드는 반환하는 위젯을 그려주는 역할을 한다. MaterialApp은 머티리얼 디자인을 위한 몇 가지 위젯을 제공한다.
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
MyHomePage는 StatefulWidget를 상속하고 있으며 _MyHomePageState는 State를 상속하고 있다. 이는 성능 때문에 분할되었으며 자세한 건 넘어가자. 코드를 보면 Scaffold 위젯을 통해 앱바와 플로팅 버튼을 나타내고 있다. Column 위젯을 통해 Text 두 개를 세로로 나열하고 있다. Column은 안드로이드에서 Linearlayout와 비슷한 느낌이다.