코딩/Flutter

Flutter : Dart vs Java 차이점 요약, StatelessWidget vs StatefulWidget과 위젯 트리 개념

americanoallday 2025. 7. 18. 16:29

Dart vs Java 차이 (요약 정리)

항목 Dart Java
플랫폼 주로 Flutter에서 사용됨 주로 Android/Backend 등
함수 위치 함수 단독 선언 가능 (main 바깥) 클래스 안에만 정의 가능
변수 타입 추론 var, final 지원 (타입 생략 가능) 대부분 명시적 타입 선언 필요
널 안전 널 안정성(Null Safety) 내장 (String?) 최근 Java도 Optional로 처리 가능
UI 구성 코드 기반 위젯 트리 XML 기반 뷰 또는 코드
클래스 문법 간결함 (this.생략, => 사용) 비교적 장황함

🧠 즉, 문법은 비슷하지만, Dart는 함수형 스타일과 UI 구성 최적화를 위해 훨씬 더 “간결하게” 설계 됨.

 

변수 타입 추론 추가 설명

더보기

일단 

var : 입력받은 값에 따라 자료형을 결정. 한 번 정한 자료형은 변경할 수 없습니다.

dynamic : 입력받은 값에 따라 자료형을 결정. 다른 변수를 입력하여 자료형을 변경 할 수 있습니다. 

 

삼항 연산자 

var visibility = isPublic ? 'public' : 'private';

String playerName(String name) => name ?? 'Guest'; 

-> ?? : 왼쪽이 널이면 오른쪽 값으로 대입

 

널 안정성 (null safety)

자료형 다음에 ? 붙이면 null 가능, 없으면 null 불가능.

식 다음에 ! 붙여 널이 아님을 나타낼 수 있음. 

그러니까 널이 들어갈 수 있는 변수는 똑같이 널이 들어갈 수 있는 변수라고 선언하고 써야지 재대입이 가능하다는 것 같음

int? couldReturnNullButDoesnt() => -3;
int? coludBeNullButIsnt = 1; // null로 변경 가능
List<int?> listThatCouldHoldNulls = [2, null, 4]; // List의 int에 null 포함 가능
List<int>? nullsList; // List 자체가 null일 수 있음.

int a = couldBeNullButIsnt; // null 넣으면 오류
int b = listThatCouldHoldNulls.first; // int b는 ?가 없으므로 오류
int b = listThatCouldHoldNulls.first!; // null이 아님을 직접 표시
int c = couldReturnNullButDoesnt().abs(); // null일 수도 있으므로 abs()에서 오류
int c = couldReturnNullButDoesnt()!.abs(); // null이 아님을 직접 표시

 

Java도 var, final 둘 다 쓸 수 있음, 다만 Dart와는 동작 방식, 적용 범위, 철학이 다르다는 점에서 중요한 차이가 있음.

 

✅ Dart vs Java의 var, final 차이 정리

항목 Dart Java (>= 10)
var 의미 타입 추론 + 재할당 가능 타입 추론 + 재할당 가능
final 의미 값 변경 불가 (immutable) 값 변경 불가 (레퍼런스 변경 불가)
val 키워드 ❌ 없음 ❌ 없음 (final로 대체)
top-level에서 var 사용 O 가능 ❌ 클래스 바깥에서 변수 선언 안 됨
함수 바깥에서 함수 선언 O (void sayHi() {}) ❌ 클래스 내부에만 함수 선언 가능
전체 스타일 함수형+스크립트형 스타일 허용 객체지향 중심, 정적 문법
요점: Java도 var가 있지만 함수 밖에 함수 정의 안 되고, top-level 변수 불가, 빌드 단위가 무겁다는 점에서 Dart는 좀 더 가볍고 유연한 언어야.

StatelessWidget vs StatefulWidget

항목 StatelessWidget StatefulWidget
정의 변하지 않는 UI 변할 수 있는 UI
예시 로고, 버튼, Text 카운터, 체크박스, TextField
상태(state) 없음 있음 (setState로 변경 가능)
재렌더링 직접 다시 빌드해야 함 상태 변경 시 자동으로 빌드

 

