[Flutter] 스토어 앱 만들기

귤's avatar
May 28, 2025
[Flutter] 스토어 앱 만들기
📂
예제 준비

1. 플러터 프로젝트 생성

notion image
notion image
page icon
  • 이름 생성할 때 소문자만 가능 (언더스코어 기법)
  • 절대 아래는 쓰면 안됨 → 오류 발생 가능성 ⚠️
  • 대문자 X
  • “-” 안됨
New 윈도우로 하기 → 버그 발생함
New 윈도우로 하기 → 버그 발생함
page icon
  • 코드 작성은 여기서 하는 거임!! (lib → main.dart)
notion image

1. runApp 말고 다 지우기

  • const도 지금은 지우기
notion image

2. MyApp = 위젯

page icon
  • stl 입력 후 자동 완성으로 코드 생성 → MyApp 추가
  • 여기서 MyApp은 위젯임
notion image
  • 아직 const 안 배워서 지우기
notion image
  • 생성자도 우선 지워 놓기 (선택적 매개변수 this.key 들고 있는 거, super.key)
notion image

3. 이게 위젯 (사진 대신 넣어줌)

import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return Placeholder(); // 이 리턴 값으로 그림을 그린다 } }
notion image

2. 화면 구조 보기

💡
  • 무조건 MaterialAPPScaffold로 감싸야 한다!!!

1. 화면 구조

🖥️
  • Column 위젯 = 수직, 자식을 여러 개 가질 수 있는 배열을 가진다
  • Row 위젯 = 수평, 여러 개를 가질 수 있는 배열을 가진다
💡
  • Row는 가로 방향으로 전체를 차지함 (Block과 같음)
  • Row의 크기는 전체
  • Row는 세로축으로
  • Column은 세로 방향으로 차지함 (inline과 같음)
  • Column의 크기는 내부의 아이템의 크기로 결정됨
  • Column은 가로축으로 inline

✅ Row와 Column의 차이 정리

📦 Row수평 방향 레이아웃 (가로로 나열)

특징
설명
방향
가로 (수평) 방향으로 자식들을 나열함
메인 축
가로축 (horizontal)
서브 축
세로축 (vertical)
기본 크기
자식들의 너비를 고려하여 부모가 허용하는 전체 가로 공간을 차지함 (block처럼 확장)
세로 크기
자식 위젯 중 가장 큰 높이를 기준으로 함 (세로는 자식에 따라 달라짐)
Row는 세로축으로 inline
세로 방향으로는 자식 크기만큼만 공간을 가짐 (inline처럼)

📦 Column수직 방향 레이아웃 (세로로 나열)

특징
설명
방향
세로 (수직) 방향으로 자식들을 나열함
메인 축
세로축 (vertical)
서브 축
가로축 (horizontal)
기본 크기
자식들의 높이를 기준으로 필요한 만큼만 높이 차지 (inline처럼 동작)
가로 크기
자식 위젯 중 가장 넓은 폭을 기준으로 함
Column은 가로축으로 inline
가로 방향으로는 자식 크기만큼만 공간을 가짐 (inline처럼)

🧠 쉽게 외우는 방법

개념
Row (가로 배치)
Column (세로 배치)
Block 방향
가로 방향을 꽉 채움 (Block처럼)
세로 방향을 꽉 채움 (Block처럼)
Inline 방향
세로 방향은 자식만큼만 차지 (inline처럼)
가로 방향은 자식만큼만 차지 (inline처럼)
Main Axis
Horizontal (가로)
Vertical (세로)
Cross Axis
Vertical (세로)
Horizontal (가로)

Flutter Inspector (검사, F12랑 같은 기능, 영역 확인 가능)

Row 선택했을 때
  • Row 선택 → Select Widget Mode → 실행된 웹 확인
notion image
notion image
 
Column 선택했을 때
notion image
notion image

2. MaterialApp VS CupertinoApp

📱
  • MaterialApp은 안드로이드 디자인
  • CupertinoApp은 IOS 디자인

3. Scaffold

