Flutter 예제 살펴보기

김원기·2024년 3월 18일

Flutter & iOS

목록 보기
1/4

뭐 Flutter를 살펴봐야 하니까 예제부터 보기로 했다.
SwiftUI랑 선언적 UI 방식이 비슷하다고 하니까 대충 되겠지 하고 찾아보는데 생각보다 쉽지는 않은것 같다.

import 'package:flutter/material.dart';

//앱의 시작 부분 main이다.
void main() { 
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key}); // 부모 클래스의 생성자를 호출하는 부분 

  @override
  Widget build(BuildContext context) { 
    return MaterialApp(
      title: 'Flutter Demo', 
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), 
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'), 
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title}); 

  final String title; 

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center( 
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), 
    );
  }
}

위는 예제의 소스코드 전문이고, 하나씩 소스코드를 살펴보자면

헤더

import 'package:flutter/material.dart';

해당 코드는 flutter에서 material design을 구현하는데 사용 되는데Material Design이 뭔가 찾아보니

  • '플랫 디자인의 장점을 살리면서도 빛에 따른 종이의 그림자 효과를 이용하여 입체감을 살리는 디자인.'
  • 'Google이 제안한 새로운 디자인 시스템으로, 모바일과 웹 앱의 일관성 있는 디자인을 제공.'
  • '해당 패키지는 버튼, 텍스트, 아이콘, 레이아웃 등의 위젯이 포함되어 있음.'

이라는 특징을 가지고있다. (이거 UIKit 아니냐...)

main()

void main() { 
  runApp(const MyApp());
}

다음으로는 main이다. runApp()이라는 메서드에 MyApp()을 인자로 넘겨준다.

StatelessWidget()

class MyApp extends StatelessWidget {
  const MyApp({super.key}); 

  @override
  Widget build(BuildContext context) { 
    return MaterialApp(
      title: 'Flutter Demo', 
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), 
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'), 
    );
  }
}

처음 봤을때는 여기서부터 멘붕이었달까... 일단 살펴봤을때 눈에 띄는건 StatelessWidget은 뭐지? 였다.

StatelessWidget의 특징은 아래와 같다.

  • '정적 위젯, 화면이 로드될 때 한번만 그려지며, 변경이 필요한 데이터가 없음.'
  • '사용자 상호 작용이 필요하지 않은 구성요소를 구성할 때 사용.'

음.... 일단 넘어가서 다시 분석해보자.

class MyApp extends StatelessWidget

해당 줄은 Flutter앱의 루트 위젯을 나타내며, StatelessWidget을 상속하기에 해당 위젯이
변경이 불가능한 상태를 가진다 는 의미이다.

const MyApp({super.key}); 

super.key는 부모 클래스의 속성을 참조하는 부분이다.

@override
  Widget build(BuildContext context) { 
    return MaterialApp(
      title: 'Flutter Demo', 
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), 
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'), 
    );
  }

해당 부분이 StatelessWidget의 Widget 속성을 오버라이드 부분이라고 생각한다.
정확히는 bulid 메서드를 오버라이드 하여 화면을 구성한다. (뭐 대충 맞겠지..)

Widget은 MaterialApp을 return하는데

  • MaterialApp은 플러터 앱의 기본 구조를 설정하는 위젯
  • title은 앱의 타이틀
  • theme은 앱의 테마
  • home은 앱이 시작될 때 표시할 기본 화면을 지정한다.

그러면 MyHomePage는 뭘까

StatefulWidget()

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title}); 

  final String title; 

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

얘는 이번엔 StatefulWidget을 상속 받았다
StatefulWidget은 아래와 같은 특징을 가지고있다.

  • 화면이 동작하는 동안 데이터 변경이 필요한 경우 사용된다.
  • 사용자 상호 작용이나 이벤트에 따라 화며을 다시 그리고 변경된 부분을 반영한다.
  • 버튼, 텍스트 필드, 체크박스등 사용자 상태에 따라 변화해야 하는 요소를 만들 때 주로 사용된다.

특징을 보면 알 수 있는듯이 혹은 Stateful과 less의 차이를 보면 알듯이 동적인 요소를 구성하는데 사용되는 위젯이다.

iOS를 공부했던 시점으로 보자면

  • StatelessWidget은 iOS의 View와 유사한 점을 가진다.
  • StatefulWidget은 iOS의 ViewController를 가진다.

이정도로 정리할 수 있을것 같다. (자세한 내용은 위를 참고하면 확실하겠지?)

const MyHomePage({super.key, required this.title}); 

부모 클래스의 속성을 참조함과 동시에 홈 페이지의 타이틀을 나타낸다.

@override
  State<MyHomePage> createState() => _MyHomePageState();

createState()라는 부모 클래스의 메서드를 참조하여 오버라이드 하는 과정이다. 정확하게는 State라는 속성의 객체를 생성하는 과정인데 State객체는
'화면의 상태를 관리하고 상태 변화에 따라 화면을 다시 그린다.' 특징을 가진다.

