AnimatedCrossFade

샤워실의 바보·2024년 2월 10일
0
post-thumbnail
post-custom-banner
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:tiktok_clone/constants/gaps.dart';
import 'package:tiktok_clone/constants/sizes.dart';
import 'package:tiktok_clone/features/main_navigation/main_navigation_screen.dart';

enum Direction { right, left }
enum Page { first, second }

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

  
  State<TutorialScreen> createState() => _TutorialScreenState();
}

class _TutorialScreenState extends State<TutorialScreen> {
  Direction _direction = Direction.right;
  Page _showingPage = Page.first;

  void _onPanUpdate(DragUpdateDetails details) {
    setState(() {
      _direction = details.delta.dx > 0 ? Direction.right : Direction.left;
    });
  }

  void _onPanEnd(DragEndDetails detail) {
    setState(() {
      _showingPage = _direction == Direction.left ? Page.second : Page.first;
    });
  }

  void _onEnterAppTap() {
    Navigator.of(context).pushAndRemoveUntil(
      MaterialPageRoute(
        builder: (context) => const MainNavigationScreen(),
      ),
      (route) => false,
    );
  }

  
  Widget build(BuildContext context) {
    return GestureDetector(
      onPanUpdate: _onPanUpdate,
      onPanEnd: _onPanEnd,
      child: Scaffold(
        body: Padding(
          padding: const EdgeInsets.symmetric(horizontal: Sizes.size24),
          child: SafeArea(
            child: AnimatedCrossFade(
              firstChild: const Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Gaps.v80,
                  Text(
                    "Watch cool videos!",
                    style: TextStyle(
                      fontSize: Sizes.size40,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  Gaps.v16,
                  Text(
                    "Videos are personalized for you based on what you watch, like, and share.",
                    style: TextStyle(
                      fontSize: Sizes.size20,
                    ),
                  )
                ],
              ),
              secondChild: const Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Gaps.v80,
                  Text(
                    "Follow the rules",
                    style: TextStyle(
                      fontSize: Sizes.size40,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  Gaps.v16,
                  Text(
                    "Videos are personalized for you based on what you watch, like, and share.",
                    style: TextStyle(
                      fontSize: Sizes.size20,
                    ),
                  )
                ],
              ),
              crossFadeState: _showingPage == Page.first
                  ? CrossFadeState.showFirst
                  : CrossFadeState.showSecond,
              duration: const Duration(milliseconds: 300),
            ),
          ),
        ),
        bottomNavigationBar: BottomAppBar(
          child: Padding(
            padding: const EdgeInsets.symmetric(
              vertical: Sizes.size24,
              horizontal: Sizes.size24,
            ),
            child: AnimatedOpacity(
              duration: const Duration(milliseconds: 300),
              opacity: _showingPage == Page.first ? 0 : 1,
              child: CupertinoButton(
                onPressed: _onEnterAppTap,
                color: Theme.of(context).primaryColor,
                child: const Text('Enter the app!'),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

이 코드는 튜토리얼 화면을 만드는 Flutter 앱의 일부입니다. 사용자는 화면을 스와이프해서 다른 튜토리얼 페이지를 볼 수 있으며, 두 번째 페이지에서는 "앱 입장하기" 버튼을 통해 앱의 메인 부분으로 이동할 수 있습니다.

아래에 코드의 주요 부분을 설명하겠습니다.

  1. enum Direction { right, left } / enum Page { first, second }:
    두 개의 열거형(enum)을 선언하여 방향과 페이지를 나타냅니다.

  2. Direction _direction = Direction.right; / Page _showingPage = Page.first;:
    현재의 방향과 표시되고 있는 페이지를 저장하는 변수입니다.

  3. _onPanUpdate(DragUpdateDetails details) / _onPanEnd(DragEndDetails detail):
    사용자가 화면을 드래그했을 때와 드래그가 끝났을 때 발생하는 이벤트를 처리하는 메서드입니다.

  4. _onEnterAppTap():
    "앱 입장하기" 버튼이 눌렸을 때의 이벤트를 처리하는 메서드입니다.

  5. AnimatedCrossFade:
    현재 보여지고 있는 페이지(_showingPage)에 따라 서로 다른 컨텐츠를 부드럽게 전환해주는 위젯입니다.

  6. BottomAppBar:
    하단에 버튼을 배치합니다. _showingPagePage.first일 경우 투명도를 0으로, Page.second일 경우 투명도를 1로 설정하여 버튼을 보이거나 숨깁니다.

  7. AnimatedOpacity:
    _showingPage에 따라 버튼의 투명도를 애니메이션으로 변경합니다.

  8. CupertinoButton:
    "앱 입장하기" 버튼입니다. 이 버튼을 누르면 _onEnterAppTap 메서드가 호출되어 앱의 메인 화면으로 이동합니다.

이러한 방식으로, 이 코드는 튜토리얼 화면에서 사용자의 인터랙션을 처리하고 다음 단계로 넘어갈 수 있는 방법을 제공합니다.

details.delta.dx > 0 이라는 코드 조건은 사용자가 터치나 드래그 동작을 수행할 때 x축의 움직임이 양의 방향(즉, 오른쪽으로) 있었는지를 판단합니다.

여기서 detailsDragUpdateDetails 객체이고, delta는 마지막 드래그 위치에서 현재 드래그 위치까지의 변화량(Offset)을 나타냅니다. dx는 그 Offset 내에서 x축 방향의 변화량을 의미합니다.

  • dx > 0: 사용자가 오른쪽으로 드래그했습니다.
  • dx < 0: 사용자가 왼쪽으로 드래그했습니다.
  • dx = 0: 사용자가 수평 방향으로는 움직이지 않았습니다.

이러한 정보는 특히 슬라이딩 메뉴, 이미지 캐러셀, 또는 카드 뒤집기 등의 UI 인터랙션을 구현할 때 유용하게 사용됩니다.

profile
공부하는 개발자
post-custom-banner

0개의 댓글