[Dart] v3.x Additional Syntax

hodu·5일 전
0

Flutter

목록 보기
32/32

@docImport

@docImport 으로 문서에서 import 하지 않아도 패키지를 그대로 사용할 수 있습니다.

참고 문서: https://dart.dev/tools/doc-comments/references


null-aware

null-aware 문법 중 ?<expression> syntax가 추가 되었습니다.
값을 연산, 접근/할당 시 null 값을 안전하게 처리합니다.

/// a의 값이 null이라면 할당하지 않습니다.
int? a;
var b = [4, 5];
var list = [1 , 2, 3, ?a, ...b]; // [1, 2, 3, 4, 5]

참고 문서: https://dart.dev/language/collections#null-aware-element


Label

goto 유사 문법인 Labelbreakcontinue control flow statement가 추가 되었습니다.

조건문 혹은 루프 문에서 특정 위치에서 break/continue 하기 위함입니다.

outerLoop: for (var i = 0; i < 5; i++) {
  for (var j = 0; j < 5; j++) {
    if (i + j > 5) break outerLoop;
  }
}

참고 문서: https://dart.dev/language/loops#labels


Record

(…) 형태를 record literal 이라고 합니다.

final buttons = [ 
	(
    	label: "Button I", 
		icon: const Icon(Icons.upload_file), 
    	onPressed: () => print("Action -> Button I"), 
    ),
    ( 
    	label: "Button II", 
        icon: const Icon(Icons.info), 
        onPressed: () => print("Action -> Button II"),
     ),
];

위와 같이 buttons 가 있을 때,

리스트 전체인 buttons 는 List<({String label, Icon icon, void Function() onPressed})> 타입을 갖으며
단일 요소로는 ({String label, Icon icon, void Function() onPressed}) 타입을 갖습니다.

위와 같이 암묵적인 타입을 갖겠지만, 리스트 타입을 명시적으로 지정해주면 코드 수정에 더 용이할 것 같습니다.

typedef ButtonItem = ({String label, Icon icon, void Function()? onPressed});
final List<ButtonItem> buttons = [
  // ...
];

그러나 이렇게 보면 이전의 타입을 명시하여 작성했던 코드처럼 보입니다.
맞습니다. 결국 타입을 명시하지 않고 간결하게 작성하도록 개선된 것입니다.

즉, 타입을 명시하지 않은 경우, 아래와 같은 단점을 갖게 됩니다.

desc
타입 안전성 부족var 또는 List<record>로 선언하지 않으면, 필드 타입이 명시되지 않아 헷갈릴 수 있음
자동 완성 제한IDE에서 클래스처럼 buttons[0].label 입력 시 자동 완성이 덜 직관적일 수 있음
재사용 어려움record는 재사용 구조/상속 불가 → 복잡한 로직에는 부적합
패턴 매칭 필요 시필드 이름이 변경되면 모든 코드 수정 필요

참고 문서: https://dart.dev/language/records#records-as-simple-data-structures


Implicit downcast

Dart v2.9 이후 부터 런타임 오류 가능성으로 인해, 암묵적 downcast를 권장하지 않습니다.

analysis_options.yaml에 아래와 같이 추가하여 더 엄격한 prevent가 가능합니다.

analyzer:
  language:
    strict-casts: true

Before

dynamic value = "hello";
String text = value; // OK, 암묵적 downcast 허용

After

dynamic value = "hello";
String text = value; // ❌ 오류: implicit downcast from dynamic
String text2 = value as String; // ✅ 명시적 cast 필요

참고 문서: https://dart.dev/language/type-system#implicit-downcasts-from-dynamic


trailing comma

새롭게 추가된 trailing comma로 인해, 만일 설정해 놓은 코드의 max length를 넘어간다면
comma가 자동 생성되어 줄바꿈이 되며,

이전 코드의 length와 현재 코드의 length의 길이 합이 최대 길이보다 작으면
줄바꿈을 위해 작성했던 comma가 삭제되고, 강제로 이전 코드 라인으로 줄 변경이 이뤄집니다.

만일 comma대로 개행이 되길 원한다면 pubspec.yaml 파일에서 dart sdk의 최소 버전을
v3.6.0 이후로 지정해 줍니다.

environment:
  sdk: ">=3.6.0 <4.0.0"

참고 문서: https://github.com/dart-lang/dart_style/issues/1253

Dart v3.8 이후 부터 analysis_options.yaml 파일에서 아래와 같이
comma를 보존한다는 옵션을 작성한다면,

개행을 위해 명시적으로 작성했던 comma가 사라지지 않지만
comma로 구분하지 않고 이어 작성했던 코드도 comma가 생성되어 강제 개행이 됩니다.

formatter:
  trailing_commas: preserve
Example

Before use

runApp(
    ShowCaseWidget(builder: (context) {
        return MultiProvider(
          providers: getProviders(),
          child: const MyApp(),
        );
      }),
  );

After use

/// builder 라인이 comma 보존 옵션에 의해 개행됩니다.
runApp(
    ShowCaseWidget(
      builder: (context) {
        return MultiProvider(
          providers: getProviders(),
          child: const MyApp(),
        );
      },
    ),
  );

page width

analysis_options.yaml 에서 최대 코드 길이를 설정하는 옵션이 새롭게 추가 되었습니다.

formatter:
  page_width: 100 

top-level에서 동작하기 때문에 IDE의 max length와 setting.json의 설정 보다도 위에서 동작합니다.
단, 코드 길이 가이드 라인은 IDE에서 조정해야 변경됩니다.

참고 문서: https://dart.dev/tools/dart-format#configuring-formatter-page-width


switch (state)

이전 버전에서는 값 기반 비교였지만 이후 부터는 객체 패턴도 매치가 가능해졌습니다.

sealed class State {}

class Loading extends State {}
class Success extends State {}
class Error extends State {}

void main() {
  State state = Loading();

  switch (state) {
    case Loading():
      print('loading...');
    case Success():
      print('successful!');
    case Error():
      print('error!');
  }
}

:final

아래와 같이 사용합니다.

ClassName(:final fieldName)

named field가 있는 객체에서 필드를 추출할 때 사용됩니다.

class Person {
  final String name;
  final int age;
  Person(this.name, this.age);
}

void main() {
  Person p = Person('h', 25);

  switch (p) {
    case Person(:final name, :final age):
      print('$name, $age');
  }
}
/// result: h, 25
profile
Flutter developer

0개의 댓글