그런데 State를 보니 SwiftUI의 상태 관리가 생각나는 부분이다.

잠깐 딴길로 새서 두 개념을 정리해보자면

iOS & Flutter

State (Flutter)

  • 위젯의 상태를 나타냄
  • StatefulWidget에서 사용되며, 화면이 동작하는 동안 변경이 필요한 데이터 관리
  • 예를 들어 버튼이 눌리면 화면이 전환되거나, 텍스트 필드의 내용이 변경되는 경우에 사용

@State (SwiftUI - View)

  • SwiftUI에서 사용하는 프로퍼티 래퍼
  • View의 상태를 관리하는 데 사용
  • @State로 선언된 프로퍼티는 값이 변경될 때 해당 뷰가 다시 그려지도록 함

@Published (SwiftUI - ViewModel)

  • SwiftUI에서 사용하는 프로퍼티 래퍼
  • ObservableObject 프로토콜을 준수하는 클래스에서 사용
  • @Published로 선언된 프로퍼티는 값이 변경될 때 해당 뷰가 다시 그려지도록 하고, 관련된 뷰를 업데이트합니다

한줄 요약) 상태 관리를 위해 사용되며 변경될 때 화면도 같이 바뀐다~

State Class

다시 코드를 보자 (생각보다 예제인데 할게 많다..)

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center( 
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), 
    );
  }
}

_MyHomgPageState 클래스는 State<MyHomePage>를 상속 받았으니
변경 가능한 상태를 가지는 '상태 클래스'

Scafflod()는 Material 디자인 앱의 뼈대가 되는 위젯으로 MetarialApp -> Scafflod가 기본 형태

Scafflod의 인자로
Appbar, Body, floatingActionButton이 인자값으로 들어왔다.
그렇지만 Scafflod는 Appbar나 bottomNavigationBar,
floatingActionButton 같은 인자는 필수 값이 아니지만 Body는 반드시 필요하다
(없으면 앱 왜만듬 ㅋㅋ)

appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),

title은 Widget의 title을 받게되는데 Widget이 여러개가 있으니 헷갈릴만 하다.

참조하게 되는 위젯은 StatefulWidget에서 StatelessWidget의 홈페이지 타이틀을 반영했고, StatefulWidget을 상속받아 생성됐던 객체를 상속받은...

(말이 왜이래 이거)

여튼 Widget은 StatefulWidget의 프로퍼티를 참조하는데 해당값이 StatelessWidget에서 선언되었다.
굳이 바꾸고자 한다면

title: Text("blah~blah~")

backgroundColor는 StatelessWidget에서 설정해둔 색상을 반영하는것 같은데..

inversePrimary에 대한 설명을 보면
An accent color used for displaying a highlight color on [inverseSurface] backgrounds, like button text in a SnackBar. 라고함
근데 읽어도 모르겠음.... 다음에 알아보도록 하자...

body: Center( 
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),

이부분이 실질적으로 앱이 보여주고자 하는 부분을 구성하는 곳이라 생각함
(뭐... html이든 뭐든 body에서 내용 넣으니까 맞지 않을까... 이것도 아님 말고..)

일단 body의 속성과 특징 대해 알아왔다.
특징을 먼저 말하자면

  • 레이아웃 위젯이라고 한다.
  • 하나의 child와 position을 가진다.

속성은

  • Center: 부모 뷰의 중간에 위치하여 child를 생성한다.
  • SizedBox.expand: 이건 설명 보면서 알게된건데 부모 뷰가 허락하는 만큼? 크게 child를 생성한다.

child 특징

  • 얘 또한 레이아웃 위젯이다.
  • 얘는 SwiftUI의 HStack이나 VStack하고 비슷한 느낌이다.

속성

  • Column : child의 좌우 크기는 하위 뷰에 딱 맞게 그려지며 상하는 부모뷰와 딱 맞게 그려지는것 같다.
  • Row : Column과 반대로 좌우는 부모뷰 상하는 하위 뷰와 맞게 그려지는것 같다.

그외에도 Column이나 Row가 가지는 하위 속성은 화면 구성을 제대로 해볼 때 조금 더 자세하게 작성하도록 하겠다...

children은 child의 하위 뷰

마지막으로 floatingActionButton인데

floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),

그냥 버튼하고 함수 만들고 버튼에 함수 기능하고 아이콘으로 +기호 넣은거임...

다음은 포스팅은 아마
StatefulWidget, StatelessWidget하고
Swift, SwiftUI 비교나 비슷한거 분석 및 정리가 되지 싶다.

끝!

(후기 1)
처음 Flutter, Dart 구조 보면서 iOS개념도 살리면서 작성하느라 힘들긴 해도 익숙해지면 괜찮겠지...?

(후기 2)
UI 구성부터 기능 구현은 어떻게 하는거지....

profile
혼자 공부하는 블로그라 부족함이 많아요 https://www.notion.so/18067a27ac7e4f4790dde645fb3bf3d3?pvs=4

0개의 댓글