flutter 3.10 + dart 3.0 알아보기

flutter 3.10 + dart 3.0 알아보기

Tag
Flutter
impeller 와 같은 내용은 제쳐두고, 일단 개발자 입장에서 추가된 문법들을 알아보려고 한다.

Records

record 는 크기가 정해져 있으며, 타입이 지정되기 때문에 타입스크립트에서의 tuple 타입을 떠올리면 될 것이다.
// Examples (String, int) // key-value 쌍의 형식도 가능하다. (String, a: int, b: bool, String)
 
이를 통해서 비구조화 할당처럼 인자를 쏙쏙 뽑아올 수 있다.
하기의 코드에서 positioned field (${position}) 는 named field를 건너 뛰고 값을 가져온다.
void main() { final result = nameAndAge1({'name': 'rldnd', 'age': 26}); print(result); // (rldnd, 26) print(result.$1); // rldnd print(result.$2); // 26 // destructure final (name, age) = nameAndAge1({'name': 'rldnd', 'age': 26}); print(name); // rldnd print(age); // 26 final (name: name1, age: age1) = nameAndAge3({'name': 'rldnd', 'age': 26}); print(name1); // rldnd print(age1); // 26 } // 1 (String, int) nameAndAge1(Map<String, dynamic> json) { return (json['name'], json['age']); } // 2 (String name, int age) nameAndAge2(Map<String, dynamic> json) { return (json['name'], json['age']); } // named field로 지정하는 방법 ({String name, int age}) nameAndAge3(Map<String, dynamic> json) { return (name: json['name'], age: json['age']); }
1번 함수와 2번 함수는 똑같다고 볼 수 있다. 변수의 이름만 붙여줘서 가독성을 높인다고 생각하자.
 

Destructuring

위에서 destructure 을 주석처리 하여, 이미 구현해 보았지만 사실 record 형식이 아니더라도 destructure가 가능하다. 자바스크립트에서의 비구조화 할당과 같다고 생각하면 편하다.
void main() { //LIST final intList = [1, 2, 3]; final [a, b, c] = intList; print(a); // 1 print(b); // 2 print(c); // 3 final numbers = [1, 2, 3, 4, 5, 6, 7, 8]; final [x, y, ...rest, z] = numbers; print(x); // 1 print(y); // 2 print(z); // 8 print(rest); // [3, 4, 5, 6, 7] // MAP final infoMap = {'name': 'rldnd', 'age': 26}; final {'name': name, 'age': age} = infoMap; print(name); // rldnd print(age); // 26 // CLASS const person = Person(name: 'rldnd', age: '26'); final Person(name: name2, age: age2) = person; print(name2); // rldnd print(age2); // 26 } class Person { final String name; final String age; const Person({ required this.name, required this.age, }); }
 

Validation

void main() { final rldnd = ('rldnd', 26); // Unhandled exception: type 'int' is not a subtype of type 'String' in type cast final (name as String, age as String) = rldnd; }
void main() { switcher('aaa'); // match : aaa switcher('bbb'); // no match switcher(['1', '2']); // match: [1, 2] switcher([1, 2]); // match: [int 1, int 2] switcher([1, 2, 3]); // match: [_,_,_] switcher([4, 5, 6]); // match: [_,_,_] switcher([4, 5, 6, 7]); // no match switcher([6, 9]); // match: [int 6, int 9] switcher([6, '9']); // no match } void switcher(dynamic arg) { switch (arg) { case 'aaa': print('match : aaa'); case ['1', '2']: print('match: [1, 2]'); case [_, _, _]: print('match: [_,_,_]'); case [int a, int b]: print('match: [int $a, int $b]'); default: print('no match'); } }
void main() { switcher(7); // 'match < 10 && > 5' print(switcher2(5)); // match: 5 print(switcher2(6)); // no match print(switcher2(7, false)); // no match print(switcher2(7, true)); // match: 7 and true final info = {'name': 'rldnd', 'age': '26'}; final info2 = {'name': 'rldnd', 'age': 26}; // print 되지 않는다. // age의 값이 String 타입이기 때문. if (info case {'name': String name, 'age': int age}) { print(name); print(age); } if (info2 case {'name': String name, 'age': int age}) { print(name); // rldnd print(age); // 26 } } void switcher(dynamic arg) { switch (arg) { // 조건문 가능 case < 10 && > 5: print('match < 10 && > 5'); default: print('no match'); } } String switcher2(dynamic value, bool condition) => switch (value) { 5 => 'match : 5', // condition이 true 이고 value가 7이다. 7 when condition => 'match: 7 and true', _ => 'no match', };
 

class

final class

final로 클래스를 선언하면 다른 파일은 extends, implement, 또는 mixin으로 사용이 불가능 하다.
📌
같은 파일에서는 extends, implement, mixin이 가능하다.
// file1.dart final class Person { final String name; final int age; const Person({required this.name, required this.age}); } // file2.dart class Idol extends Person {} // 애초에 불러와지지 않는다.
 

base class

base로 선언하면 extends는 가능하지만 implement는 불가능하다.
base, sealed, final로 선언된 클래스만 extends가 가능하다.
// file1.dart base class Person { final String name; final int age; Person({required this.name, required this.age}); } // file2.dart base class Idol extends Person { Idol({required super.age, required super.name}); }
 

interface class

interface로 선언하면 implement만 가능하다
// file1.dart interface class Person { final String name; final int age; Person({required this.name, required this.age}); } // file2.dart class Idol implements Person { @override final String name; @override final int age; Idol({required this.name, required this.age}); }
 

sealed clsas

sealed로 선언하면 abstract 이면서 final이다.
패턴매칭을 사용할 수 있도록 해준다.
sealed class Person {} class Idol extends Person {} class Engineer extends Person {} class Chef extends Person {} // exhausted match: chef를 매칭해주지 않았기 때문에 에러가 난다. String whoIsHe(Person person) => switch (person) { Idol i => '아이돌', Engineer e => '엔지니어', };
 

mixin class

mixin은 extends나 with를 사용할 수 없다. 그렇기 때문에 mixin class도 마찬가지로 사용 불가능하다.
클래스는 on 키워드를 사용할 수 없다. 그렇기 때문에 mixin class도 on 키워드를 사용할 수 없다.
mixin class AnimalMixin { String bark() { return '멍멍'; } } class Dog with AnimalMixin { @override String bark() { print('hi'); return super.bark(); } }