다트란?
- 완전한 비동기 언어
- 널 안전성 및 스프레드 기능 등 효율적으로 UI코드 작성
- 핫 리로드 : 진짜 굳! 변경사항 즉시 화면에 반영됨
기초 문법
변수 선언 var
void main() {
var name = '문자열';
print(name);
// var는 변수로 타입을 추론하고 확정함
// 위에서 이미 문자열로 타입을 확정했는데 숫자 못 넣음
name = 1;
print(name);
}
변수 선언 dynamic
void main() {
dynamic name = '문자열';
//dynamic은 변수 타입이 고정되지 않아서 다른 타입의 값도 넣기 가능
name = 1;
print(1);
}
런타입 상수 final
void main() {
final DateTime now = DateTime.now();
//final은 런타임 상수
//빌드한 시점까지는 괜찮고 실행할 때 값이 정해지면 그대로 변경 불가
print(now);
}
빌드 타임 상수 const
void main() {
const DateTime now = DateTime.now();
//const는 빌드 타임 상수
//코드를 빌드 할 때 정해져 있어야하니까 코드 실행 전부터 값이 있어야함
//즉 빌드 시점에 값이 확정 안 나니까 에러뜸
print(now);
}
기본 변수 타입
- var이나 dynamic으로 타입추론도 가능하지만 명시해주는 게 직관적이고 좋음
void main() {
String name = 'name';
int isInt = 10;
double isDouble = 2.4;
bool isTrue = true
}
컬렉션
-여러 값을 하나의 변수에 저장할 수 있는 타입
1) List : 여러 값을 순서대로 저장
2) Map : 특정 키값을 토대로 값을 저장 및 검색
3) Set : 중복된 데이터를 제거하는 컬렉션
List 타입
- 여러 값을 순서대로 나열할 때 사용
- 원소 값은 [] 로 표시됨
void main() {
// 리스트에 넣을 타입을 <> 사이에 명시할 수 있습니다.
List<String> blackPinkList = ['리사', '지수', '제니', '로제'];
print(blackPinkList);
print(blackPinkList[0]); // 첫 원소 지정
print(blackPinkList[3]); // 마지막 원소 지정
print(blackPinkList.length); // ❶ 길이 반환
blackPinkList[3] = '코드팩토리'; // 3번 인덱스값 변경
print(blackPinkList);
}
결과
[리사, 지수, 제니, 로제]
리사
로제
4
[리사, 지수, 제니, 코드팩토리]
1) add함수
- list에 값 추가할 때 사용하며 리스트의 끝에 값이 추가됨
void main() {
List<String> blackPinkList = ['리사', '지수', '제니', '로제'];
blackPinkList.add('코드팩토리'); // 리스트의 끝에 추가
print(blackPinkList);
}
2) where함수
- 리스트에 있는 값들을 순서대로 돌면서 특정 조건에 맞는 값만 필터링해서 `이터러블`로 리턴해줌
- 매개변수가 필요하며 해당 매개변수를 통한 조건을 통해 참인지 거짓인지 판별
- where(), map()등 순서가 있는 값을 반환할 때 사용
void main() {
List<String> blackPinkList = ['리사', '지수', '제니', '로제'];
final newList = blackPinkList.where(
(name) => name == '리사' || name == '지수', // ‘리사’ 또는 ‘지수’만 유지
);
print(newList);
print(newList.toList()); // Iterable을 List로 다시 변환할 때 .toList() 사용
}
(리사, 지수)
[리사, 지수]
3) Map함수
- 리스트에 있는 값들을 순서대로 돌면서 값들을 변경할 수 있음
- 파라미터가 필요하며 map에 있는 값들을 하나씩 꺼내서 원하는 조작을 한 뒤 그 값을 현재값이랑 대체해서 넣음
void main() {
List<String> blackPinkList = ['리사', '지수', '제니', '로제'];
final newBlackPink = blackPinkList.map(
(name) => '블랙핑크 $name', // 리스트의 모든 값 앞에 ‘블랙핑크’를 추가
);
print(newBlackPink);
// Iterable을 List로 다시 변환하고 싶을 때 .toList() 사용
print(newBlackPink.toList());
}
(블랙핑크 리사, 블랙핑크 지수, 블랙핑크 제니, 블랙핑크 로제)
[블랙핑크 리사, 블랙핑크 지수, 블랙핑크 제니, 블랙핑크 로제]
이터러블 Iterable
- 반복 가능한 객체를 나타내는 인터페이스
- 해당 인터페이스를 List,Map이 가지고 있어서 where, map함수에서 리턴값으로 받기 가능
- 컬랙션을 쉽게 조작하고 for-in같은 반복작업을 수행할 수 있음
- 요점은 이터러블로 리턴을 받으면 이게 리스트가 아니란 거임
리스트 -> []
이터러블 -> ()
감싸는 모양자체가 다르니까 따로 변환해서 사용하던가 하자!!
4) reduce 함수
- 리스트에 있는 값들을 순회하면서 매개변수에 있는 함수를 실행
- 특징은 매개변수가 2개 받고 순회할 때마다 앞에서 처리한 결과를 그대로 가지고 와서 다시 매개변수로 사용한다는 것
- 처리한 값을 쌓아가면서 순회한다는 것이 특징
- iterable을 반환하지 않고 리스트를 반환
void main() {
List<String> blackPinkList = ['리사', '지수', '제니', '로제'];
final allMembers = blackPinkList.reduce((value, element) => value + ', ' + element); // ➊ 리스트를 순회하며 값들을 더합니다.
print(allMembers);
}
리사, 지수, 제니, 로제
5) fold함수
- reduce랑 같이 함수 순회하면서 축약하는 기능
- 파라미터로 리스트 내의 원소가 아니라 특정값을 첫 번째 값으로 지정하고 순회하는 것이 reduce랑 차이점
void main() {
List<String> blackPinkList = ['리사', '지수', '제니', '로제'];
final allMembers = blackPinkList.reduce((value, element) => value + ', ' + element); // ➊ 리스트를 순회하며 값들을 더합니다.
print(allMembers);
}
리사, 지수, 제니, 로제
reduce 와 fold함수 비교
- 둘 다 리스트를 축약하는 함수
- reduce는 초기값부터가 리스트 내부에 있는 원소로 진행
- fold는 초기값을 사용자가 따로 지정해서 순회가능
Map타입
- 키와 값을 매핑해서 값을 저장
- 순서대로 저장하는 리스트와 달리 키값을 통해 원하는 값을 빠르게 찾는데 중점
- 맵은 딱히 볼게 없군!
- 이터러블로 반환
void main() {
Map<String, String> dictionary = {
'Harry Potter': '해리 포터', // 키 : 값
'Ron Weasley': '론 위즐리',
'Hermione Granger': '헤르미온느 그레인저',
};
print(dictionary['Harry Potter']);
print(dictionary['Hermione Granger']);
print(dictionary.keys);
// Iterable이 반환되기 때문에 .toList()를 실행해서 List를 반환받을 수도 있음
print(dictionary.values);
}
해리 포터
헤르미온느 그레인저
(Harry Potter, Ron Weasley, Hermione Granger)
(해리 포터, 론 위즐리, 헤르미온느 그레인저)
Set타입
- 중복없는 값들의 집합
- 각 원소들의 유일함을 보장할 수 있음
- 각 결과값은 {} 으로 표시 됨
void main() {
Set<String> blackPink = {'로제', '지수', '리사', '제니', '제니'}; // ➊ 제니 중복
print(blackPink);
print(blackPink.contains('로제')); // ➋ 값이 있는지 확인하기
print(blackPink.toList()); // ➌ 리스트로 변환하기
List<String> blackPink2 = ['로제', '지수', '지수'];
print(Set.from(blackPink2)); // ➍ List 타입을 Set 타입으로 변환
}
{로제, 지수, 리사, 제니}
true
[로제, 지수, 리사, 제니]
{로제, 지수}
Enum
- 한 변수의 값을 몇 가지 옵션으로 제한하는 기능
- 선택지가 제한적일 때 사용
- switch문에서 사용하면 적절할 듯
enum Status {
approved,
pending,
rejected,
}
void main() {
Status status = Status.approved;
print(status); // Status.approved
}
기본 연산자
1) 기본 수치 연산자
- 그냥 간단한 것들
void main() {
double number = 2;
print(number + 2); // 4 출력
print(number - 2); // 0 출력
print(number * 2); // 4 출력
print(number / 2); // 1 출력. 나눈 몫
print(number % 3); // 2 출력. 나눈 나머지
// 단항 연산도 됩니다.
number++; // 3
number--; // 2
number += 2; // 4
number -= 2; // 2
number *= 2; // 4
number /= 2; // 1
}
2) null 관련 연산자
- 다트의 경우 변수타입이 null을 가지는지 아닌지 지정해줘야함
- 만약 null이 들어갈 가능성이 있는 경우 ? 를 붙여줘야함
void main() {
// 타입 뒤에 ?를 명시해서 null값을 가질 수 있습니다.
double? number1 = 1;
// 타입 뒤에 ?를 명시하지 않아 에러가 납니다.
// double number2 = null;
double? number; // 자동으로 null값 지정
print(number);
number ??= 3; // ??를 사용하면 기존 값이 null일 때만 저장됩니다.
print(number);
number ??= 4; // null이 아니기 때문에 기존 3이 그대로 유지됩니다.
print(number);
}
null
3
3
switch문
- 입력된 상수값에 따라 조건에 맞는 로직을 수행
- enum이랑 잘 맞음
enum Status {
approved,
pending,
rejected,
}
void main() {
Status status = Status.approved;
switch (status) {
case Status.approved:
// approved 값이기 때문에 다음 코드가 실행됩니다.
print('승인 상태입니다.');
break;
case Status.pending:
print('대기 상태입니다.');
break;
case Status.rejected:
print('거절 상태입니다.');
break;
default:
print('알 수 없는 상태입니다.');
}
// Enum의 values값은 Enum의 모든 수를 리스트로 반환합니다.
print(Status.values);
}
승인 상태입니다.
[Status.approved, Status.pending, Status.rejected]
함수와 람다
매개변수 지정 방법
1) 포지셔널 파라미터
- 매개변수 순서에 따라 함수에 해다 값이 지정됨
int addTwoNumbers(int a, int b) {
return a + b;
}
void main() {
print(addTwoNumbers(1, 2));
}
- 포지셔널 파라미터에 기본값 지정해서 정의하기
int addTwoNumbers(int a, [int b = 2]) {
return a + b;
}
void main() {
print(addTwoNumbers(1));
}
- required를 제거하면 굳이 파라미터로 기본 값 지정 안해도 파라미터 한 개면 해당 매개변수를 쓰는 게 가능
int addTwoNumbers({
required int a,
int b = 2,
}) {
return a + b;
}
void main() {
print(addTwoNumbers(a: 1));
}
2) 네임드 파라미터
- 순서와 관계없이 매개변수 이름을 이용해서 값을 입력할 수 있음
- 키와 값형태로 매개변수를 입력하면 되니까 입력 순서는 중요하지 않음
int addTwoNumbers({
//required : 매개변수가 null이 불가능하다는 것
// 기본값을 입력해주거나 필수로 입력해야함
required int a,
required int b,
}) {
return a + b;
}
void main() {
print(addTwoNumbers(a: 1, b: 2));
}
3) 포지셔널 & 네임드 파라미터 섞어서 사용하기
- 포지셔널 파라미터를 무조건 앞에 정의하고 그 후 네임드 파라미터를 뒤에 정의
int addTwoNumbers(
int a, {
required int b,
int c = 4,
}) {
return a + b + c;
}
void main() {
print(addTwoNumbers(1, b: 3, c: 7));
}
람다 함수
- 간단하게 함수를 정의할 수 있음
- 가독성이 높다
- 콜백 함수 혹은 리스트의 map , reduce , fold함수 등 일회성 동작이 많은 코드에서 로직 수행을 위해 쓰이곤 한다
- 아래의 코드를 보면 람다함수로 정의한 코드가 더 간결함
void main() {
List<int> numbers = [1,2,3,4,5];
// 일반 함수로 모든 값 더하기
final allMembers = numbers.reduce((value, element) {
return value + element;
});
print(allMembers);
// 람다 함수로 모든 값 더하기
final allMembers2 = numbers.reduce((value, element) => value + element);
print(allMembers2);
}
typedef 함수
- 함수의 시그니처(반환값, 타입, 매개변수 개수 및 타입 등 )를 정의
- 함수 선언부를 정의하는 키워드
- Operation에 대한 선언부를 typedef로 선언하고 그 하위에 함수 기능들을 정의해서 사용할 수 있도록 함
- 다트에서 함수는 일급 객체 => 함수를 값처럼 사용 가능 = 변수 및 파라미터로 사용이 가능
typedef Operation = void Function(int x, int y);
void add(int x, int y) {
print('결괏값 : ${x + y}');
}
void subtract(int x, int y) {
print('결괏값 : ${x - y}');
}
void calculate(int x, int y, Operation oper) {
oper(x, y);
}
void main() {
// typedef는 일반적인 변수의 type처럼 사용 가능
Operation oper = add;
oper(1, 2);
// subtract() 함수도 Operation에 해당되는
// 시그니처이므로 oper 변수에 저장 가능
oper = subtract;
oper(1, 2);
// 함수를 파라미터로 사용
calculate(1, 2, add);
}
참고
“이 글은 골든래빗 《Must Have 코드팩토리의 플러터 프로그래밍 2판》의 스터디 내용 입니다.”
'프로그래밍 > 플러터' 카테고리의 다른 글
Getters and Setters (0) | 2024.02.18 |
---|---|
다트 비동기 프로그래밍 (1) | 2024.02.10 |
다트 객체지향 프로그래밍 (1) | 2024.02.10 |
플러터 안드로이드 스튜디오 세팅 (0) | 2024.01.27 |
플러터 macOS 환경설정 (0) | 2024.01.27 |
하고 싶은 걸 하고 되고 싶은 사람이 되자!
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!