24년 4월을 기준으로 작성되었습니다.
1. 앞으로 자주 방문하게 될 웹사이트
1) Dart 공식 사이트 : https://dart.dev/
Dart programming language
Dart is an approachable, portable, and productive language for high-quality apps on any platform.
dart.dev
Dart 언어의 개요, 샘플 프로그램, 튜토리얼, 표준 확장 기능(라이브러리/패키지)의 설명, 개발 도구, 기술 자료,
블로그, 웹 기반 프로그램 실행 환경 (DartPad), 개발 환경 설치 프로그램, 커뮤니티 등의 정보를 제공한다.
2) Flutter 공식 사이트 : https://flutter.dev/
Flutter - Build apps for any screen
Flutter transforms the entire app development process. Build, test, and deploy beautiful mobile, web, desktop, and embedded apps from a single codebase.
flutter.dev
Flutter의 소개, 기술 설명, 개발 환경 설치 프로그램, 커뮤니티 및 이벤트 등의 정보를 제공한다.
3) DartPad : https://dartpad.dev/
DartPad
dartpad.dev
웹 브라우저에서 즉시 Dart와 Flutter 프로그래밍을 시작할 수 있다.
4) Dart Code : https://dartcode.org/
Dart Code - Dart & Flutter support for Visual Studio Code
dartcode.org
VSCode 에서 Dart와 Flutter를 사용할 수 있도록 하는 확장 기능을 개발한 곳
5) pub.dev : https://pub.dev/
The official repository for Dart and Flutter packages.
Pub is the package manager for the Dart programming language, containing reusable libraries & packages for Flutter and general Dart programs.
pub.dev
Dart와 Flutter로 소프트웨어를 개발하는 프로그래머들이 서로 본인들이 만든 소프트웨어를 공유하는 웹 사이트
이 사이트를 활용할 수 있도록 flutter에서도 "flutter pub" 이라는 세부 명령을 제공하고 있다.
pub 명령어 공식 사이트 : https://dart.dev/tools/pub/cmd
dart pub
The command-line interface for pub, a package management tool for Dart.
dart.dev
Pubspec.yaml의 Dependencies 공식 사이트 : https://dart.dev/tools/pub/dependencies
Package dependencies
Add other packages to your app. Specify package locations, version constraints, and more.
dart.dev
pub.dev 의 메인 화면은 이런 식으로 구성되어 있다.
6) Flutter 공식 유튜브 : https://www.youtube.com/flutterdev
Flutter
Welcome to the official Flutter YouTube channel. Subscribe to stay up to date with best practices about the Flutter SDK. See real code examples, and watch engineers from around the world putting Flutter to work!
www.youtube.com
유튜브를 통한 기술 설명을 적극적으로 하기도 하고, Flutter의 기능을 정기적으로 소개하는 콘텐츠도 있다.
7) Medium : https://medium.com/
Medium – Where good ideas find you.
Medium is an open platform where readers find dynamic thinking, and where expert and undiscovered voices can share their writing on any topic.
medium.com
Dart와 Flutter의 공식 발표(릴리즈)가 있을 때마다, 공식 발표의 주요 설명을 하는 글이 이 사이트에서 공개된다.
구독 기반의 유료 서비스이지만, 무료로도 제한된 개수의 기사를 볼 수 있다.
또한 이메일을 등록하면, 주기적으로 관심 있는 기사를 보내주기도 한다.
2. mixin
독립적으로 동작하는 클래스보다는
다른 클래스의 악세서리나 부품처럼 특정 기능만 수행하는 게 좋은 상황이면, mixin을 사용한다.
class Integer {
late int _value;
Integer([int givenValue = 0]) {
_value = givenValue;
}
int get() {
return _value;
}
void set(int givenValue) {
_value = givenValue;
}
// 리턴하는 타입(String)과 함수 이름(asString) 사이에 get 키워드를 사용하여 getter를 정의함
String get asString => "$_value";
// set이라는 키워드를 사용하여 setter를 정의함
set value(int givenValue) => _value = givenValue;
// 연산자 오버로딩을 위해 + 연산자를 오버로딩함
Integer operator +(Integer givenValue) {
return Integer(_value + givenValue.get());
}
}
/*
사실상 클래스인데, 스스로 객체가 되어 사용되기보다는 다른 클래스의 부속품으로 사용되는 클래스!
*/
mixin ActivationFlag {
bool _flag = true;
bool get activated => _flag;
set activated(bool givenFlag) => (_flag = givenFlag);
}
// mixin 을 적용하여 개선
class TimemachineInteger extends Integer with ActivationFlag {
List<int> _timemachine = [];
TimemachineInteger([int givenValue = 0]) {
_value = givenValue;
}
// mixin을 추가함으로써, 사용자의 설정에 의해서 바뀌는 값을 저장하거나 <=> 저장하지 않도록 변경
@override
void set(int givenValue) {
if (activated == true) {
_timemachine.add(_value);
print("set($givenValue) called ==> _timemachine is $_timemachine");
} else {
print("set($givenValue) called ==> current $_value is ignored.");
}
super.set(givenValue);
}
List getOld() {
return _timemachine;
}
@override
String get asString => "$_value, previously $_timemachine";
}
void main() {
// mixin 사용 예제
var tmpTmInteger = TimemachineInteger(0);
tmpTmInteger.set(2);
tmpTmInteger.set(4);
tmpTmInteger.activated = false;
tmpTmInteger.set(6);
tmpTmInteger.activated = true;
tmpTmInteger.set(8);
print("${tmpTmInteger.asString}");
}
※ mixin을 아무 클래스나 적용하지 않고, 특정 클래스에만 적용하였으면 좋겠다고 생각할 수 있다.
-> 이런 경우에는 'on' 문법을 사용한다. (자주 사용하는 문법은 아니지만, 기억해둘만하다)
mixin ActivationFlag on TimemachineInteger {
}
※ 다른 개발자가 개발한 mixin을 사용하는 것도 좋은 방법이다!
3. Dart 언어의 비동기(Asynchronous) 기능
Dart 언어로 프로그램을 개발하다 보면, Future나 Stream으로 명시된 클래스의 객체를 리턴하는 경우가 종종 있다.
이들은 "비동기 작업(async)"을 지원하기 위한 목적으로 만들어졌다.
-> 간단히 생각해서 2개 이상의 작업이 동시에 수행된다는 의미이다.
Future 클래스는 Dart 언어에서 복수의 작업을 동시에 수행하는 비동기 작업을 지원하고 있다.
※ Stream 클래스는 CPU 보다 속도가 느린 외부 장치를 읽고 쓰는 작업(입출력 작업)과,
CPU가 계산하는 작업을 동시에 수행하도록 하는 경우에 주로 사용한다.
void main() async {
print("main(): started.");
await Future.delayed(Duration(seconds: 3),
() => print("main(): Hello, World! @ 3 seconds later."));
print("main(): completed.");
}
await : 비동기 작업을 요청하며, 해당 비동기 작업이 제대로 종료될 때까지 멈춰서 기다리겠다는 의미다.
async : await 문법을 사용하려면, 반드시 await 구문이 있는 함수의 시작 부분에 async를 작성해야 한다.
void doBackgroundJob(int jobTime) {
if (jobTime > 0) {
print("doBackgroundJob(): $jobTime seconds remained.");
Future.delayed(Duration(seconds: 1), () => doBackgroundJob(jobTime - 1));
} else {
print("doBackgroundJob(): finished.");
}
}
void main() async {
print("main(): started.");
await doBackgroundJob(5);
print("main(): completed.");
}
Q. 그러면, 위의 코드가 의도대로 잘 동작할까?
A. 아니다, 에러가 발생한다!
리턴값이 Future 또는 Stream을 리턴하는 함수에 메서드에만 await를 사용할 수 있다! (doBackgroundJob은 void)
-> 새로운 문법을 찾기 보다는, 직접 생각한 코드로 문제를 해결하려는 노력이 필요하다.
/*
이제 main 함수가 doBackground() 함수의 종료 여부를 알도록 만들어서, doBackground() 함수가 종료된 후에 main 함수가 종료되도록 해보자.
*/
class ActivationFlag {
late bool _flag;
ActivationFlag(bool givenFlag) : _flag = givenFlag;
bool get activated => _flag;
set activated(bool givenFlag) => _flag = givenFlag;
}
void doBackgroundJob(int jobTime, var jobEnd) {
if (jobTime > 0) {
print("doBackgroundJob(): $jobTime seconds remained.");
Future.delayed(
Duration(seconds: 1), () => doBackgroundJob(jobTime - 1, jobEnd));
} else {
print("doBackgroundJob(): finished.");
jobEnd.activated = true;
}
}
void main() async {
var bgJobFinished = ActivationFlag(false);
print("main(): started.");
doBackgroundJob(5, bgJobFinished);
while (bgJobFinished.activated == false) {
await Future.delayed(Duration(seconds: 1));
}
print("main(): completed.");
}
3-1. 동시에 수행할 작업 추가하기
/**
* 방문자의 주문을 접수하는 비동기 작업을 만드는 것
*/
Future<String> serveCustomer() async {
print("serveCustomer(): 접수 시작.");
var customerOrder = await simulateCustomerOrder();
print("serveCustomer(): 주문 접수 완료: $customerOrder.");
return customerOrder;
}
Future<String> simulateCustomerOrder() {
return Future.delayed(Duration(seconds: 2), () => '아이스 아메리카노');
}
void main() async {
print("main(): started.");
var customerOrder = await serveCustomer();
print("main() : serve '$customerOrder'.");
print("main(): completed.");
}
/*
main(): started.
serveCustomer(): 접수 시작.
serveCustomer(): 주문 접수 완료: 아이스 아메리카노.
main() : serve '아이스 아메리카노'.
main(): completed.
*/
아래와 같은 규칙 때문에 serveCustomer() 함수를 보면, 리턴 값이 Future<String>으로 되어 있다.
"async 함수가 리턴 값을 갖는 경우, 리턴 값의 타입은 반드시 Future<>로 정의한다"
1. 비동기로 동작하는 Future.delayed() 메서드의 리턴 값이 Future 객체이기에,
Future<String>을 리턴하는 simulateCustomerOrder() 함수를 만들었다,
2. 이 함수를 호출하면서 결과를 기다려야 하므로,
또 다시 await/async 구문을 사용하면서, Future<String>를 비동기로 리턴하는 serveCustomer() 함수를 만들었다.
3. 또 serveCustomer() 함수도 비동기 함수이기에, 결과가 제대로 리턴될 때까지 함수를 호출한 쪽에서 기다려 주어야 한다.
따라서 serveCustomer()를 호출하는 main 함수에서도 await/async 구문을 반영한다!
4. 파일 입출력 기능 활용하기
Dart 언어는 File 클래스를 통해서 파일의 내용을 읽고 쓸 수 있다.
그리고 Dart 언어는 파일을 다루는 방법을 여러 가지 지원한다.
4-1. Dart 언어의 파일 처리 방법 이해하기
첫 번째는 동기 방식과 비동기 방식 중 어떤 방법을 사용할 지 선택해야 한다.
파일이 저장되어 있는 저장 장치는 물리적으로 느릴 수 있기에 -> 파일을 다룰 때는 주로 비동기 방식을 선택한다.
아래 코드를 보면, 파일을 읽고 쓰는 대부분의 함수들은 비동기 함수로서 async 구문을 사용하며, 리턴 값도 Future<>이다.
두 번째는 파일의 내용을 어떻게 다룰 것인가의 문제이다.
1. 파일을 하나의 문자열로 다루는 방법 : readAsString()
2. 스트림 (stream)을 사용하는 방법 : openRead() 또는 openWrite() 메서드를 써서 파일을 연다.
일단 파일을 열었으면, 필요한 작업을 진행하면서 원하는 형태로 결과를 만들면 된다.
3. 랜덤 액세스 (Random access) 방식 : open() 메서드를 써서 파일을 연 다음, 원하는 지점으로 바로 이동하면서 읽고 쓴느 작업을 하는 방법이다. 따라서 파일 중 원하는 위치를 찾아가기 위한 "위치 계산 방법" 등을 알아야 한다.
※ Random access는 다소 어려운 난이도이므로, 추후 고난이도의 프로그램을 만들어야 하는 경우
"파일 처리(File structure)" 같은 소프트웨어 분야의 이론 서적을 읽고 적용해보기 바란다.
import 'dart:io';
import 'dart:convert'; // Dart 언어의 변환 기능을 사용하기 위해
// 파일을 읽는 목적으로 사용할 전용 함수 2개
// 1. readAsString()으로 하나의 파일을 하나의 문자열에 모두 저장한다
Future<String> readFileToString(String fileName) async {
var file = File(fileName); // 파일을 읽기 위한 File 객체 생성
String fileContent = await file.readAsString(); // 파일의 내용을 문자열로 읽기
return fileContent;
}
// 2. Stream 클래스를 이용하는 방법
Future<List<String>> readFileToList(String fileName) async {
Stream<String> lines = File(fileName)
.openRead() // 파일을 읽기 위한 Stream 객체 생성
.transform(utf8.decoder) // 바이트를 UTF-8로 변환
.transform(LineSplitter()); // 스트림을 개별 라인으로 변환
// 파일을 사용하는 경우, 더욱 더 에러 처리에 신경을 써야 한다.
try {
List<String> sList = [];
await for (var line in lines) {
sList.add(line);
}
return sList;
} catch (error) {
throw (error);
}
}
void main() async {
print("[1] File read scenario #1.");
try {
String fileContent = await readFileToString('src.txt');
print(fileContent);
} catch (error) {
print(error);
}
// ======================================================
print("[2] File read scenario #2.");
try {
List<String> fileContent = await readFileToList('src.txt');
for (var fileLine in fileContent) {
print(fileLine);
}
} catch (error) {
print(error);
}
// ======================================================
print("[3] File write scenario.");
List<String> fileContent = await readFileToList('src.txt');
var sList = [];
var iVar1 = 0;
var iVar2 = 0;
var count = 0;
var dstSink = File('dst.txt').openWrite(); // 파일을 쓰기 위한 Stream 객체 생성
dstSink.write(":=> FILE ACCESSED ${DateTime.now()}\n");
for (var fileLine in fileContent) {
sList = fileLine.split(',');
iVar1 = int.parse(sList[0]);
iVar2 = int.parse(sList[1]);
print("$iVar1 x $iVar2 = ${iVar1 * iVar2}");
dstSink.write("$iVar1 x $iVar2 = ${iVar1 * iVar2}\n");
count++;
}
dstSink.write(":=> $count ITEMS CALCULATED");
dstSink.close(); // 파일 쓰기 종료
}
// File : https://api.dart.dev/stable/dart-io/File-class.html
// Stream : https://api.dart.dev/stable/dart-async/Stream-class.html
5. Dart의 표준 라이브러리 활용하기
여태까지 배웠던 Dart 언어 표준 라이브러리(Standard library)들은 주로 dart:core와 dart:async 그룹에 속했다.
또한 File, IOSink, stdin, stdout 같은 기능들은 dart:io 라는 라이브러리에 속해 있었다.
앞으로 웹 서버/클라이언트 프로그램, 데스크톱 프로그램, 모바일 앱 프로그램을 개발해 나가면서 사용할 라이브러리는 다음과 같다.
1. dart:convert - JSON, ASCII, UNICODE 등의 데이터 코드 간 변환
2. dart:math - 수학의 상수, 함수, 난수(Random number) 생성
3. dart:collection - 큐(queue), 연결 리스트(Linked List), 해시 맵(Hash map), 이진 트리(Binary tree)와 같이 컬렉션 타입 라이브러리
4. dart:html - 웹 브라우저 및 DOM(Document Object Model)을 다뤄야 하는 웹 기반 응용 프로그램을 위한 HTML 요소 및 기타 리소스를 담당한다.
5. dart:typed_data - Unsigned 8바이트 정수와 같은 고정 크기 데이터 및 SIMD(Single Instruction Multiple Data) 숫자타입을 효과적으로 처리하는 데이터 타입
6. dart:ffi - C 언어 스타일 인터페이스를 제공하는, 다른 코드와의 상호 운용성을 위한 외부 함수 인터페이스
7. dart:isolate - 격리(isolates)를 사용한 동시(Concurrent) 프로그래밍, 스레드(Thread)와 유사하지만 메모리를 공유하지 않고, 메시지를 통해서만 통신하는 독립 작업 기능
이외에도 웹 서비스 개발을 지원하기 위한 dart:js / dart:js_util / dart:svg / dart:web_audio / dart:web_gl 등의 라이브러리가 있다.
※ Dart 표준 라이브러리 개요 : https://dart.dev/overview#libraries
Dart overview
A short introduction to Dart.
dart.dev
※ Dart API Reference: https://api.dart.dev/stable/index.html
Dart - Dart API docs
Welcome! Welcome to the Dart API reference documentation, covering the Dart core libraries. These include: dart:core: Core functionality such as strings, numbers, collections, errors, dates, and URIs. dart:io: I/O for non-web apps. dart:html: DOM manipulat
api.dart.dev
6. Flutter로 모바일 앱 개발하기
Material 은 안드로이드 운영체제 전용 그래픽 라이브러리(Material Design 라이브러리)이다.
import 'package:flutter/material.dart';
참고로 Apple의 Design 방식은 Cupertino 라고 한다.
import 'package:flutter/cupertino.dart';
1장. Hello World 프로그램 개발하기
import 'package:flutter/material.dart';
void main() {
runApp(
const Center(
child: Text(
'Hello, World!',
textDirection: TextDirection.ltr,
style: TextStyle(
fontSize: 32,
color: Colors.white,
),
),
),
);
}
첫번째 코드는 위와 같이 해체할 수 있다.
TextStyle 클래스 : Flutter의 표준 클래스. 화면에 글자를 출력해야 하는 경우, 글자의 형태를 어떻게 나타낼 지 정한다.
※ TextStyle 공식 사이트 : https://api.flutter.dev/flutter/painting/TextStyle-class.html
TextStyle class - painting library - Dart API
An immutable style describing how to format and paint text. Bold Here, a single line of text in a Text widget is given a specific style override. The style is mixed with the ambient DefaultTextStyle by the Text widget. link content_copy const Text( 'No, we
api.flutter.dev
Text 클래스 공식 사이트 : https://api.flutter.dev/flutter/widgets/Text-class.html
Text class - widgets library - Dart API
A run of text with a single style. The Text widget displays a string of text with single style. The string might break across multiple lines or might all be displayed on the same line depending on the layout constraints. The style argument is optional. Whe
api.flutter.dev
Center 클래스 : "child 파라미터에 주어지는 무언가"를 화면 정중앙에 배치한다.
Center 클래스 공식 사이트 : https://api.flutter.dev/flutter/widgets/Center-class.html
Center class - widgets library - Dart API
A widget that centers its child within itself. This widget will be as big as possible if its dimensions are constrained and widthFactor and heightFactor are null. If a dimension is unconstrained and the corresponding size factor is null then the widget wil
api.flutter.dev
runApp() 함수 : 주어진 위젯(widget)을 Inflate하여, 화면에 attach한다.
위젯은 레이아웃 중에 전체 화면을 채우도록 강제하는 constraints(제약 조건)이 부여된다.
위젯을 화면의 원하는 위치에 정렬하려면 Align 위젯을 사용하고, 화면 중앙에 위치하도록 하려면 Center 위젯을 사용해라.
Dart 언어의 main() 함수가 시작 지점인 것처럼, Flutter 프로그램의 시작점은 runApp() 함수이다.
즉, main 함수에서는 runApp 함수를 호출하여 Flutter 애플리케이션을 시작하는 기능만 수행한다는 것이다.
main() => runApp(MyApp());
2장. Hello World 프로그램 진화시키기 Part.1
StatelessWidget은 StatefulWidget 과 더불어 Flutter에서 가장 중요하다고 할 수 있는 대표적인 두 개의 위젯이다.
StatefulWidget : 프로그램이 사용자로부터 정보를 받고, 이를 토대로 화면 구성이나 콘텐츠 내용이 바뀌어야 할 때 사용한다.
StatelessWidget : 고정된 내용을 사용자에게 일방적으로 보여주기만 할 때 사용한다.
StatelessWidget 공식 사이트 : https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html
StatelessWidget class - widgets library - Dart API
A widget that does not require mutable state. A stateless widget is a widget that describes part of the user interface by building a constellation of other widgets that describe the user interface more concretely. The building process continues recursively
api.flutter.dev
StatelessWidget 클래스를 상속받으면, 반드시 build() 메서드를 오버라이드해야 한다.
운영체제가 앱을 실행하면서 화면에 출력해야 하는 내용들을 준비하고, 화면에 나타나도록 하는 핵심 기능을 한다.
MaterialApp : Material Design을 사용하는 애플리케이션에서 공통적으로 요구되는 다수의 위젯들을 감싸주는 편리한 위젯
MaterialApp 공식 사이트 : https://api.flutter.dev/flutter/material/MaterialApp-class.html
MaterialApp class - material library - Dart API
An application that uses Material Design. A convenience widget that wraps a number of widgets that are commonly required for Material Design applications. It builds upon a WidgetsApp by adding material-design specific functionality, such as AnimatedTheme a
api.flutter.dev
Scaffold : Material Design으로 만든 모바일 앱이 화면에 어떻게 나타날지를 표현하는 클래스
appBar 속성 : scaffold 화면 상단에 보여줄 애플리케이션 바 내용 (또는 타이틀과 메뉴)
body 속성 : scaffold 화면의 메인 컨텐츠 출력 내용
Scaffold 클래스 공식 사이트 : https://api.flutter.dev/flutter/material/Scaffold-class.html
Scaffold class - material library - Dart API
Implements the basic Material Design visual layout structure. This class provides APIs for showing drawers and bottom sheets. To display a persistent bottom sheet, obtain the ScaffoldState for the current BuildContext via Scaffold.of and use the ScaffoldSt
api.flutter.dev
AppBar 클래스 공식 사이트 : https://api.flutter.dev/flutter/material/AppBar-class.html
AppBar class - material library - Dart API
A Material Design app bar. An app bar consists of a toolbar and potentially other widgets, such as a TabBar and a FlexibleSpaceBar. App bars typically expose one or more common actions with IconButtons which are optionally followed by a PopupMenuButton for
api.flutter.dev
import 'package:flutter/material.dart';
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'App Title',
home: Scaffold(
appBar: AppBar(
title: const Text('AppBar Title'),
),
body: const Center(
child: Text(
'Hello, World!',
textDirection: TextDirection.ltr,
style: TextStyle(
fontSize: 32,
color: Colors.black,
),
),
),
),
);
}
}
3장. Hello World 프로그램 진화시키기 Part.1
Container 위젯은 여러 위젯들을 하나로 묶은 후, 위치 및 크기 조정 등의 시각적인 부분을 처리하는 편리한 위젯이다.
프로그램 개발 시에 여백, 간격, 테두리, 배경색을 추가하고 싶을 때 사용을 추천한다.
5장. Flutter 공식 Counter 프로그램 이해하기
DartPad의 Counter 샘플을 분석해보자.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({
super.key,
required this.title,
});
@override
State<MyHomePage> 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: [
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
debugShowCheckedModeBanner: false -> 프로그램 실행 화면에 "DEBUG"라는 배너 표시를 제거한다.
theme: ThemeData() -> MaterialApp의 기본 테마 설정을 설정한다. theme은 색상, 폰트, 모양 등 앱 외관에 해당하는 설정을 보관한다.
여기서 primarySwatch : Material Design을 따르는 앱의 색상 정보를 저장하는 파라미터이다.
Swatch 설명 : https://m2.material.io/design/color/the-color-system.html#color-theme-creation
Material Design
Build beautiful, usable products faster. Material Design is an adaptable system—backed by open-source code—that helps teams build high quality digital experiences.
m3.material.io
StatefulWidget으로 만드는 프로그램의 구조에는 일종의 공식이 있다.
StatefulWidget은 프로그램의 변경 가능한 '상태(state)'를 가지는 State 위젯을 만들어서 사용한다.
여기서 state는 위젯이 프로그램 속에서 존재하는(life-cycle) 동안 변경될 수 있고,
위젯이 화면 출력 작업을 하는 build() 메서드에 의해서 동기적으로 읽을 수 있는 정보이다.
더 쉽게 말하자면, 실제 StatefulWidget 클래스의 객체는 변경할 수 없다.
프로그램 실행 동안 바뀌는 값은 StatefulWidget 자체에 저장하는 것이 아니고, StatefulWidget이 만드는 State 위젯에 저장한다.
State 위젯을 생성(create)하는 것이 StatefulWidget의 역할이다. 변경되는 정보 자체는 State 위젯의 내용이다!
그래서 StatefulWidget의 createState() 메서드를 오버라이드한 것이다. -> State 위젯 생성
State 위젯은 변경되는 정보를 저장할 "변수"를 정의하고, 변수가 화면에 표시될 때의 모습을 구성하는 build() 메서드를 정의한다. -> 업데이트 되는 정보는 State 위젯이 가지고 있으니까
그러나, 정보가 업데이트되도, 언제 화면을 업데이트할지는 결국 개발자가 setState() 메서드로 결정한다.
결론적으로 아래 같은 4단계로 동작한다는 것을 알 수 있다.
StatefulWidget을 상속받은 클래스는 3가지 요소를 갖는다.
1) 생성자
const MyHomePage({
super.key,
required this.title,
});
Key 타입의 입력 파라미터는 보통 개발자가 직접 부여할 일은 없다.
이 값은 프로그램 내부적으로 만들어지는 객체들의 고유 식별자이다.
this.x 입력 파라미터는, 생성자를 통해서 받은 입력 파라미터를 활용해서 내부의 값을 설정하는 일반적인 절차이다.
2) 상속받은 클래스가 자체적으로 저장하고 싶은 정보
final String title;
예제에서는 AppBar의 title 이름을 저장했다.
다시 한 번 말하자면, StatefulWidget을 상속받은 클래스는 '사용자와 상호작용하여 변동 가능한 값' 이 아니다!
일반적으로 프로그램 전체에서 동일하게 요청하는 문자열, 색상 등의 정보를 저장한다.
3) createState() 메서드의 오버라이드 작업
이 부분이 동적으로 변경되고, 화면의 업데이트 작업을 하는 State 위젯을 만들게 된다.
당연히 createState()의 반환 값은 -> 자신이 만든 State니까 State<MyHomePage> 같은 형식이어야 한다.
State 클래스를 상속받은 클래스는 4가지 요소를 갖는다.
1) 사용자의 상호작용을 통해서(또는 프로그램 내부 계산 작업을 통해서) 동적으로 변하는 변수 영역
int _counter = 0;
4가지 요소 중에서, 가장 중요하다!
2) 이 변수를 실제로 변경하는 영역
void _incrementCounter() {
setState(() {
_counter++;
});
}
변수를 변경했으니, 당연히 setState() 메서드를 호출해서, 화면을 업데이트할 필요가 발생했음을 알린다.
3) build() 메서드의 오버라이드 작업
변경된 상태 정보로 인한 화면의 변화를 계산하고, 화면을 다시 그리는 작업을 한다.
@override
Widget build(BuildContext context) {
4) StatefulWidget이 만든 State 위젯이 반대로, StatefulWidget의 정보에 접근하는 방법
State<> 클래스가 StatefulWidget을 기반으로 만들어진 클래스니까, 당연히 관련 정보가 있다.
widget이라 불리는 프로퍼티에 저장하고, 따라서 widget.{StatefulWidget 객체의 상수/final의 이름} 으로 접근 가능하다.
appBar: AppBar(
title: Text(widget.title),
),
StatefulWidget 공식 사이트 : https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html
StatefulWidget class - widgets library - Dart API
A widget that has mutable state. State is information that (1) can be read synchronously when the widget is built and (2) might change during the lifetime of the widget. It is the responsibility of the widget implementer to ensure that the State is promptl
api.flutter.dev
State 공식 사이트 : https://api.flutter.dev/flutter/widgets/State-class.html
State class - widgets library - Dart API
The logic and internal state for a StatefulWidget. State is information that (1) can be read synchronously when the widget is built and (2) might change during the lifetime of the widget. It is the responsibility of the widget implementer to ensure that th
api.flutter.dev
6장. Stateless Widget 활용하기
Flutter on Mobile 공식 사이트 : https://flutter.dev/multi-platform/mobile
Flutter on Mobile
Bring your app idea to life to more users from day one by building with Flutter on iOS and Android simultaneously, without sacrificing features, quality, or performance.
flutter.dev
Theme 공식 사이트 : https://api.flutter.dev/flutter/material/Theme-class.html
Theme class - material library - Dart API
Applies a theme to descendant widgets. A theme describes the colors and typographic choices of an application. Descendant widgets obtain the current theme's ThemeData object using Theme.of. When a widget uses Theme.of, it is automatically rebuilt if the th
api.flutter.dev
Theme.of(context). 와 같은 표현은 StatelessWidget 이나 State<> 클래스의 build() 메서드 안에서 종종 볼 수 있다.
context는 상위 위젯의 정보가 포함된다. 따라서 Theme.of(context).primaryColor 라고 작성하면, 현재의 위젯이 속해 있는 상위 위젯의 "주 색상"을 가져온다.
ListView 위젯 공식 사이트 : https://api.flutter.dev/flutter/widgets/ListView-class.html
ListView class - widgets library - Dart API
A scrollable list of widgets arranged linearly. ListView is the most commonly used scrolling widget. It displays its children one after another in the scroll direction. In the cross axis, the children are required to fill the ListView. If non-null, the ite
api.flutter.dev
Row 위젯처럼 위에서 아래로 항목들을 나열하는 데 사용한다. 차이점으로는 ListView 위젯은 가장 일반적으로 사용되는 스크롤 위젯이라는 것이다. 즉, 스크롤 방향으로 포함하고 있는 위젯들을 차례대로 표시한다.
7. Flutter 이해하기
Flutter의 기본 철학은, "위젯들을 레고 블록처럼 다루어 프로그램을 만드는 것" 이다.
"Everything is a Widget" 이라고 많이 언급할 정도로, 위젯을 이해하고, 필요한 위젯을 연결하는 작업이 주가 된다.
위젯 공식 사이트 4가지
1) Widget catalog : 그룹별 사전식으로 정리했다. 14개의 항목으로 Flutter의 위젯을 그룹화하여 보여준다.
https://docs.flutter.dev/ui/widgets
Widget catalog
A catalog of some of Flutter's rich set of widgets.
docs.flutter.dev
2) Cookbook : 해결하고자 하는 문제를 기준으로 정리했다. 비슷한 기존 사례가 있다면, 어떤 위젯이 있는지 알아보고 흉내내기 좋다.
EX) 애니메이션에 관심이 있는 사람이 Animation 그룹을 선택하면, 어떤 방식으로 Animation을 사용할 수 있는지 사례가 나온다.
https://docs.flutter.dev/cookbook
Cookbook
The Flutter cookbook provides recipes for many commonly performed tasks.
docs.flutter.dev
3) Sample 프로그램 : 다양한 종류의 예제들이 설명과 함께 제공되고 있다.
EX) "Play and pause a video"에서는 동영상을 재생하는 예제 코드와 함께 주요 부분 설명이 있다.
https://flutter.github.io/samples/#
Flutter samples
sample Flutter Maps Firestore A Flutter sample app that shows the end product of the Cloud Nex...
flutter.github.io
4) Flutter widget index : 사전처럼 모든 위젯을 알파벳 순으로 나열해 놓았다. 이름 + 간단한 1~2줄의 설명 + 간단히 요약한 그림등으로 구성되어 있어서, 알고 싶은 내용을 바로 찾고 싶다면 직관적인 방법이다.
https://docs.flutter.dev/reference/widgets
Flutter widget index
An alphabetical list of Flutter widgets.
docs.flutter.dev
8. 두고두고 활용할 레퍼런스 프로그램 개발하기
8-1. 기본 레퍼런스 프로그램
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final _title = 'Flutter SketchApp';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: const MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
@override
State<MyStatefulWidget> createState() => MyStatefulWidgetState();
}
class MyStatefulWidgetState extends State<MyStatefulWidget> {
final PageController _pageController = PageController();
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
// 첫번째 프로그램은 화면 중앙의 콘텐츠 영역을 간단한 텍스트로 채운다
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Home',
style: optionStyle,
),
Text(
'Hello',
style: optionStyle,
),
Text(
'Star',
style: optionStyle,
),
];
@override // 배치하다, 배열하다, 처분하다
void dispose() {
_pageController.dispose();
super.dispose();
// MyStatefulWidgetState의 Base 클래스에서 dispose() 메서드를 호출한다
// 화면에서 제거되어야 하는 부분이 있을 때 사용하는 용도이다.
// 특별한 작업이 없다면, 코드 그대로 사용한다.
}
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter Sketch Application'),
),
body: PageView(
controller: _pageController,
children: <Widget>[
Scaffold(
body: Center(
// elementAt()으로 _widgetOptions의 아이템 중 하나를 보여준다
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.wb_cloudy),
label: 'Hello',
),
BottomNavigationBarItem(
icon: Icon(Icons.star),
label: 'Star',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.white,
backgroundColor: Colors.blueAccent, // BottomNavigationBar의 기본 색상
onTap: _onItemTapped,
),
),
],
),
),
);
}
}
/*
* REFERENCES
* https://api.flutter.dev/flutter/widgets/PageController-class.html
* https://api.flutter.dev/flutter/widgets/PageView-class.html
* https://api.flutter.dev/flutter/widgets/BottomNavigationBarItem-class.html
*/
PageController 클래스 : 사용자가 대표 메뉴 중 하나를 선택하면 사용자의 선택을 받은 메뉴에 맞춰서 콘텐츠를 보여주고, 메뉴들의 상태(색상 같은)를 변경해주는 기능을 한다.
즉, 개발자가 일일이 메뉴 선택 처리를 하지 않아도, 편하게 개발이 가능하다.
PageView 위젯 : 대표 메뉴를 보여주고, 이 중 선택된 메뉴에 따라 콘텐츠를 보여주는 방식을 제공하기 위해 만들어진 전용 위젯
bottomNavigationBar 프로퍼티 : 말 그대로 아래(bottom)에 있는 메뉴 선택(navigation) 막대기(bar)다.
8-2. 통합 프로그램
TextButton 위젯 : 사용자가 "클릭할 수 있는" 글자를 표시한다.
return Scaffold(
body: Center(
// 사용자가 클릭할 수 있는 글자 : TextButton
child: TextButton(
style: TextButton.styleFrom(
primary: Colors.black,
textStyle: const TextStyle(
fontSize: 32,
),
),
onPressed: () {
showAlertDialog(context);
},
child: const Text('Hello, Press Here!'),
),
),
);
showDialog() 함수 : Material Design 앱의 화면 상단에, 개발자가 설정한 대로 다이얼로그 박스를 나타낸다.
void showAlertDialog(BuildContext context) async {
String result = await showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('AlertDialog Sample'),
content: const Text("Select button you want"),
actions: <Widget>[
TextButton(
child: const Text('OK'),
onPressed: () {
Navigator.pop(context, "OK");
},
),
TextButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.pop(context, "Cancel");
},
),
],
);
},
);
print("showAlertDialog(): $result");
}
context 파라미터 : 이 함수의 입력 파라미터로 받아서 그대로 전달하면 된다.
barrierDismissible : 다이얼로그 박스 밖의 화면을 클릭하면, 다이얼로그 박스를 없앨지 결정하는 파라미터
∴ false라면, 반드시 다이얼로그의 버튼을 클릭해야 사라진다.
builder : 화면에 다이얼로그 박스를 나타내고, 사용자의 입력에 맞춰 동작하도록 설정한다.
지금은 간단하게 AlertDialog() 객체를 만들어서 리턴한다.
AlertDialog : 제목을 title 에서 정의한다. 사용자에게 안내할 내용은 content에서 정의한다.
사용자의 선택에 맞춰서 반응할 메뉴는 actions에서 정의한다. 예제에서는 TextButton 2개를 담은 <Widget>리스트를 추가했다. 선택한 값은 String result에 저장되므로, 필요한 곳에서 사용할 수 있다.
9. 스마트폰에서 Flutter 프로그램 실행하기
9-1. Flutter 공식 사이트의 Get started 완주하기
Flutter 공식 사이트의 Get started : https://docs.flutter.dev/get-started/install
Choose your development platform to get started
Install Flutter and get started. Downloads available for Windows, macOS, Linux, and ChromeOS operating systems.
docs.flutter.dev
Flutter 한국어 공식 사이트 (적용 안된듯?) : https://flutter-ko.dev/
Flutter documentation
Get started with Flutter. Widgets, examples, updates, and API docs to help you write your first Flutter app.
docs.flutter.dev
'Dart와 Flutter > 도서 내용 정리' 카테고리의 다른 글
내용 정리 Part. 2 - [코드팩토리의 플러터 프로그래밍] (0) | 2024.06.13 |
---|---|
내용 정리 Part. 1 - [코드팩토리의 플러터 프로그래밍] (0) | 2024.06.03 |
내용 정리 Part.2 - [풀스택 개발이 쉬워지는 다트 & 플러터] (1) | 2024.03.30 |