이전 내용
[플러터] 웹 앱 만들어보기
에러 관련 [플러터] 에러 발생 및 해결1. Gradle build failed to produce an .apk file...import 'package:flutter/material.dart';void main() { runApp( MaterialApp( // 머터리얼 디자인 위젯 home: Scaffold( // Scaffold 위젯 body: Center( /
puppy-foot-it.tistory.com
◆ 에러 관련
[플러터] 에러 발생 및 해결
1. Gradle build failed to produce an .apk file...import 'package:flutter/material.dart';void main() { runApp( MaterialApp( // 머터리얼 디자인 위젯 home: Scaffold( // Scaffold 위젯 body: Center( // 가운데 정렬 child: Text( // Text 위젯 'He
puppy-foot-it.tistory.com
◆ 깃허브 업로드 관련
[플러터] 대용량 파일 깃허브 업로드하기 (안드로이드 스튜디오)
대용량 파일 깃허브 업로드(안드로이드 스튜디오) 플러터 공부를 하다가 만들어둔 작업물들을 깃허브에 올리려고 하는데, 파일 용량이 크고, 파일 갯수도 많아서 GUI로 업로드가 안 된다. 이전에
puppy-foot-it.tistory.com
이미지 롤링 기능
이미지 여러 개를 롤링해 보여주는 기능을 구현해 본다.
여기서는 주기적으로 콜백 함수를 실행하기 위해 Timer 클래스의 Timer.periodic()을 사용한다.
◆ 이미지 추가하기
먼저, 롤링될 이미지를 asset 이라는 디렉터리를 생성 후, 그 안에 image라는 디렉터리를 생성하여 해당 디렉터리 내에 넣는다.
image_rolling / asset / image / 롤링될 이미지들
필자의 경우, 지난 11월에 다녀온 제주도 사진으로 해당 기능을 구현해 볼 예정이다.

이미지의 경우, 직접 폴더로 들어가서 넣어줘도 되고, IDE에다 드래그 앤 드롭해도 된다.

혹시 이미지가 필요하다면 자유롭게 다운 받아 사용해도 된다.
◆ pubspec.yaml 설정
그리고 추가시킨 이미지를 pubspec.yaml에 등록해 준다.
assets: # 에셋 등록
- asset/image/ # 에셋으로 등록할 폴더 위치

그리고나서 pub get을 실행하여 등록한 이미지를 프로젝트에서 사용할 수 있도록 해준다.
◆ 프로젝트 구조화
lib 디렉터리 내에 screen 디렉터리를 생성하고 home_screen.dart 파일을 생성해 준다.
- main.dart: 애플리케이션의 진입점
- home_screen.dart: 화면 구성 (위젯 등) 및 기능
추가로, 좌우로 위젯을 스와이프할 수 있는 기능을 구현하기 위해 PageView 위젯을 추가해 준다.
※ PageView 위젯: 여러 개의 위젯을 단독 페이지로 생성하고 가로 또는 세로 스와이프로 페이지를 슬라이드할 수 있게 하는 위젯
* main.dart
import 'package:flutter/material.dart';
import 'package:image_rolling/screen/home_screen.dart';
void main() {
runApp(
MaterialApp(
home: HomeScreen(),
),
);
}
* home_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:async';
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
State<HomeScreen> createState() => _HomeScreenState();
}
// _HomeScreenState 정의
class _HomeScreenState extends State<HomeScreen> {
// PageController 생성
final PageController pageController = PageController();
// initState() 함수
@override
void initState() {
super.initState(); // 부모 initState()
Timer.periodic( // Timer.periodic
Duration(seconds: 3), // 3초
(timer) {
// 현재 페이지 가져오기
int? nextPage = pageController.page?.toInt();
if (nextPage == null) { // 페이지 값 없을 때 예외 처리
return;
}
if (nextPage == 5) { // 마지막 페이지일 경우 첫 페이지로 이동
nextPage = 0;
} else {
nextPage ++; // 마지막 페이지 외에는 한 페이지씩 이동
}
pageController.animateToPage( // 페이지 변경
nextPage,
duration: Duration(milliseconds: 500),
curve: Curves.ease,
);
},
);
}
@override
Widget build(BuildContext context) {
// 상태바 색상 변경
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light); // 상태바 검정 -> 흰색
return Scaffold(
body:PageView( // PageView
controller: pageController, // pageController 등록
children: [1, 2, 3, 4, 5, 6] // 샘플 리스트
.map(
(number) => Image.asset('asset/image/image_$number.png',
fit: BoxFit.cover, // 항상 전체 화면이 되도록 이미지 핏 조절
),
)
.toList(),
),
);
}
}
[코드 전체 구조]
구성 요소 | 설명 |
HomeScreen | StatefulWidget - 상태를 가질 수 있는 위젯 |
_HomeScreenState | 실제 상태와 UI를 관리하는 클래스 |
PageController | PageView에서 현재 페이지를 제어하기 위한 도구 |
Timer.periodic | 3초마다 페이지를 자동으로 넘기도록 설정 |
PageView | 페이지를 좌우로 넘길 수 있는 위젯 (여기선 이미지 슬라이더) |
Image.asset() | 로컬 폴더에서 이미지를 불러옴 |
SystemChrome.setSystemUIOverlayStyle | 상태바 색상 설정 |
※ Timer를 사용하기 위해서는 플러터에서 기본으로 제공되는 async 패키지를 불러와야 한다.
[코드 상세 내용]
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
- StatefulWidget: 상태를 갖는 위젯 (시간에 따라 UI가 바뀜)
- const: 위젯이 변하지 않는다면 const로 선언 가능 (최적화)
@override
State<HomeScreen> createState() => _HomeScreenState();
- createState(): StatefulWidget에서 꼭 override 해야 하는 메서드
- 실제 동작은 _HomeScreenState 클래스에서 함
final PageController pageController = PageController();
- PageController: PageView를 프로그래밍적으로 제어할 수 있게 해줌
예: 몇 번째 페이지로 이동할지 정함
※ initState()는 타이머를 등록하는 역할
@override
void initState() {
super.initState();
Timer.periodic(
Duration(seconds: 3),
(timer) {
int? nextPage = pageController.page?.toInt();
if (nextPage == null) return;
if (nextPage == 5) {
nextPage = 0;
} else {
nextPage++;
}
pageController.animateToPage(
nextPage,
duration: Duration(milliseconds: 500),
curve: Curves.ease,
);
},
);
}
요소 | 설명 |
Timer.periodic() | 매 3초마다 콜백 함수를 실행 |
pageController.page?.toInt() | 현재 페이지를 정수로 가져옴 |
animateToPage() | 다음 페이지로 애니메이션 효과를 주며 이동 |
Curves.ease | 부드러운 이동 효과 곡선 |
※ build() 메서드: UI 생성
@override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
return Scaffold(
body: PageView(
controller: pageController,
children: [1, 2, 3, 4, 5, 6].map(
(number) => Image.asset(
'asset/image/image_$number.png',
fit: BoxFit.cover,
),
).toList(),
),
);
}
요소 | 설명 |
SystemChrome.setSystemUIOverlayStyle | 안드로이드 상단 상태바의 아이콘 색상을 흰색으로 변경 |
Scaffold | 기본 화면 레이아웃 구조 (앱바, 바디 등 포함 가능) |
PageView | 페이지를 좌우로 넘기는 뷰 |
.map(...).toList() | 1~6 숫자를 돌면서 각각에 대해 이미지 위젯을 만들어 리스트로 변환 |
'asset/image/image_$number.png' | 예: image_1.png, image_2.png처럼 반복적으로 이미지 불러옴 |
BoxFit.cover | 이미지를 전체 화면에 맞게 조절 (잘릴 수 있음) |
★ SystemChrome
- handleAppLifecycleStateChanged ( AppLifecycleState 상태 ) → void
새로운 앱 수명 주기 상태로 전환하는 동안 바인딩에서 호출.
- restoreSystemUIOverlays ( ) → Future < void >
시스템 오버레이를 setEnabledSystemUIMode를 통해 제공된 마지막 설정으로 복원 . 플랫폼에서 UI 요소를 강제로 활성화/비활성화할 때 사용.
- setApplicationSwitcherDescription ( ApplicationSwitcherDescription 설명 ) → Future < void >
애플리케이션 전환기와 관련된 애플리케이션의 현재 상태에 대한 설명 지정(최근 작업이라고도 함).
- setEnabledSystemUIMode ( SystemUiMode 모드 , { List < SystemUiOverlay > ? 오버레이 }) → Future < void >
애플리케이션이 실행 중일 때 표시할 SystemUiMode 지정.
- setPreferredOrientations ( List < DeviceOrientation > 방향 ) → Future < void >
애플리케이션 인터페이스가 표시될 수 있는 방향 집합 지정.
- setSystemUIChangeCallback ( SystemUiChangeCallback ? 콜백 ) → Future < void >
시스템 UI의 변경에 응답하기 위한 콜백 메서드 설정.
- setSystemUIOverlayStyle ( SystemUiOverlayStyle 스타일 ) → void
표시되는 시스템 오버레이(예: Android 또는 iOS의 상태 표시줄, Android의 시스템 탐색 표시줄)에 사용할 스타일 지정(있는 경우).
★ BoxFit
BoxFit은 Image 위젯에서 이미지를 어떻게 채워넣을지를 정의한다.
https://api.flutter.dev/flutter/painting/BoxFit.html
BoxFit enum - painting library - Dart API
BoxFit enum How a box should be inscribed into another box. See also: applyBoxFit, which applies the sizing semantics of these values (though not the alignment semantics). Inheritance Available extensions Values fill → const BoxFit Fill the target box by
api.flutter.dev
- 주요 속성
- contain: 이미지가 잘리지 않는 선에서 최대한 크게 늘림
- cover: 부모 위젯 전체를 덮는 선에서 최소한 크기로 조절
- fill: 이미지의 비율을 무시하고 부모 위젯의 이미지 비율대로 이미지 크기 조절
- fitHeight: 이미지의 비율을 유지한 채로 부모 위젯의 높이에 이미지의 높이를 맞춤.
- fitWidth: 이미지의 비율을 유지한 채로 부모 위젯의 넓이에 이미지의 넓이를 맞춤.
- none: 원본 이미지 크기와 비율을 그대로 사용
- scaleDown: 원본 이미지 크기와 비율을 그대로 사용(none)하면서 이미지를 중앙 정렬하고 부모 위젯이 이미지보다 작으면 이미지 크기를 줄여서 부모 위젯에 맞춤
Timer 클래스
Timer 클래스는 특정 시간이 지난 후에 일회성 또는 지속적으로 함수를 실행한다.
https://api.flutter.dev/flutter/dart-async/Timer-class.html
Timer class - dart:async library - Dart API
A countdown timer that can be configured to fire once or repeatedly. The timer counts down from the specified duration to 0. When the timer reaches 0, the timer invokes the specified callback function. Use a periodic timer to repeatedly count down the same
api.flutter.dev
- Timer(Duration, void callback (Timer timer) {}
▶ Timer는 지정된 시간부터 0까지 카운트다운하며, 타이머가 0에 도달하면 지정된 콜백 함수를 호출한다.
- Timer.periodic(Duration, void callback (Timer timer) {}
▶ 새로운 반복 타이머를 만들어 주기적으로 콜백 함수를 실행한다.
Duration: 지속시간, void callback: 콜백 함수
예를 들어, 2초마다 '감사합니다' 라는 문자열을 출력하게 하려면
Timer.periodic(Duraction(seconds: 2), (Timer timer) {print('안녕하세요')})
완성 화면
위젯 생명주기

◆ StatelessWidget: 무상태 위젯
StatelessWidget은 상태가 없는 위젯으로, 생성자에서 전달된 속성들을 기반으로만 자신을 구성한다. 즉, 한 번 생성된 후에는 내부 상태가 변화하지 않는다.
- 예를 들어, 텍스트, 아이콘, 버튼 등의 고정된 UI 요소일 경우 StatelessWidget 사용.
- 상태 변화가 필요 없는 경우 적합하며, 애플리케이션의 "불변" 부분을 표현하는 데 유용.
- UI를 업데이트할 필요가 없기 때문에 성능이 상대적으로 우수.
▶ StatelessWidget이 빌드되면 생성자가 실행되며, build() 함수가 실행된다. 그리고 build() 함수에 반환한 위젯이 화면에 렌더링된다. 모든 위젯은 불변(한 번 생성하고 나면 속성 변경 불가)의 특성을 갖고 있는데, 만약 위젯의 속성을 변경해야 할 때 StatelessWidget은 불변이므로 인스턴스를 새로 생성한 후 기존 인스턴스를 대체해서 변경 사항을 반영해야 한다.
◆ StatefulWidget: 상태 위젯
용어 | 설명 |
State | 앱의 UI에 영향을 주는 데이터. 예: 버튼 클릭 횟수, 로그인 상태 등 |
Widget | UI 구성요소. Flutter에선 모든 것이 위젯으로 표현된다. |
StatefulWidget | 상태(State)를 가질 수 있는 위젯. 상태가 바뀌면 UI도 자동으로 다시 그려진다. |
StatefulWidget은 상태가 있는 위젯으로, 위젯이 생성될 때와 후에 상태를 수정할 수 있다. 이러한 상태는 위젯이 다시 그려질 때 영향을 미친다.
- 동적인 UI, 즉 상태가 변경될 수 있는 경우 주로 사용된다. 예를 들어, 사용자의 입력, 애니메이션, 또는 데이터 변화에 따른 UI 업데이트가 필요한 경우 적합.
- StatefulWidget은 두 부분으로 구성: StatefulWidget 자체 / State
- StatefulWidget 자체: 위젯의 속성을 정의.
- State: 위젯의 상태를 관리하며, 상태가 변경될 때 setState 메서드를 호출하여 UI를 업데이트.
▶ StatefulWidget은 요리 레시피 + 냉장고의 재료들에 비유할 수 있다. 레시피(위젯)는 정해져 있어도, 재료(State)가 바뀌면, 결과물(UI)도 달라지는 것과 비슷한 이치다.
[위젯 생명 주기 주요 메서드]
메서드 이름 | 호출 시점 | 주요 목적 | 비유 |
createState() | StatefulWidget이 처음 생성될 때 | 위젯의 상태(State 객체)를 생성 | 🛠️ 설계도에서 집을 짓기 시작 |
initState() | State 객체가 처음 생성될 때 한 번만 호출됨 | 초기화 작업(예: 데이터 불러오기, 리스너 등록) | 🔌 전기 연결, 수도 설치 |
didChangeDependencies() | initState() 직후 또는 InheritedWidget이 변경될 때 | 상위 위젯의 의존성 변화 대응 | 📡 주변 환경의 변화 감지 |
build() | 상태 변경, 위젯 트리 변경 시 호출됨 | 실제 UI를 그리는 부분 | 🖼️ 그림을 그리는 캔버스 |
didUpdateWidget() | 부모 위젯이 새로 빌드되어 현재 위젯을 갱신할 때 | 이전 위젯과 비교해 변경 사항 처리 | 🧩 레고 부품이 바뀐 경우 조정 |
setState() | 상태를 변경하고 UI를 다시 그릴 때 호출됨 | 상태를 바꾸고 build() 재호출 | 🪄 마법처럼 화면 갱신 |
dirty() (내부용) | 위젯의 상태가 변경되어 다시 build되어야 할 때 | Flutter 엔진이 위젯을 "더럽혔다"고 표시함 (사용자가 직접 호출하진 않음) | 💢 ‘청소가 필요한 방’ 표시 |
deactivate() | 위젯이 위젯 트리에서 제거될 때 호출 | 위젯 제거 전 준비 작업 가능 | 🚪방 나가기 직전의 체크 |
dispose() | 위젯이 완전히 제거될 때 호출 | 메모리 정리, 리소스 해제 | 🧹 방 완전히 비우고 청소 |
[참고]
코드팩토리의 플러터 프로그래밍
다음 내용
[플러터] D-Day 앱 만들기
이전 내용 [플러터] 이미지 롤링 기능 구현하기이전 내용 [플러터] 웹 앱 만들어보기에러 관련 [플러터] 에러 발생 및 해결1. Gradle build failed to produce an .apk file...import 'package:flutter/material.dart';void ma
puppy-foot-it.tistory.com
'[앱개발] > Flutter, Dart, Figma' 카테고리의 다른 글
[플러터] 안드로이드 스튜디오로 플러터 시작하기 (0) | 2025.05.23 |
---|---|
[플러터] D-Day 앱 만들기 (feat. 폰트 반영하기, Cupertino 위젯) (0) | 2025.05.23 |
[플러터] pub 명령어 (0) | 2025.05.22 |
[플러터] 웹 앱 만들어보기 (feat. 웹뷰, 사용 권한) (0) | 2025.05.22 |
[플러터] 에러 발생 및 해결 (1) | 2025.05.21 |