[Flutter] 2. Flutter 문법

문정준's avatar
May 19, 2025
[Flutter] 2. Flutter 문법

1. 변수

// 변수 void main() { int n1 = 1; double d1 = 10.1; bool b1 = true; String s1 = "홍길동"; print("n1: ${n1}"); print("d1: ${d1}"); print("b1: ${b1}"); print("s1: ${s1}"); print(d1.runtimeType); }
 
notion image

2. 타입 추론

void main() { // 타입 추론 var n1 = 1; // n1 = "문자열"; n1 = 3; print(n1); print(n1.runtimeType); // 다이나믹 (Object) dynamic n2 = 1; // 타입 변경이 가능 n2 = "문자열"; print(n2.runtimeType); }
 
notion image
 
💡

Object vs Var

  • Object : 모든 객체의 부모 - 어떤 객체의 타입이라도 될 수 있음
  • Var : 값을 보고 타입을 결정 - 이후 다른 타입의 값을 넣으면 타입 변경 불가
💡

Dynamic

  • Object와 같이 모든 객체의 부모 - 어떤 객체의 타입이라도 될 수 있음
  • 특정 타입에서 사용하는 함수 사용은 불가능 : 실제 그 타입은 아니기 때문
 

3. 함수

void f1() { print("f1 호출됨"); } void f2(int n1, int n2) { print("f2 호출됨 : ${n1 * n2}"); } int f3(int n1, int n2) { return n1 * n2; } void main() { f1(); f2(5, 3); int result = f3(8,4); print("f3 result = ${result}"); }
 
notion image
 

4. Lambda

// Lambda Function var f1 = () { print("f1 호출됨"); }; var f2 = () => print("f2 호출됨"); var f3 = () => 1; int f4() { return 1; } void main() { f1(); f2(); print("f3 호출됨 : ${f3()}"); print("f4 호출됨 : ${f4()}"); }
 
notion image
 
💡

익명 함수, 일반 함수, 람다식

  • 익명 함수 : 이름이 없어 1회용으로 사용하는 함수
  • 람다식 : 익명 함수를 사용할 때 간결하게 사용 가능한 표현식
  • 일반 함수 : 이름이 존재하고, 재사용이 가능한 함수
 
💡

함수 타입을 사용하는 이유

  • 함수 타입을 통해 행위를 전달할 수 있음
  • 필요한 곳에만 호출하여 사용 가능
 
 

5. Null 처리 & 선택적 매개변수

Null 처리 (Sound Null Safety)

  • Dart의 기본 타입(int, double, bool, String)은 null을 받을 수 없음
  • null을 처리하기 위해 ‘?’, ‘??’, ‘!’ 사용
    • ? : 값이 있을 수도 있고, 없을 수도 있음 (Optional.ofNullable)
      • Null Safety(Aware) Operator (Null 인지 연산자)
    • ?? : 값이 있으면 그대로 주고, 없으면 다른 값 주기 (Optional.orElseGet)
      • Null Coalescing Operator (Null 병합 연산자)
    • ! : 값이 무조건 있음 (Optional.of)
      • Null Assertion Operator (Null 부정 연산자)
 

선택적 매개변수 (Optional Parameters)

  • Dart는 함수의 오버로딩을 지원하지 않음
  • 대신, 매개변수를 선택적으로 넣을 수 있는 선택적 매개변수를 지원
    • 선택적 매개변수를 추가하면 값이 null 또는 매개변수 값으로 들어감
      • 이때, 값이 null일 경우 default 값도 설정 가능함
    • 함수 호출 시 Key 값을 통해 값을 넣을 수 있음, 또는 넣지 않아도 됨
    • 선택적 매개변수에서 값을 무조건 넣어야 할 경우?
        1. 선택적 매개변수에서 해당 매개변수를 제외
        1. 선택적 매개변수에서 해당 매개변수에 ‘required’ 추가
            • 이 경우 키 값을 통해 값을 넣을 수 있어 편리하지만 코드가 길어질 수 있음
 
// null 처리, 선택적 매개변수 String? username = "nulla"; // s1은 ? 타입, s2는 선택적 매개변수로 인한 default 값 설정 int lenDiv({String? s1, String s2 = "hello"}) { return (((s1 ?? "ssar").length + s2.length) / 2).toInt(); } void main() { int? len = username?.length; // Optional.ofNullable (null 허용) print(len); int len2 = username!.length; // Optional.of (null이면 터짐) print(len2); int len3 = (username ?? "ssar").length; // Optional.orElseGet (null이면 다른값) print(len3); int len4 = lenDiv(s1: username); print(len4); }
 
username에 값이 있을 경우 결과
username에 값이 있을 경우 결과
 
username이 null 일 경우 결과 (len2는 제외)
username이 null 일 경우 결과 (len2는 제외)
 
💡

Null 처리 및 활용

  • 일반 인수에 nullable 값을 넘겨줄 때에는 null이 되지 않도록 처리해서 넘겨주어야 함
  • ‘!’ 기호는 실제로 null이 들어올 수 있을 경우에 매우 위험한 연산자
    • null 처리를 전부 하거나 null이 절대 들어올 수 없을 경우에 사용
  • 선택적 매개변수는 실제 함수의 사용처를 보고 판단하여 사용하여야 함
    • 간단한 함수의 경우는 값을 그냥 적는 것이 더욱 가독성과 코드 이해에 도움이 됨
 

6. 클래스

  1. 클래스 기본
// 클래스 class User { String username; String password; User(this.username, this.password); } void main() { User u1 = User("ssar", "1234"); print(u1.username); print(u1.password); }
 
  1. 선택적 매개변수 활용
// 클래스 class User { String username; String password; User({required this.username, this.password = "1234"}); } void main() { User u1 = User(username: "ssar"); print(u1.username); print(u1.password); }
 
  1. Initialized Keyword (:) 사용
// 클래스 class User { String username; String password; User(String username, String password) : this.username = username, this.password = password == "1234" ? "5678" : password; } void main() { User u1 = User("ssar", "1234"); print(u1.username); print(u1.password); }
 
  1. 초기화 함수 init & cascade 사용
// 클래스 class User { String username; String password; User(this.username, this.password); void init() { if (username == "ssar") { if (password == "1234") { this.password = "9999"; } } } } void hello(User u) {} void main() { hello(User("ssar", "1234")..init()); }
 
  1. 이름이 있는 생성자
  • 기본 생성자와는 다른 상태를 부여할 수 있음
class Button { String text; String color; int x; int y; int width; int height; Button( this.text, { this.color = "회색", this.x = 0, this.y = 0, this.width = 200, this.height = 150, }); Button.block( this.text, { this.color = "회색", this.x = 0, this.y = 0, this.width = 100000000, this.height = 150, }); static Button inlineButton(String text) { return Button(text); } } void main() { Button basicButton = Button("로그인"); Button redButton = Button("로그인", color: "red"); Button blockButton = Button.block("회원가입"); Button inlineButton = Button.inlineButton("회원가입"); }
 

7. 상속 & 컴퍼지션

상속 (Inherit)

  • 한 클래스가 부모의 클래스를 상속받아 자신의 클래스이면서 부모 클래스의 성질도 가질 수 있음
  • 상속은 하나의 클래스밖에 받을 수 없음
// 상속 class Burger { String name; Burger(this.name); } // is class ChickenBurger extends Burger { int price; ChickenBurger(this.price, super.name); } class CheeseBurger extends Burger { CheeseBurger(String name) : super(name); } void main() { Burger b1 = CheeseBurger("치즈버거"); Burger b2 = ChickenBurger(1000, "치킨버거"); }
 

Composition

  • 상속 관계가 아닌, 의존성에 의한 연결 관계
  • Dart에서는 mixin, with를 제공하여 편하게 사용 가능
    • 한 번에 여러 클래스를 받을 수 있음
    • 다른 클래스를 참조(.)하지 않고도 상태를 참조할 수 있음 → 내 클래스의 상태처럼 사용 가능
mixin class Engine { int power = 5000; } mixin class Wheel { int size = 21; } // has class Car with Engine, Wheel {} void main() { Engine e = Engine(); Car c1 = Car(); print(c1.power); print(c1.size); }
Share article

sxias