🔸 예제 비교

✅ StatelessWidget

class HelloWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text('Hello World');
  }
}
  • build() 한 번 실행되고 끝.
  • 텍스트 바꿔도 다시 그리고 싶으면 다시 앱을 실행해야 함.

✅ StatefulWidget

class CounterWidget extends StatefulWidget {
  @override
  _CounterWidgetState createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int count = 0;

  void _increment() {
    setState(() {
      count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Count: $count'),
        ElevatedButton(
          onPressed: _increment,
          child: Text('Increment'),
        ),
      ],
    );
  }
}
  • setState()를 호출하면 UI가 자동으로 다시 빌드됨.
  • Java에는 없는 개념인데, React의 상태 변경 개념과 비슷해요.

 

2️⃣ 위젯 트리 구조란?

Flutter 앱은 전부 위젯으로 구성되어 있고, 이 위젯들이 트리 구조로 계층적으로 쌓여 있어요.

예시 구조:

MaterialApp
 └── Scaffold
     ├── AppBar
     │   └── Text('타이틀')
     └── Body
         └── Center
             └── Column
                 ├── Text('안녕')
                 └── ElevatedButton

 

🔸 구조적으로 이해해야 하는 이유:

  • 모든 UI 요소는 위젯 (심지어 Padding, Margin, 정렬도 위젯)
  • 부모-자식 관계가 명확해야 레이아웃이 깨지지 않음
  • 예: Center → Column → Text + Button 같이 꼭 중첩 구조로 감싸야 함

🔁 Dart와 Flutter 개념 구분 정리

구분 Dart Flutter
역할 프로그래밍 언어 UI 프레임워크
특징 Java와 유사 위젯 기반
문법 변수, 조건문, 클래스 Stateless/StatefulWidget, 위젯 트리
런타임 CLI나 VM에서 실행 가능 모바일 UI에서 실행 (Hot Reload 지원)

 

 

✅ DartPad 실습: “숫자 증가 앱”

https://dartpad.dev/

 

DartPad

 

dartpad.dev

 

코드👇

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: CounterApp());
  }
}

class CounterApp extends StatefulWidget {
  @override
  _CounterAppState createState() => _CounterAppState();
}

class _CounterAppState extends State<CounterApp> {
  int count = 0;

  void _increment() {
    setState(() {
      count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Flutter Counter')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('버튼을 누른 횟수:'),
            Text('$count', style: TextStyle(fontSize: 32)),
            ElevatedButton(
              onPressed: _increment,
              child: Text('증가'),
            ),
          ],
        ),
      ),
    );
  }
}

 

해석

더보기
import 'package:flutter/material.dart';

👉 **Flutter에서 기본으로 제공하는 UI 위젯(Material Design)**을 쓰기 위한 패키지 import.

 material.dart에는 Text, Column, Scaffold, AppBar, ElevatedButton 같은 모든 기본 UI 요소가 들어있음.

 

void main() => runApp(MyApp());
  • ✔️ main()은 Dart에서 프로그램이 시작되는 지점. Java랑 동일.
  • 👉 =>는 Dart의 **람다식(화살표 함수)**.
  • 즉, void main() { runApp(MyApp()); }과 같은 뜻!
  • 📌 runApp()화면에 보여줄 첫 번째 위젯(앱의 시작점)을 실행하는 Flutter 함수.
  • 여기서는 MyApp()이라는 클래스를 실행하고 있음.
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: CounterApp());
  }
}

 

👉 MyApp 클래스는 화면의 전체 뼈대를 구성하는 위젯.

  • StatelessWidget: 상태(state)가 없는 정적인 위젯
  • build() 함수: Flutter에서 위젯을 그리는 함수. UI가 여기서 만들어짐.
  • BuildContext: 현재 위젯이 앱 트리에서 어디에 있는지 알려주는 객체 
  • MaterialApp: 앱 전체를 감싸주는 껍데기 같은 것.
    • home: 앱을 실행하면 가장 처음 보여줄 화면을 지정하는 속성
    • 여기선 CounterApp()이라는 화면을 보여주고 있음.
