📌 Day 5: Dart & Flutter 기초부터 실전까지! 기본 UI 위젯 (Row, Column, Stack, ListView) 완전 정

2025. 2. 24. 12:15같이 공부합시다 - Flutter/Dart & Flutter 기초부터 실전까지

728x90


🚀 주제

✅️ Flutter의 주요 레이아웃 위젯 확인
✅️ 이를 활용 기본적인 화면을 구성
Row(가로 배치)
Column(세로 배치)
Stack(위젯을 겹쳐 배치)
ListView(스크롤 가능한 목록 UI)
 
 

📌 소개

Row여러 위젯을 가로로 배치네비게이션 바, 아이콘 버튼 그룹
Column여러 위젯을 세로로 배치로그인 화면, 프로필 화면
Stack위젯을 겹쳐서 배치프로필 이미지 + 이름, 배경 위에 텍스트
ListView스크롤 가능한 리스트채팅 목록, 게시물 리스트

 
 
 
 
 
 


1️⃣ Row (가로 배치)

📌 Row는 여러 개의 위젯을 가로로 배치하는 위젯
📌 mainAxisAlignment와 crossAxisAlignment를 사용하여 정렬 가능
 

🔹 Row 기본 예제

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(
      title: 'test',
      home: HomeScreen(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('title')),
      body: Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround, // 가로 정렬
        children: [
          Container(width: 50, height: 50, color: Colors.red), // 빨간색 박스
          Container(width: 50, height: 50, color: Colors.blue), // 파란색 박스
          Container(width: 50, height: 50, color: Colors.green), // 초록색 박스
        ],
      ),
    );
  }
}

MainAxisAlignment.spaceAround: 각 아이템 사이에 같은 간격을 둠
가로 배치 시, 화면을 벗어나지 않도록 주의
 
[ mainAxisAlignment 옵션에 따른 화면 ]


 
 
 
축(Axis) 개념

  • Main Axis (주축) → Row에서는 가로, Column에서는 세로
  • Cross Axis (교차축) → Row에서는 세로, Column에서는 가로

 
 
 
정렬 방식 비교

속성Row(가로)Column(세로)
mainAxisAlignment좌/우 정렬위/아래 정렬
crossAxisAlignment위/아래 정렬좌/우 정렬
   

 
 
 
 
 
 


2️⃣ Column (세로 배치)

📌 Column은 여러 개의 위젯을 세로로 배치하는 위젯
📌 mainAxisAlignment와 crossAxisAlignment로 정렬 가능
 

🔹 Column 기본 예제

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(
      title: 'test',
      home: HomeScreen(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('title')),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: [
          Container(width: 50, height: 50, color: Colors.red),
          Container(width: 50, height: 50, color: Colors.blue),
          Container(width: 50, height: 50, color: Colors.green),
        ],
      ),
    );
  }
}

mainAxisAlignment.center: 세로 방향으로 중앙 정렬
 
[ mainAxisAlignment 옵션에 따른 화면 ]


 
 
 
 
 


3️⃣ Stack (위젯 겹치기)

📌 Stack은 여러 개의 위젯을 겹쳐서 배치하는 위젯
📌 Positioned 위젯을 사용하여 특정 위치에 배치 가능
 

🔹 Stack 기본 예제

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(
      title: 'test',
      home: HomeScreen(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('title')),
      body: Stack(
        children: [
          Positioned(
              top: 110,
              left: 110,
              child: Container(width: 150, height: 150, color: Colors.green)),
          Positioned(
              top: 100,
              left: 100,
              child: Container(width: 150, height: 150, color: Colors.blue)),
          Positioned(
              top: 90,
              left: 90,
              child: Container(width: 150, height: 150, color: Colors.red)),
        ],
      ),
    );
  }
}

위젯을 겹쳐서 배치할 때 사용
Positioned을 활용하여 특정 위치 지정 가능
 
[ Stack 테스트 ]


 
 
 
 
 


4️⃣ ListView (스크롤 가능한 리스트)

📌 ListView는 화면이 넘칠 경우 스크롤 가능한 리스트를 만드는 위젯
📌 ListView.builder()를 사용하여 동적인 리스트 생성 가능
 

