티스토리 뷰

프로그래밍/Flutter

Dart 기본기 다지기

Bㅇㅇker 2023. 2. 4. 10:29
반응형

[코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출! 강의을 참고하여 작성하였습니다.

 

Flutter 의 언어는 Dart로 되어 있습니다.

이번 포스트는 Flutter을 시작 하기 전에 알아두면 좋을 Dart 의 기본적인 부분을 공유하려고 합니다.

 

Optional (옵셔널)

Dart는 자료형을 NonNull 또는 Nullable 로 선언할 수 있는데요.

Nullable로 처리하는 방법은 Optional로 처리할 수 있습니다.

  • 옵셔널 선언 방법
    • NULL 허용하겠다는 의미로써 자료형 뒤에 ? 을 입력 (ex) String?)
  • 옵셔널로 설정된 Value에 초기값 설정 방법은 다음과 같습니다.
    • ??= 코틀린의 ?: 와 동일

 

자료형 확인

  • 자료형 확인 방법은 is 로 확인할 수 있습니다.
    • x is <자료형> (is 자바의 intanceof 와 동일. 코틀린의 is와 동일)
    • is가 아닌 경우 is 뒤에 느낌표(!) 을 붙여주어 is! 이렇게 표현하면 됩니다.
void main() {
  int number = 1;
  print(number is int);
  print(number is! String);
}

[result]

true
true

 

Split 사용 방법

Split 에 아무것도 안넣으면 한글짜씩 Split 합니다.

String number = 12345

final parsed = number.split(’’).map((x ⇒ ‘$x.jpg’).toList()

 

Arrow Function (⇒)

map 의 역할은 List 의 존재하는 item을 한개씩 호출하여 List의 값을 변경할 수 있습니다.
Dart 에서는 Arrow Function를 지원하며, Arrow Function을 활용하면 함수 블록(function block) 안에 한줄인 코드를 단축하여 사용 가능합니다.
map 을 활용하여 Arrow 함수를 사용하는 방법은 다음과 같습니다.

List<String> number = [1, 3, 5, 7, 9]
final odd = number.map((x) {
	return '홀수 $x';
});

// 위의 로직을 Arrow 로 표현
final oddUseArrow = number.map((x) =>'홀수 $x');

 

Reduce

List.reduce(pre, next) 는 List의 item을 하나씩 호출하는데 pre 는 이전 값, next 은 다음 값을 가져옵니다.
ex) List.length = 4 인 List.reduce(pre, next) 사용 시
step. step1. pre index0,
next index1 step2. pre (index0 + index1) ,
next index 2 step3. pre ((index0 + index1) + index 2) ,
next index 3

void main() {
  // reduce List을 각 항을 return에 맞춰서 제공한다.
  // reduece는 동일한 값이 들어가야한다.
  List<String> words = [
    '안녕하세요. ',
    '저는 ',
    '샤이찬입니다'
  ];
  final sentence = words.reduce((pre,next) => pre + next);
  print(sentence);
  
  final lenth = words.reduce((pre,next) => '${pre.length + next.length}');
  print(lenth);
}

[result]

안녕하세요. 저는 샤이찬입니다
8

 

reduce의 특성상 reduce block 에 선언한 값은 List의 자료형과 같은 것을 두어야 합니다.

예를들면 List<String> words = [’안녕하세요’, ‘저는’] 이 존재할 때 words.reduce((pre, next) ⇒ pre.lenght + next.lenght); 을 선언할 수 없습니다.

 

Fold

Fold는 Reduce와 다르게 시작 값을 입력 받고 시작 값이 prev 에 들어오고 next 은 index 값이 들어온다.

