๊ด€๋ฆฌ ๋ฉ”๋‰ด

ruriruriya

[Flutter] ์œ„์ ฏ์— ๋Œ€ํ•ด์„œ(Widget) ๋ณธ๋ฌธ

๐Ÿ“ฑFlutter/Flutter Framework

[Flutter] ์œ„์ ฏ์— ๋Œ€ํ•ด์„œ(Widget)

๋ฃจ๋ฆฌ์•ผใ…‘ 2025. 1. 15. 23:19
๋ฐ˜์‘ํ˜•

ํ”Œ๋Ÿฌํ„ฐ์—์„œ ์œ„์ ฏ(Widget)์ด๋ž€?

ํ”Œ๋Ÿฌํ„ฐ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๊ธฐ๋ณธ ์š”์†Œ์ด๋‹ค.
๊ฐ ์œ„์ ฏ์€ ํ…์ŠคํŠธ๋‚˜ ๋ฒ„ํŠผ ๊ฐ™์€ ๋ฌผ๋ฆฌ์  ์š”์†Œ๋ถ€ํ„ฐ ๋ ˆ์ด์•„์›ƒ ํšจ๊ณผ๊นŒ์ง€ ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ชจ๋“  ์ธก๋ฉด์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ณณ์— ์‚ฌ์šฉ๋œ๋‹ค.

์œ„์ ฏ ๊ณ„์ธต ๊ตฌ์กฐ