📱
  • Flutter에서 앱 화면을 만들기 위한 기본 틀
  • Scaffold로 틀부터 만들고 그 안에 내용을 넣는 구조
  • flutter 앱에서 자주 쓰는 기본 UI 요소들이 Scaffold 안에 있음
  • 기본 UI 요소
    • 요소
      설명
      appBar
      상단 툴바(앱 제목, 메뉴 아이콘 등)
      body
      실제 화면의 주 내용
      bottomNavigationBar
      하단 탭바 메뉴
      floatingActionButton
      오른쪽 하단의 동그란 버튼 (FAB)

3. 위젯

참고 자료
Childern: [] 설명
notion image
💡
  • Children: [] : 배열 넣을 수 있음
  • 여러 위젯을 담는 위젯(Container 역할)
Childern: [] 설명
Childern: [] 설명

Text 위젯

import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // 이 리턴 값으로 그림을 그린다 return MaterialApp( // 안드로이드 디자인, CupertinoApp은 IOS 디자인 // home: 은 앱 실행 시 가장 먼저 보여줄 위젯을 설정하는 프로퍼티 home: Scaffold( body: Column( children: [ Row( children: [ Text("Women"), Text("Women"), Text("Women"), Text("Women"), ], ), ], ), ), ); } }
notion image

Spacer 위젯

💡
  • html 에서 flex와 비슷한 기능
  • 빈 공간이 생김 (남는 공간 다 차지 함, Expanded랑 기능은 같지만 특징은 다름)
  • Expanded와 다르게 자식을 가질 수 없음
이걸 보고 알아 차려야 함
이걸 보고 알아 차려야 함
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // 이 리턴 값으로 그림을 그린다 return MaterialApp( // 안드로이드 디자인, CupertinoApp은 IOS 디자인 // home: 은 앱 실행 시 가장 먼저 보여줄 위젯을 설정하는 프로퍼티 home: Scaffold( body: Column( children: [ Row( children: [ Spacer(), Text("Women"), Spacer(), Text("Women"), Spacer(), Text("Women"), Spacer(), Text("Women"), Spacer(), ], ), ], ), ), ); } }
notion image

Row → mainAxisAlignment: , 위젯

💡
  • Row 기준에서 사용
  • flex의 justify: content와 같음
notion image
mainAxisAlignment 타입
mainAxisAlignment 타입

Padding 위젯

Padding 생성하고 싶으면?
Row에 커서 올리고 Alt + Enter
notion image
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // 이 리턴 값으로 그림을 그린다 return MaterialApp( // 안드로이드 디자인, CupertinoApp은 IOS 디자인 // home: 은 앱 실행 시 가장 먼저 보여줄 위젯을 설정하는 프로퍼티 home: Scaffold( body: Column( children: [ Padding( padding: EdgeInsets.all(16.0), child: Row( mainAxisAlignment: MainAxisAlignment .spaceBetween, children: [ Text("Women"), Text("Women"), Text("Women"), Text("Women"), ], ), ), ], ), ), ); } }
자동으로 Padding으로 감싸줌
notion image
 
Padding 삭제 하고 싶으면?
💡
  • Padding에 커서 올려놓고 Alt + Enter
notion image
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // 이 리턴 값으로 그림을 그린다 return MaterialApp( // 안드로이드 디자인, CupertinoApp은 IOS 디자인 // home: 은 앱 실행 시 가장 먼저 보여줄 위젯을 설정하는 프로퍼티 home: Scaffold( body: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text("Women"), Text("Women"), Text("Women"), Text("Women"), ], ), ], ), ), ); } }

SafeArea 위젯

먼저 알아두면 좋은점 (인셋 영역)
💡
  • 모바일에는 인셋 영역이 있다
  • 엣지 인셋 영역이라고도 하는데, 그 영역은 그림을 못 그리는 구역을 말한다
  • 예) 키보드 올라오는 부분, WIFI or 데이터, 배터리, 날씨 등 표시되는 상단 부분
notion image
💡
  • SafeArea
  • 인셋 영역을 해치지 않기 위해 사용한다
