오늘은 내가 자주 쓰고 있는 앱들 중 하나인 차이 앱을 클론해보려 한다.
클론 앱이라고 거창하게 표현했지만 사실 그냥 메인화면 인터렉션 흉내 정도이다.
최근 차이 앱 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. 자동 스크롤
리스트의 방향을 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
의 값으로 사용자로 인한 이벤트의 종료를 알 수 있다.
사실 차이 앱의 메인화면은 위의 애니메이션을 구현하면서 끝났다고 해도 될 것 같다.
오늘은 차이 앱을 클론해보는 시간을 가져보았다.
개인적으로 차이 앱은
서비스와 디자인과는 별개로 메인화면의 사용성은 그렇게 좋지는 않은 것 같다.
이쁘긴 하지만, 할인되는 목록을 한 눈에 보기도 힘들고 검색도 되지않아서 리스트를 처음부터 끝까지 찾아봐야한다...
결제 및 할인 내역을 달력 뷰로도 제공해줬으면 좋겠다...