본문 바로가기
Flutter for Beginners

Flutter PageView을 이용한 자동 슬라이드 이미지 겔러리

by Andrew's Akashic Records 2025. 3. 24.
728x90

Flutter

  • PageController를 사용해 슬라이드를 제어
  • Timer.periodic으로 3초마다 다음 페이지로 이동
  • 마지막 이미지에 도달하면 처음으로
  • 페이지 이동에는 animateToPage를 사용해 부드러운 전환을 구현
  • dispose()에서 타이머와 컨트롤러 정리

전체 코드

import 'dart:async';
import 'package:flutter/material.dart';


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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '자동 이미지 갤러리',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: AutoSlidingGallery(), // 홈 화면 지정
    );
  }
}

class AutoSlidingGallery extends StatefulWidget {
  const AutoSlidingGallery({super.key});

  @override
  _AutoSlidingGalleryState createState() => _AutoSlidingGalleryState();
}

class _AutoSlidingGalleryState extends State<AutoSlidingGallery> {
  final PageController _pageController = PageController();
  final List<String> _imageUrls = [
    'https://picsum.photos/id/1018/600/400',
    'https://picsum.photos/id/1023/600/400',
    'https://picsum.photos/id/1043/600/400',
    'https://picsum.photos/id/1057/600/400',
  ];

  int _currentPage = 0;
  Timer? _timer;

  @override
  void initState() {
    super.initState();

    // 3초마다 페이지 넘기기
    _timer = Timer.periodic(Duration(seconds: 3), (Timer timer) {
      if (_currentPage < _imageUrls.length - 1) {
        _currentPage++;
      } else {
        _currentPage = 0;
      }

      _pageController.animateToPage(
        _currentPage,
        duration: Duration(milliseconds: 500),
        curve: Curves.easeInOut,
      );
    });
  }

  @override
  void dispose() {
    _pageController.dispose();
    _timer?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("자동 이미지 갤러리")),
      body: PageView.builder(
        controller: _pageController,
        itemCount: _imageUrls.length,
        itemBuilder: (context, index) {
          return Image.network(
            _imageUrls[index],
            fit: BoxFit.cover,
            width: double.infinity,
            loadingBuilder: (context, child, progress) {
              if (progress == null) return child;
              return Center(child: CircularProgressIndicator());
            },
          );
        },
      ),
    );
  }
}

앱 실행 결과

 

전체 코드 구조

MyApp

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '자동 이미지 갤러리',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: AutoSlidingGallery(), // 홈 화면
    );
  }
}
  • 앱 전체의 테마와 구조를 설정하는 루트 위젯
  • MaterialApp은 Material 스타일 앱을 생성
  • home: 속성으로 앱 시작 시 보여줄 화면을 AutoSlidingGallery로 지정

AutoSlidingGallery

class AutoSlidingGallery extends StatefulWidget {
  @override
  _AutoSlidingGalleryState createState() => _AutoSlidingGalleryState();
}
  • StatefulWidget으로 만든 이유: 시간마다 자동으로 상태 변경(페이지 전환) 되기 때문
  • createState()가 연결된 _AutoSlidingGalleryState가 실제 동작 처리

_AutoSlidingGalleryState

class _AutoSlidingGalleryState extends State {
  final PageController _pageController = PageController();
  final List _imageUrls = [
    'https://picsum.photos/id/1018/600/400',
    'https://picsum.photos/id/1023/600/400',
    'https://picsum.photos/id/1043/600/400',
    'https://picsum.photos/id/1057/600/400',
  ];
  • PageController: 페이지를 프로그래밍적으로 제어 (전환할 때 사용)

List<String>: 갤러리에 보여줄 이미지 URL 리스트

  int _currentPage = 0;
  Timer? _timer;
  • _currentPage: 현재 보여지고 있는 페이지 번호 저장
  • _timer: 3초마다 자동 슬라이드를 수행할 타이머

initState()

  @override
  void initState() {
    super.initState();

    _timer = Timer.periodic(Duration(seconds: 3), (Timer timer) {
      if (_currentPage < _imageUrls.length - 1) {
        _currentPage++;
      } else {
        _currentPage = 0;
      }

      _pageController.animateToPage(
        _currentPage,
        duration: Duration(milliseconds: 500),
        curve: Curves.easeInOut,
      );
    });
  }
  • 위젯이 처음 생성될 때 호출됨
  • Timer.periodic()을 사용해 3초마다 실행되는 함수 등록
    • 페이지 끝까지 갔다면 다시 처음으로 돌아가도록 로직 작성
    • animateToPage()로 부드럽게 전환

dispose()