ex) List.length = 4 인 List.fold(0, (pre, next) 사용 시 다음 순서로 동작합니다.
step. step1. pre 0(시작값),
next index0 step2. pre 0(시작값) + index0 ,
next index1 step3. pre (0(시작값) + index0) + index 1 ,
next index 2 step4. pre (0(시작값) + index0 + index 1) + index 2 ,
next index 3

void main() {
  List<String> words = ['안녕하세요. ', '저는 ', 'KYU입니다'];

  // fold
  List<int> numbers = [1, 3, 5, 7, 9];
  final sum1 = numbers.reduce((prev, next) => prev - next);
  print(sum1);

  final sum = numbers.fold<int>(0, (prev, next) => prev + next);
  // <int> 제네닉으로 어떤값을 리턴할것인지 처리
  // fold<자료형>(시작값, (prev, next))
  print(sum);
  final count = words.fold<int>(0, (pre, next) => next.length);
  print(count);
}

[result]

-23
25
6

Fold는 reduce의 동일한 자료형을 설정하는 것과 다르게 Fold 는 자료형을 입력 받아 원하는 자료형으로 Fold Block 안의 동작을 수행할 수 있습니다.

예를들면 List<String> words = [’안녕하세요’, ‘저는’] 이 존재할 때 words.fold<int>(’’,(pre, next) ⇒ pre.lenght + next.lenght); 이렇게 사용할 수 있습니다.

 

List 를 map 으로 index 가져오는 방법

List 를 map 으로 index 가져오는 방법은 List를 asMap()을 통해 Map으로 변경하고 entries 를 통해 key을 index ㅍvalue에는 List의 index 값을 넣어줍니다.

void main() {
  final numbers = [
    123,
    456,
    789
  ];
  
 print(numbers.asMap().entries.toList());
}

[result]

[MapEntry(0: 123), MapEntry(1: 456), MapEntry(2: 789)]

 

List 합치기

List 2개를 하나의 리스트에 넣는 방법은 […List] 을 하면 됩니다.

[…List]는 함수형 프로그램으로써 List와 다른 instance를 생성하게 됩니다.

void main() {
  List<int> even = [
    2,
    4,
    6,
    8,
  ];

  List<int> odd = [1, 3, 5, 7, 9];

  // cascading operator
  print([even, odd]);
  print([...even, ...odd]);
  print(even == [...even]);
}

[result]

[[2, 4, 6, 8], [1, 3, 5, 7, 9]]
[2, 4, 6, 8, 1, 3, 5, 7, 9]
false

 

Future (Async Programming)

Future 키워드는 Future<T> 로 선언할 수 있습니다.

Future.delayed(param1 param2)
   * param1 : 지연할 기간 (얼마나 지연할 것인가)
    * param2 : 지연 시간이 지난 후 실행할 함수

void main() {
  // Future 미래
  // 미래에 받아올 값
  Future<String> name = Future.value('코드팩토리');
  Future<int> number = Future.value(1);
  Future<bool> isTrue = Future.value(true);
  
  addNumbers(1, 1);
  addNumbers(2, 2);
}

void addNumbers(int number1, int number2) {
  print('계산 시작 : $number1 + $number2');

  Future.delayed(Duration(seconds: 2), () {
    print('계산 완료 : $number1 + $number2 = ${number1 + number2}');
  });

  print('함수 완료');
}

[result]

계산 시작 : 1 + 1
함수 완료
계산 시작 : 2 + 2
함수 완료
계산 완료 : 1 + 1 = 2
계산 완료 : 2 + 2 = 4

 

Await

Future.delayed() … ansync 된 것을 ansync가 끝나기 전까지 대기 할 수 없을까? 이런 고민을 해결할 수 있도록 await 을 제공하고 있습니다.
await 을 하였을 때 thread로 돌리지만 ansync 안에 함수는 대기를 하게 됩니다.

void main() {
  // Future 미래
  // 미래에 받아올 값
  Future<String> name = Future.value('코드팩토리');
  Future<int> number = Future.value(1);
  Future<bool> isTrue = Future.value(true);
  
  addNumbers(1, 1);
  addNumbers(2, 2);
}

void addNumbers(int number1, int number2) async {
  print('계산 시작 : $number1 + $number2');

  await Future.delayed(Duration(seconds: 2), () {
    print('계산 완료 : $number1 + $number2 = ${number1 + number2}');
  });

  print('함수 완료');
}

[result]

계산 시작 : 1 + 1
계산 시작 : 2 + 2
계산 완료 : 1 + 1 = 2
함수 완료
계산 완료 : 2 + 2 = 4
함수 완료

 

await 을 사용하여 async을 처리 시 기다리게 할 수 있습니다. 여기서 더 나아가 Function도 async 를 설정 할 수 있습니다.

void main() async {
  // Future 미래
  // 미래에 받아올 값
  Future<String> name = Future.value('코드팩토리');
  Future<int> number = Future.value(1);
  Future<bool> isTrue = Future.value(true);

  final result1 = await addNumbers(1, 1);
  final result2 = await addNumbers(2, 2);
  print('result1 + result2 = ${result1 + result2}');
}

Future<int> addNumbers(int number1, int number2) async {
  print('계산 시작 : $number1 + $number2');

  await Future.delayed(Duration(seconds: 2), () {
    print('계산 완료 : $number1 + $number2 = ${number1 + number2}');
  });

  print('함수 완료');

  return number1 + number2; // 자동으로 Future 에 감싸서 return 해준다.
}

[result]

계산 시작 : 1 + 1
계산 완료 : 1 + 1 = 2
함수 완료
계산 시작 : 2 + 2
계산 완료 : 2 + 2 = 4
함수 완료
result1 + result2 = 6

 

Stream

stream 을 사용하려면 ‘dart:async’ 을 import 해야합니다.

import 'dart:async';

void main() {
  final controller = StreamController();
  final stream = controller.stream;
  
  final streamListener1 = stream.listen((val){
    print('Listener 1: $val');
  });
  
  controller.sink.add(1);
}

[result]

Listener 1: 1

 

stream으로 listen 을 할 수 있는데요. steam 만 선언하고 listen을 하면 한번만 listen을 할 수 있습니다.

계속 리스닝 할 수 있는 방법은 asBroadcastStream() 을 설정하면 됩니다.

import 'dart:async';

void main() {
  final controller = StreamController();
  final stream = controller.stream.asBroadcastStream();
  
  final streamListener1 = stream.listen((val){
    print('Listener 1: $val');
  });
  
  final streamListener2 = stream.listen((val){
    print('Listener 2: $val');
  });
  
  controller.sink.add(1);
}

[result]

Listener 1: 1
Listener 2: 1

 

stream에 where 조건을 두어서 필터를 걸 수 있습니다.

import 'dart:async';

void main() {
  final controller = StreamController();
  final stream = controller.stream.asBroadcastStream();
  
  final streamListener1 = stream.where((val) => val % 2 == 0).listen((val){
    print('Listener 1: $val');
  });
  
  final streamListener2 = stream.where((val) => val % 2 == 1).listen((val){
    print('Listener 2: $val');
  });
  
  controller.sink.add(1);
  controller.sink.add(2);
  controller.sink.add(3);
  controller.sink.add(4);
  controller.sink.add(5);
}

[result]

Listener 2: 1
Listener 1: 2
Listener 2: 3
Listener 1: 4
Listener 2: 5

 

stream을 function 으로 처리할 수 있습니다.

yield는 stream 이 돌고 있는 동안 값을 listen에 넣어준다.

import 'dart:async';

void main() {
  calculate(2).listen((val){
    print('calculate(2) :  $val');
  });
  
    calculate(4).listen((val){
    print('calculate(4) :  $val');
  });
}

Stream<int> calculate(int number) async*{
  for(int i = 0; i< 5; i++){
    yield i * number;
    
    // await 하고 있으나 thread는 돌고 있다.
    await Future.delayed(Duration(seconds: 1));
  }
}

[result]

calculate(2) :  0
calculate(4) :  0
calculate(2) :  2
calculate(4) :  4
calculate(2) :  4
calculate(4) :  8
calculate(2) :  6
calculate(4) :  12
calculate(2) :  8
calculate(4) :  16

 

Stream 이 다 끝나면 다음 Steam을 하고 싶을 때는 어떻게 할 수 있을까요?

Stream 함수 앞에 yield* 을 붙이면 됩니다.

import 'dart:async';

void main() {
  playAllStream().listen((val){
    print(val);
  });
}

Stream<int> playAllStream() async*{
  yield* calculate(1);
  yield* calculate(1000);
  // yield* : Steam 이 끝날때까지 기다린다.
}

Stream<int> calculate(int number) async*{
  for(int i = 0; i< 5; i++){
    yield i * number;
    
    // await 하고 있으나 thread는 돌고 있다.
    await Future.delayed(Duration(seconds: 1));
  }
}

[result]

0
1
2
3
4
0
1000
2000
3000
4000

마무리

Dart의 기본 문법을 알아봤습니다. 가장 많이 사용하는 방식으로써 Flutter를 개발하는데 도움이 되면 좋겠습니다.

 

참고

코드팩토리 강의

 

반응형
댓글