class CounterApp extends StatefulWidget {
  @override
  _CounterAppState createState() => _CounterAppState();
}

 

👉 상태가 바뀌는 위젯을 만들려면 StatefulWidget을 상속받아야 함.

  • StatefulWidget = 버튼 클릭 등으로 UI가 바뀌는 위젯
  • createState()에서 실제 동작을 처리하는 State 클래스를 연결해줌
  • 앞에 _ 붙은 _CounterAppState는 private 클래스라는 뜻 (Dart 규칙)
class _CounterAppState extends State<CounterApp> {
  int count = 0;
  • State<CounterApp>: 이 클래스는 CounterApp이 보여줄 상태를 관리하는 곳
  • count = 0: 현재 숫자 값 (초기값은 0)
  void _increment() {
    setState(() {
      count++;
    });
  }
  • _increment(): 버튼을 누르면 실행될 함수
  • setState(): 내부 상태(count)를 바꾼 후 UI를 다시 그려주는 함수
    • 이게 있어야 화면에 숫자가 즉시 바뀜
  @override
  Widget build(BuildContext context) {
    return Scaffold(
  • build()는 화면을 다시 그릴 때마다 호출됨
  • Scaffold: 앱의 기본 구조 (화면 틀)
    • 기본적으로 appBar, body, floatingActionButton 등을 포함할 수 있음
      appBar: AppBar(title: Text('Flutter Counter')),
  • appBar: 화면 상단에 나오는 제목줄
  • AppBar: 위젯 타입, 그 안에 Text() 넣어서 타이틀을 설정
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
  • body: 화면 본문
  • Center: 중앙 정렬 위젯 (body 전체를 가운데 정렬)
  • Column: 위아래로 쌓는 레이아웃 (HTML의 div 같음)
  • mainAxisAlignment: MainAxisAlignment.center: 위젯들을 세로 중앙에 배치하겠다는 뜻
          children: [
            Text('버튼을 누른 횟수:'),
            Text('$count', style: TextStyle(fontSize: 32)),
            ElevatedButton(
              onPressed: _increment,
              child: Text('증가'),
  • children: Column 아래에 들어갈 자식 위젯 리스트
    • Text(): 글자 표시
    • '$count': Dart에서 문자열에 변수 끼워 넣을 때 $변수명
    • ElevatedButton: 클릭 가능한 버튼
      • onPressed: 눌렀을 때 실행할 함수 연결
      • child: 버튼 안에 들어갈 텍스트 (여기선 ‘증가’)

 

 

✅ 위 코드에서 중요한 구조 포인트

  • Stateless → Stateful 전환: StatefulWidget + setState()
  • UI 변경 로직이 자동 반영
  • Hot Reload로 즉시 결과 확인 가능 (DartPad도 가능)

✅ 2위젯 트리 직접 만들어보기

코드:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('위젯 트리 연습')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('안녕하세요!', style: TextStyle(fontSize: 24)),
              SizedBox(height: 20),
              Image.network(
                'https://flutter.dev/images/flutter-logo-sharing.png',
                width: 100,
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: () {
                  print('버튼 눌림!');
                },
                child: Text('클릭'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

 

🔍 위젯 트리 시각화

MaterialApp
 └── Scaffold
     ├── AppBar
     │   └── Text('위젯 트리 연습')
     └── Body
         └── Center
             └── Column
                 ├── Text('안녕하세요')
                 ├── Image.network
                 └── ElevatedButton

 

👉 직접 해볼 수 있는 응용 미션

  1. Text 위젯을 하나 더 추가해서 자기 이름 출력
  2. Image.network → 다른 이미지 링크로 바꾸기
  3. 버튼을 눌렀을 때 숫자 증가하게 만들기 (Stateful로 전환해야 함)