[Flutter] Clone App - 차이

LOCKED·2021년 10월 22일
12

CloneApp

목록 보기
1/1
post-thumbnail

오늘은 내가 자주 쓰고 있는 앱들 중 하나인 차이 앱을 클론해보려 한다.
클론 앱이라고 거창하게 표현했지만 사실 그냥 메인화면 인터렉션 흉내 정도이다.

최근 차이 앱 UI/UX가 변경되었는데, 변경된 기념으로...

차이 앱에서 클론할 영역은 메인화면으로 잡았다.
어려운 레이아웃이나 디자인이 없어서 금방 할 수 있었다.

차이 메인화면 구조

변경된 메인 화면은 상단 중단 하단 크게 세 영역으로 나뉘어져 있다.

  • 상단 : 번개와 카드
  • 중단 : 할인중인 목록
  • 하단 : 메뉴 및 히스토리

메인화면 개발

상단과 중단에 해당하는 부분까지만 개발해보려한다.

백그라운드

백그라운드는 linear gradient가 적용되어 있다.
색상은 투명한 검정 -> 검정 으로 적용한다.

...
body: Container(
  decoration: BoxDecoration(
    gradient: LinearGradient(
      begin: Alignment.topCenter,
      end: Alignment.bottomCenter,
      colors: [
        Colors.black.withOpacity(0.7),
        Colors.black.withOpacity(0.9),
        Colors.black,
      ],
      stops: [
        0,
        0.22,
        0.5,
      ],
    ),
  ),
...

할인 리스트

상단 영역과 하단 영역보다는 중단 영역에 있는 할인 리스트 를 구현해보려 한다.
차이할인 리스트를 분석하여 특징을 나열해보면 다음과 같다.

1. horizontal Axis
2. 각 아이템의 위아래 애니메이션
3. 자동 스크롤

horizontal Axis

리스트의 방향을 vertical -> horizontal 로 바꾸는 방법이다.

ListView( // 혹은 ListView.builder
  scrollDirection: Axis.horizontal,
  ...
)

할인 아이템 데이터 클래스

class Data{
  int id;
  int price;
  int percentage;
  String name;
  String image;
  Color color;
  ...
}

차이의 할인 아이템을 보고 데이터 클래스를 만들었다.
아마 실제 차이 에서는 색상값을 hex로 저장하지 않을까 추측해본다.

할인 아이템 애니메이션

할인 아이템은 사용자의 터치 이벤트가 있기 전까지는 위아래로 움직이는 것을 확인할 수 있다.

먼저, 카드의 애니메이션을 구현한다.


/// MainPage Widget (Stateful)

void initState() {
  super.initState();
  animationController = AnimationController(
    vsync: this,
    duration: Duration(milliseconds: 3600),
    upperBound: pi * 2,
  )..drive(CurveTween(curve: Curves.bounceInOut));
  animationController.repeat();
 ... 
}

/// ItemCard Widget (Stateless)
return AnimatedBuilder(
  child: CardInfoWidget(color: cardColor),
  builder: (context, child) {
    var value = sin(widget.index.isOdd ? -widget.animationController.value : widget.animationController.value);
    return Transform.translate(
      offset: Offset(0, value * 30),
      child: child,
    );
  },
  animation: widget.animationController,
);

AnimatedBuilder 를 사용하여 할인 아이템 카드의 애니메이션을 구현한다.
해당 카드는 0 ~ 2*pi 의 값을 반복하며, 홀수 아이템과 짝수 아이템의 움직임을 달리한다.

할인 아이템 자동 스크롤

할인 아이템들은 일정 시간동안 자동으로 스크롤 된다.
이를 구현하기 위해서 ScrollController 를 사용한다.

Timer.periodic(Duration(milliseconds: 100), (timer) {
  if (!detectUserEvent) scrollController.animateTo(scrollController.offset + 7, duration: Duration(milliseconds: 100), curve: Curves.linear);
});

타이머를 사용하여, 유저의 이벤트가 감지되지 않으면 자동으로 스크롤한다.

detectUserEvent는 바로 뒤에 나온다.

스크롤 이벤트 감지

자동으로 스크롤 도중에 사용자의 이벤트가 있다면 위에서 구현한 카드 애니메이션을 멈춘다.
이를 구현하기 위해서 NotificationListener, Listener 위젯을 사용한다.

child: NotificationListener(
  onNotification: (_) {
    if (_ is ScrollEndNotification) {
      animationController.repeat();
      setState(() {
        detectUserEvent = false;
      });
    }
    return true;
  },
  child: Listener(
    onPointerDown: (_) {
      animationController.stop();
      setState(() {
        detectUserEvent = true;
      });
    },
    child: ListView.builder(
      scrollDirection: Axis.horizontal,
      ...

먼저 Listener 위젯으로 사용자의 터치를 감지한다.
사용자의 터치가 감지되면 컨트롤러를 정지한다.

그리고 NotificationListener 위젯으로 사용자로 인해 발생한 이벤트의 종료 체크한다.
ScrollEndNotification의 값으로 사용자로 인한 이벤트의 종료를 알 수 있다.

사실 차이 앱의 메인화면은 위의 애니메이션을 구현하면서 끝났다고 해도 될 것 같다.

결과 화면

오늘은 차이 앱을 클론해보는 시간을 가져보았다.

개인적으로 차이 앱은
서비스와 디자인과는 별개로 메인화면의 사용성은 그렇게 좋지는 않은 것 같다.
이쁘긴 하지만, 할인되는 목록을 한 눈에 보기도 힘들고 검색도 되지않아서 리스트를 처음부터 끝까지 찾아봐야한다...

결제 및 할인 내역을 달력 뷰로도 제공해줬으면 좋겠다...

profile
Flutter 개발자 :'>

0개의 댓글