Column에서 alt + enter 입력 후 Wrap with widget 선택 → SafeArea가 따로 나오지 않아서 Wrap with widget을 선택하는 거임! → 그 다음 이름만 SafeArea로 바꾸면 됨!! → 그러고 나서 다시 Row 를 Padding 으로 감싸기!!
notion image
notion image
Padding 까지 한 코드
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // 이 리턴 값으로 그림을 그린다 return MaterialApp( // 안드로이드 디자인, CupertinoApp은 IOS 디자인 // home: 은 앱 실행 시 가장 먼저 보여줄 위젯을 설정하는 프로퍼티 home: Scaffold( body: SafeArea( child: Column( children: [ Padding( padding: const EdgeInsets.all( 25.0, ), child: Row( mainAxisAlignment: MainAxisAlignment .spaceBetween, children: [ Text("Women"), Text("Women"), Text("Women"), Text("Women"), ], ), ), ], ), ), ), ); } }
notion image

Expanded 위젯

💡
  • Expanded를 하면 내부에 있는 자식까지 같이 늘어난다
  • 남는 공간을 모두 차지한다 (Spacer 위젯이랑 차이 있음)
notion image
Expanded 사용한 코드
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // 이 리턴 값으로 그림을 그린다 return MaterialApp( // 안드로이드 디자인, CupertinoApp은 IOS 디자인 // home: 은 앱 실행 시 가장 먼저 보여줄 위젯을 설정하는 프로퍼티 home: Scaffold( body: SafeArea( child: Column( children: [ Padding( padding: EdgeInsets.all(25), child: Row( mainAxisAlignment: MainAxisAlignment .spaceBetween, children: [ Text("Women"), Text("Kids"), Text("Shoes"), Text("Bag"), ], ), ), Expanded( child: Placeholder(), flex: 1, ), Expanded( child: Placeholder(), flex: 1, ), ], ), ), ), ); } }
notion image
SizeBox 사용 (= margin)
코드
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // 이 리턴 값으로 그림을 그린다 return MaterialApp( // 안드로이드 디자인, CupertinoApp은 IOS 디자인 // home: 은 앱 실행 시 가장 먼저 보여줄 위젯을 설정하는 프로퍼티 home: Scaffold( body: SafeArea( child: Column( children: [ Padding( padding: EdgeInsets.all(25), child: Row( mainAxisAlignment: MainAxisAlignment .spaceBetween, children: [ Text("Women"), Text("Kids"), Text("Shoes"), Text("Bag"), ], ), ), Expanded( child: Placeholder(), flex: 1, ), SizedBox(height: 10), Expanded( child: Placeholder(), flex: 1, ), ], ), ), ), ); } }
notion image
notion image

Image 위젯

추가 설명
💡
  • 실행 세모 버튼을 누르는 거는 -> 전부 다 집어 넣는거
  • 저장을 하는 건 그 해당 부분만 다시 넣어지는 거
yaml 설명
💡
  • yaml : 중간 언어, json 보다 가벼움 (중괄호가 없다), 경량 언어 (표준은 Json)
  • 중간 언어는 통신할 때 쓰임
  • 확장자 이름 (.yaml, .yml)
  • 배열만 띄워 쓰기만 잘해도 오류가 없음
yaml (.yaml, .yml) class User { String username="ssar"; String password="1234"; } <user> <username>ssar</username> <password>1234</password> </user> { "username":"ssar", "password":"1234" } // yaml 규칙 user: username: ssar // 'username' 앞에는 무조건 두칸 띄우기, ':' 다음엔 무조건 한 칸 띄우기 password: 1234 addr: first: 부산 // 4칸 띄우기 mid: 수영구 hobby: - 농구 // 배열 하고 싶을 때는 4칸 띄우고 '-' 쓰기 - 축구
 