🔹 ListView 기본 예제

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(
      title: 'test',
      home: HomeScreen(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('title')),
      body: ListView(
        children: [
          Card(
            elevation: 8.0,
            child: ListTile(
              leading: Icon(Icons.person),
              title: Text('tester'),
              subtitle: Text('Flutter 개발자'),
            ),
          ),
          Card(
            elevation: 8.0,
            child: ListTile(
              leading: Icon(Icons.person),
              title: Text('tester'),
              subtitle: Text('Flutter 개발자'),
            ),
          ),
          Card(
            elevation: 8.0,
            child: ListTile(
              leading: Icon(Icons.person),
              title: Text('tester'),
              subtitle: Text('Flutter 개발자'),
            ),
          ),
          Card(
            elevation: 8.0,
            child: ListTile(
              leading: Icon(Icons.person),
              title: Text('tester'),
              subtitle: Text('Flutter 개발자'),
            ),
          ),
          Card(
            elevation: 8.0,
            child: ListTile(
              leading: Icon(Icons.person),
              title: Text('tester'),
              subtitle: Text('Flutter 개발자'),
            ),
          ),
          Card(
            elevation: 8.0,
            child: ListTile(
              leading: Icon(Icons.person),
              title: Text('tester'),
              subtitle: Text('Flutter 개발자'),
            ),
          ),
          Card(
            elevation: 8.0,
            child: ListTile(
              leading: Icon(Icons.person),
              title: Text('tester'),
              subtitle: Text('Flutter 개발자'),
            ),
          ),
          Card(
            elevation: 8.0,
            child: ListTile(
              leading: Icon(Icons.person),
              title: Text('tester'),
              subtitle: Text('Flutter 개발자'),
            ),
          ),
          Card(
            elevation: 8.0,
            child: ListTile(
              leading: Icon(Icons.person),
              title: Text('tester'),
              subtitle: Text('Flutter 개발자'),
            ),
          ),
        ],
      ),
    );
  }
}

ListTile을 활용하면 간단한 리스트 아이템을 쉽게 구성 가능
스크롤이 자동으로 적용됨
 
[ ListView 테스트 ]


 
 
 
📌 ListView와 Column의 차이점

속성ColumnListView
스크롤 가능 여부❌ 불가능✅ 가능
성능❌ 아이템이 많으면 성능 저하✅ 필요할 때만 렌더링
사용 사례정적인 UI 배치긴 목록 데이터 표시
   

 
💡 많은 데이터를 표시할 때는 ListView를 사용해야 성능이 좋음!
 
📌 많은 데이터를 다룰 때는 ListView.builder()를 사용하는 것이 더 효율적. (렌더링 성능 최적화)

ListView.builder(
  itemCount: 10, // 10개의 아이템 생성
  itemBuilder: (context, index) {
    return Card(
      elevation: 4.0,
      child: ListTile(
        leading: Icon(Icons.person),
        title: Text("사용자 ${index + 1}"),
        subtitle: Text("Flutter 개발자"),
      ),
    );
  },
)

 
 
 
 
 
 


5️⃣ Row, Column, Stack, ListView 조합 테스트

 

🔹 예제

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(
      debugShowCheckedModeBanner: false,
      home: const ProfileScreen(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('프로필 화면'),
        backgroundColor: Colors.teal,
      ),
      body: Column(
        children: [
          // 📌 1. Stack을 이용해 프로필 사진과 이름을 겹쳐 배치
          Stack(
            alignment: Alignment.center,
            children: [
              Container(
                width: double.infinity,
                height: 150,
                color: Colors.teal.withValues(alpha: 0.3), // 배경색 효과
              ),
              Column(
                children: [
                  ClipOval(
                    child: Image.network(
                      '<https://picsum.photos/600/400>',
                      width: 80,
                      height: 80,
                      fit: BoxFit.cover,
                    ),
                  ),
                  const SizedBox(height: 8),
                  const Text(
                    'SteadyBuilder',
                    style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                  ),
                ],
              ),
            ],
          ),

          // 📌 2. Row를 활용해 버튼 배치
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 20),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                ElevatedButton(
                  onPressed: () {},
                  child: const Text('메시지 보내기'),
                ),
                ElevatedButton(
                  onPressed: () {},
                  child: const Text('✔팔로우'),
                ),
              ],
            ),
          ),

          // 📌 3. ListView + Expanded를 활용해 스크롤 가능한 목록 UI
          Expanded(
            child: ListView(
              padding: const EdgeInsets.all(10),
              children: List.generate(
                10,
                (index) => Card(
                  elevation: 4.0,
                  margin: const EdgeInsets.symmetric(vertical: 5),
                  child: ListTile(
                    leading: const Icon(Icons.article),
                    title: Text('게시물 ${index + 1}'),
                    subtitle: const Text('이것은 게시물 설명입니다.'),
                    trailing: const Icon(Icons.arrow_forward_ios, size: 16),
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

 
[ 조합 테스트 결과 ]


 
 
 
 
 


📌 오늘의 실습 과제

[실습 1] Row를 사용하여 아이콘을 가로로 배치하는 UI 만들기
[실습 2] Column을 사용하여 텍스트와 버튼을 세로로 배치하는 UI 만들기
[실습 3] ListView를 사용하여 이름과 직업이 표시되는 리스트 UI
[실습 4] Stack을 활용하여 이미지 위에 텍스트를 배치하는 UI 만들기

728x90