chyam

[flutter] - 이미지 자동으로 넘기기(PageController) 본문

Android

[flutter] - 이미지 자동으로 넘기기(PageController)

chyam_eun 2026. 4. 29. 17:33

프로젝트를 진행하며 사진들이 순서대로 자동으로 넘겨지거나 직접 넘길 수 있도록 하고자 하였습니다.

 

먼저 사용할 이미지들을 assets/images 내에 저장해주고, pubspec.yaml도 아래와같이 해준 뒤 Pub get을 해줍니다.

flutter:
  ...
  assets:
    - assets/images/

 

class _HomeScreenState extends State<HomeScreen> {
  Timer? timer;
  final List<String> _Examples = [
    'assets/images/cat1.jpg',
    'assets/images/cat2.jpg',
    'assets/images/cat3.jpg',
  ];

  // 이미지 슬라이더를 제어할 컨트롤러
  final int _initialPage = 1002;
  late PageController _pageController;
  int _currentPage = 0;

currentPage는 0으로 설정해줍니다.

 

  @override
  void initState() {
    super.initState();
    // 시작 페이지 지정
    _pageController = PageController(initialPage: _initialPage);
    // 처음 켰을 때 점 위치를 0번으로 맞춤
    _currentPage = _initialPage % _mosaicExamples.length;

    timer = Timer.periodic(const Duration(seconds: 3), (timer) { // 3초타이머
      if (_pageController.hasClients) {
        // 다음 페이지로 그냥 1만 더해서 이동하면 됨
        _pageController.nextPage(
          duration: const Duration(milliseconds: 400),
          curve: Curves.ease,
        );
      }
    });
  }

  @override
  void dispose() { // 위젯 죽을때
    _pageController.dispose(); // 리소스 해제
    if (timer!=null){
      timer!.cancel();
    }
    super.dispose();
  }

3초마다 사진이 옆으로 이동하게됩니다.

initState에서는 타이머 객체 생성 + 페이지 넘김

dispose에서는 리소스를 해제해줍니다.

 

 @override
  Widget build(BuildContext context) {
    return Scaffold(
      ...
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 20.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center, // 모든 요소를 상단 중앙 정렬
          children: [
            const SizedBox(height: 20),
            SizedBox(
              height: 300,
              child: PageView.builder(
                controller: _pageController,
                onPageChanged: (int page) {
                  setState(() {
                    // 현재 점의 위치는 '페이지 번호 % 사진개수'로 계산
                    _currentPage = page % _Examples.length;
                  });
                },
                itemBuilder: (context, index) {
                  // 실제 사진도 'index % 사진개수'로 가져옴
                  final imageIndex = index % _Examples.length;
                  return Container(
                    clipBehavior: Clip.antiAlias,
                    decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(15),
                      border: Border.all(color: Colors.black12),
                    ),
                    child: Image.asset(
                      _Examples[imageIndex], // 해당사진을 넣음
                      fit: BoxFit.cover,
                    ),
                  );
                },
              ),
            ),

            // 자동으로 넘어가는 듯한 시각적 묘사를 위한 페이지 인디케이터
            const SizedBox(height: 10),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: List.generate(
                _mosaicExamples.length,
                    (index) => Container(
                  margin: const EdgeInsets.symmetric(horizontal: 4),
                  width: _currentPage == index ? 10 : 6, // 선택됐으면 좀더 길게
                  height: 6,
                  decoration: BoxDecoration(
                    color: _currentPage == index ? Colors.black : Colors.grey.shade400, // 선택됐으면 검은색으로
                    borderRadius: BorderRadius.circular(3),
                  ),
                ),
              ),
            ),

 

결과는 아래와 같습니다!

0