사진 넣기
notion image
이미지 경로 잡기
notion image
notion image
flutter: uses-material-design: true assets: - assets/bag.jpeg - assets/cloth.jpeg
사진이 여러 장 일 때
flutter: uses-material-design: true assets: - assets/
assets 파일 안에 있는 모든 사진을 조회해줌
전체 코드
name: flutter_recipe description: "A new Flutter project." # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 # followed by an optional build number separated by a +. # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. # In Android, build-name is used as versionName while build-number used as versionCode. # Read more about Android versioning at https://developer.android.com/studio/publish/versioning # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. version: 1.0.0+1 environment: sdk: ^3.7.2 # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions # consider running `flutter pub upgrade --major-versions`. Alternatively, # dependencies can be manually updated by changing the version numbers below to # the latest version available on pub.dev. To see which dependencies have newer # versions available, run `flutter pub outdated`. dependencies: flutter: sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 dev_dependencies: flutter_test: sdk: flutter # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^5.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter packages. flutter: uses-material-design: true assets: - assets/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/to/resolution-aware-images # For details regarding adding assets from package dependencies, see # https://flutter.dev/to/asset-from-package # To add custom fonts to your application, add a fonts section here, # in this "flutter" section. Each entry in this list should have a # "family" key with the font family name, and a "fonts" key with a # list giving the asset and other descriptors for the font. For # example: # fonts: # - family: Schyler # fonts: # - asset: fonts/Schyler-Regular.ttf # - asset: fonts/Schyler-Italic.ttf # style: italic # - family: Trajan Pro # fonts: # - asset: fonts/TrajanPro.ttf # - asset: fonts/TrajanPro_Bold.ttf # weight: 700 # # For details regarding fonts from package dependencies, # see https://flutter.dev/to/font-from-package

Image.asset

💡
  • 로컬 이미지 사용 (assets/xxx 경로의 이미지 파일)
  • fit: BoxFit.cover: 이미지를 공간에 꽉 채우되, 비율 유지하며 잘라냄

SizedBox(height: 2)

💡
  • 두 이미지 사이에 2픽셀 간격을 만들어줌

Expanded

💡
  • Column/Row 등 Flex 계열 위젯 안에서 사용
  • 남아 있는 공간을 비율(flex)에 따라 나누어 차지함
  • 여기서는 두 개의 Expandedflex: 1씩 설정되어 있음 → 공간을 1:1로 나눔
  • 즉, bag.jpegcloth.jpeg는 세로로 화면을 반씩 나눠서 보여줌
Expanded( child: Image.asset( "assets/bag.jpeg", fit: BoxFit.cover, ), // 이름있는 생성자, 나중에 네트워크로 넣으려면 Image.netWork 해야함 flex: 1, ), SizedBox(height: 2), Expanded( child: Image.asset( "assets/cloth.jpeg", fit: BoxFit.cover, ), flex: 1, ),
notion image
요소
설명
Expanded
공간을 flex 비율로 나누기 (반응형 레이아웃 핵심)
Image.asset
로컬 이미지 출력
BoxFit.cover
이미지 비율 유지하면서 공간에 꽉 채움 (잘림 가능)
SizedBox
위젯 사이 간격 조절용
⚠️ 주의
이렇게 넣으면 안 됨! 지금은 문법이 바뀌어서 가능한데 그래도 가급적이면 사용하지 않기
이렇게 넣으면 안 됨! 지금은 문법이 바뀌어서 가능한데 그래도 가급적이면 사용하지 않기
네트워크 이미지로 바꾸고 싶으면?
  • 이런 식으로 사용하면 됨
Image.network("https://example.com/image.jpg")

4. 완성

import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // 이 리턴 값으로 그림을 그린다 return MaterialApp( // 안드로이드 디자인, CupertinoApp은 IOS 디자인 // home: 은 앱 실행 시 가장 먼저 보여줄 위젯을 설정하는 프로퍼티 home: Scaffold( body: SafeArea( child: Column( children: [ Padding( padding: EdgeInsets.all(25), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text("Women", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), Text("Kids", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), Text("Shoes", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), Text("Bag", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), ], ), ), Expanded( child: Image.asset( "assets/bag.jpeg", fit: BoxFit.cover, ), // 이름있는 생성자, 나중에 네트워크로 넣으려면 Image.netWork 해야함 flex: 1, ), SizedBox(height: 2), Expanded( child: Image.asset( "assets/cloth.jpeg", fit: BoxFit.cover, ), flex: 1, ), ], ), ), ), ); } }
notion image

다시 만들어보기

 
Share article

gyul