์œ„์ ฏ์€ ๊ตฌ์„ฑ(Composition)์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ํ˜•์„ฑํ•œ๋‹ค.
๊ฐ ์œ„์ ฏ์€ ๋ถ€๋ชจ ์œ„์ ฏ ๋‚ด๋ถ€์— ์ค‘์ฒฉ๋˜๋ฉฐ, ๋ถ€๋ชจ๋กœ๋ถ€ํ„ฐ Context๋ฅผ ์ „๋‹ฌ ๋ฐ›๋Š”๋‹ค.
์ด ๊ตฌ์กฐ๋Š” ๋ฃจํŠธ ์œ„์ ฏ๊นŒ์ง€ ์—ฐ๊ฒฐ๋œ๋‹ค.

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp( // ๋ฃจํŠธ ์œ„์ ฏ
      home: Scaffold(
        appBar: AppBar(
          title: const Text('My Home Page'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Text('Hello, World!'),
              const SizedBox(height: 20),
              ElevatedButton(
                onPressed: () {
                  print('Click!');
                },
                child: const Text('A button'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

 

์œ„์ ฏ ๊ตฌ์„ฑ(Widget Composition)

ํ”Œ๋Ÿฌํ„ฐ๋Š” ๋ชจ๋“  ๊ฒƒ์ด ์œ„์ ฏ์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.
์ž‘์€ ๋‹จ์œ„์˜ ์œ„์ ฏ์„ ์—ฌ๋Ÿฌ ๊ฐœ ์กฐํ•ฉํ•ด์„œ UI๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.

๋ ˆ์ด์•„์›ƒ ์œ„์ ฏ(Padding, Alignment, Row, Column, Grid ๋“ฑ)

์ด๋Ÿฐ ์œ„์ ฏ์€ ๋””์ž์ธ ์š”์†Œ๊ฐ€ ์—†์ด ๋ ˆ์ด์•„์›ƒ์„ ๋‹ด๋‹นํ•œ๋‹ค. ํ˜•ํƒœ๋ผ๊ธฐ ๋ณด๋‹ค ๊ณต๊ฐ„์ด๋ผ๊ณ  ๋ณด๋ฉด ์ข€๋” ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.

์œ ํ‹ธ๋ฆฌํ‹ฐ ์œ„์ ฏ(Container ๋“ฑ)

์ด ์œ„์ ฏ์€ ๋””์ž์ธ ์ง€์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค. 
๋ ˆ์ด์•„์›ƒ, ํŽ˜์ธํŒ…, ์œ„์น˜ ์ง€์ •, ํฌ๊ธฐ ์กฐ์ • ๋“ฑ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ๊ฒฐํ•ฉํ•œ ๋‹ค๋ชฉ์  ์œ„์ ฏ์ด๋‹ค.

๋””์ž์ธ ์ ์šฉ ๊ฐ€๋Šฅ ์œ„์ ฏ(ElevateButton, Text, Icon, Image ๋“ฑ)

์ด ์œ„์ ฏ๋“ค์€ ์œ„์ ฏ ๋‚ด์˜ ์†์„ฑ์œผ๋กœ css์ฒ˜๋Ÿผ ์„ธ์„ธํ•œ ๋””์ž์ธ ์ ์šฉ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

-

์•„๋ž˜ ์ด๋ฏธ์ง€๋ฅผ ์„ค๋ช…ํ•˜์—ฌ ์ดํ•ด๋ฅผ ๋•์ž๋ฉด
Center ์œ„์ ฏ ์•„๋ž˜์— Column ์œ„์ ฏ์ด ๋“ค์–ด๊ฐ€๊ณ 
Column ์œ„์ ฏ์€ ๋ฆฌ์ŠคํŠธ ํ˜•์‹์œผ๋กœ ์œ„์ ฏ์„ ๋‹ค์ˆ˜ ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค.
๊ทธ๋ž˜์„œ Column ์œ„์ ฏ ์•ˆ์— Text, SizedBox, Button ์œ„์ ฏ์„ ์„ธ๋กœ๋กœ ๋ฐฐ์น˜ํ–ˆ๋‹ค.

Center ์œ„์ ฏ ์•„๋ž˜์— ์žˆ์–ด์„œ ๊ฐ€์šด๋ฐ ์ •๋ ฌ์ด ๋œ๋‹ค.

์œ„์ ฏ ์ƒ์„ฑ(Building Widgets)

ํ”Œ๋Ÿฌํ„ฐ์—์„œ UI๋ฅผ ๊ตฌํ˜„ํ•˜๋ ค๋ฉด ์œ„์ ฏ ๊ฐ์ฒด์˜ build ๋ฉ”์„œ๋“œ๋ฅผ ์žฌ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค.
๋ชจ๋“  ์œ„์ ฏ์€ build ๋ฉ”์„ธ๋“œ๋ฅผ ๊ฐ€์ ธ์•ผ ํ•˜๊ณ , ์ด ๋ฉ”์„œ๋“œ๋Š” ๋‹ค๋ฅธ ์œ„์ ฏ์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค.

build ๋ฉ”์„œ๋“œ ํŠน์ง•

  • ์œ„์ ฏ์ด ์ƒ์„ฑ๋˜๊ฑฐ๋‚˜ ์˜์กด์„ฑ์ด ๋ณ€๊ฒฝ๋  ๋•Œ ํ˜ธ์ถœ๋œ๋‹ค.
  • ์ด ๋ฉ”์„œ๋“œ๋Š” ์œ„์ ฏ์„ ์ƒ์„ฑํ•  ๋•Œ๋งŒ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

์˜ˆ์‹œ ์ฝ”๋“œ

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

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: const Text('Hello, World!'),
    );
  }
}

 

์œ„์ ฏ ์ƒํƒœ(Widget State)

ํ”Œ๋Ÿฌํ„ฐ์—์„œ ์œ„์ ฏ ์ƒํƒœ๋Š” ์ฃผ์š” ๋‘๊ฐ€์ง€ ํด๋ž˜์Šค๊ฐ€ ์žˆ๋‹ค.

  • StatelessWidget: ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ์ƒํƒœ๋ฅผ ๊ฐ€์ง„ ์œ„์ ฏ
  • StatefulWidget: ์‚ฌ์šฉ์ž ์ƒํ˜ธ์ž‘์šฉ์ด๋‚˜ ๋‹ค๋ฅธ ์š”์ธ์— ๋”ฐ๋ผ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋Š” ์œ„์ ฏ

StatefulWidget์€ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๊ณ , ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค setState๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ UI๋ฅผ ์—…๋ฐ์ดํŠธ ํ•œ๋‹ค.

์•„๋ž˜๋Š” ๊ฐ„๋‹จํ•œ StatefulWidget ์นด์šดํ„ฐ ์˜ˆ์‹œ์ด๋‹ค.
์ƒํƒœ๋Š” State์— ์ €์žฅ๋˜๊ณ 
setState()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด, ํ”Œ๋Ÿฌํ„ฐ๋Š” ํ•ด๋‹น ์œ„์ ฏ์˜  build ๋ฉ”์„œ๋“œ๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœํ•ด UI๋ฅผ ์—…๋ฐ์ดํŠธ ํ•œ๋‹ค.

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

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

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

  @override
  Widget build(BuildContext context) {
    return Text('$_counter');
  }
}

 

 

๋ฐ˜์‘ํ˜•