  @override
  void dispose() {
    _pageController.dispose();
    _timer?.cancel();
    super.dispose();
  }
  • 위젯이 제거될 때 호출
  • 리소스 정리:
    • PageController 해제
    • Timer 중지
  • 메모리 누수 방지 필수 처리!

build()

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("자동 이미지 갤러리")),
      body: PageView.builder(
        controller: _pageController,
        itemCount: _imageUrls.length,
        itemBuilder: (context, index) {
          return Image.network(
            _imageUrls[index],
            fit: BoxFit.cover,
            width: double.infinity,
            loadingBuilder: (context, child, progress) {
              if (progress == null) return child;
              return Center(child: CircularProgressIndicator());
            },
          );
        },
      ),
    );
  }
}
  • 화면 UI 구성
  • Scaffold: 기본 머티리얼 앱 구조 (AppBar + Body)
  • PageView.builder: 페이지 형태로 이미지 넘기기 구현
    • controller: 우리가 선언한 _pageController
    • itemBuilder: 각 페이지에 보여줄 Image.network
    • BoxFit.cover: 이미지를 화면 가득 채우되, 비율 유지
    • loadingBuilder: 이미지 로딩 중에는 로딩 인디케이터 표시

 

BoxFit 속성

속성 이름 설명
BoxFit.fill 위젯의 크기에 정확히 맞게 이미지가 강제로 맞춰짐. 비율 무시.
BoxFit.contain 이미지의 비율을 유지하면서, 컨테이너 안에 완전히 들어가도록 축소/확대됨. (여백 생길 수 있음)
BoxFit.cover 이미지의 비율을 유지하면서, 컨테이너를 가득 채우도록 잘림 없이 확대됨. (일부 잘릴 수 있음)
BoxFit.fitWidth 이미지가 컨테이너의 너비에 맞춰짐, 비율 유지. 세로는 잘릴 수 있음.
BoxFit.fitHeight 이미지가 컨테이너의 높이에 맞춰짐, 비율 유지. 가로는 잘릴 수 있음.
BoxFit.none 이미지의 실제 크기로 표시됨. 컨테이너보다 크면 잘림.
BoxFit.scaleDown none과 같지만, 이미지가 너무 크면 축소됨. 크면 줄이고, 작으면 그대로.

 

BoxFit 사용 예시

  • 사진 썸네일: BoxFit.cover (잘라도 이쁨)
  • 로고/아이콘: BoxFit.contain (비율 유지가 중요)
  • 배경 이미지: BoxFit.cover or BoxFit.fill
  • 이미지 원본 그대로: BoxFit.none

PageController

기능 설명
현재 페이지 추적 .page, .hasClients, .initialPage 등을 통해 현재 위치 확인 가능
특정 페이지로 이동 animateToPage(), jumpToPage() 등으로 프로그래밍적으로 페이지 전환
스크롤 위치 제어 jumpTo()나 animateTo() 등으로 픽셀 단위 이동 가능
PageView 연결 controller 속성에 넣어서 PageView와 연결

 

final PageController _controller = PageController(initialPage: 0);

1. 초기 페이지 지정

PageController(initialPage: 2); // 2번째 페이지부터 시작

2. 페이지 강제로 이동 (즉시)

_controller.jumpToPage(3); // 4번째 페이지로 "바로" 이동

3. 페이지 부드럽게 이동

_controller.animateToPage(
  1,
  duration: Duration(milliseconds: 500),
  curve: Curves.easeInOut,
);

4. 현재 페이지 확인

double? currentPage = _controller.page; // 예: 2.0, 2.3 (스크롤 중이면 소수점)

5. 픽셀 위치로 이동

_controller.jumpTo(200.0); // 200픽셀 위치로 이동 (세부 스크롤 제어)

사용 예

  • 자동 슬라이드 구현할 때 (animateToPage)
  • 사용자 입력 없이 특정 페이지 보여줄 때
  • 커스텀 페이지 인디케이터 만들 때
  • 페이지 위치 감지해서 상태 반영할 때

 

728x90