Swiper#
스와이프 제스처로 아이템을 옆으로 밀어내거나 다음/이전 아이템으로 전환하는 컴포넌트입니다. 알림 해제, 카드 덱 전환 등에 활용됩니다.
Live Preview#
Web
Swipe to reveal actions
Flutter
Loading Flutter...
Swiper(
position: SwiperPosition.bottom,
child: text('Swipe content'),
)
// Swiper requires gesture context
Swiper(
position: SwiperPosition.bottom,
child: Text('Swipe content'),
)
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 알림이나 목록 항목을 스와이프하여 해제할 때
- 카드 스택에서 스와이프로 카드를 건너뛸 때
- 모바일 환경에서 자연스러운 스와이프 제스처 인터랙션이 필요할 때
대신 다른 컴포넌트를 사용하세요:
Carousel: 스와이프로 콘텐츠를 탐색하는 슬라이드쇼에Drawer: 화면 가장자리에서 슬라이드하여 열리는 사이드 패널에
기본 사용법 (Basic Usage)#
// 수평 스와이프로 해제
Swiper(
onDismiss: handleDismissed,
child: NotificationCard(
title: '새 메시지',
body: '홍길동님이 메시지를 보냈습니다.',
),
)
// 방향 및 임계값 설정
Swiper(
direction: SwipeDirection.horizontal,
threshold: 0.4,
onSwipe: handleSwiped,
onDismiss: handleDismissed,
child: Card(
child: ListTile(title: Text('스와이프 가능한 아이템')),
),
)
// 세로 스와이프
Swiper(
direction: SwipeDirection.vertical,
onDismiss: handleDismissed,
child: ImageCard(imageUrl: 'https://example.com/image.png'),
)
// 수평 스와이프 오버레이 — 좌측에서 열리는 드로어 방식
Swiper(
position: SwiperPosition.left,
handler: SwiperHandler.drawer,
open: isMenuOpen,
overlayContent: nav([
text('메뉴 항목 1'),
text('메뉴 항목 2'),
]),
child: mainContent,
)
// 세로 스와이프 — 아래에서 올라오는 시트 방식
Swiper(
position: SwiperPosition.bottom,
handler: SwiperHandler.sheet,
open: isSheetOpen,
showDragHandle: true,
barrierDismissible: true,
overlayContent: div([
text('바텀 시트 콘텐츠'),
]),
child: pageContent,
)
// 방향 제어 — 우측 오버레이, 드래그 핸들 없음
Swiper(
position: SwiperPosition.right,
handler: SwiperHandler.drawer,
open: isDetailOpen,
showDragHandle: false,
overlayContent: DetailPanel(),
child: listContent,
)
Props / Parameters#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
child | Widget | 필수 | 스와이프 대상 위젯 |
onSwipe |
void Function(SwipeDirection)? |
null |
스와이프 발생 시 콜백 |
onDismiss |
void Function()? |
null |
임계값 초과 후 해제 시 콜백 |
direction |
SwipeDirection |
horizontal |
스와이프 허용 방향 |
threshold |
double |
0.3 |
해제 트리거 임계값 (0.0 ~ 1.0) |
변형 (Variants)#
수평 스와이프 (Horizontal)#
좌우 방향으로 스와이프합니다. 알림 해제, 카드 스택에 적합합니다.
Swiper(
direction: SwipeDirection.horizontal,
onDismiss: handleDismissed,
child: NotificationTile(title: '알림'),
)
수직 스와이프 (Vertical)#
상하 방향으로 스와이프합니다. 피드 건너뛰기 등에 적합합니다.
Swiper(
direction: SwipeDirection.vertical,
onDismiss: handleDismissed,
child: FeedCard(content: '피드 콘텐츠'),
)
모든 방향 (Any)#
모든 방향으로 스와이프를 허용합니다.
Swiper(
direction: SwipeDirection.any,
onSwipe: handleSwiped,
onDismiss: handleDismissed,
child: PhotoCard(imageUrl: 'https://example.com/photo.png'),
)
동작 스펙 (Behavior)#
인터랙션#
- 드래그 시작: 터치/마우스 드래그 시 위젯이 따라 이동
- 임계값 미만 해제: 드래그를 놓으면 원래 위치로 스프링 복귀
- 임계값 초과 해제: 방향으로 밀려나가며
onDismiss호출 - 스와이프 방향 감지:
onSwipe(direction)으로 방향 전달
상태 전환#
idle→dragging: 드래그 시작dragging→dismissed: 임계값 초과 후 해제dragging→idle: 임계값 미만에서 드래그 해제
애니메이션#
- 복귀 애니메이션: 스프링 효과 300ms
- 해제 애니메이션: 방향으로 슬라이드 아웃 + 페이드 200ms
사용 가이드라인 (Usage Guidelines)#
✅ Do#
스와이프 가능함을 시각적 힌트로 안내
CouiSwiper(
onDismiss: handleDismissed,
child: Stack(
children: [
NotificationCard(title: '새 알림'),
Positioned(
right: 8,
top: 0,
bottom: 0,
child: Center(child: Icon(Icons.swipe, color: Colors.grey)),
),
],
),
)
스와이프 가능함을 알지 못하는 사용자를 위해 아이콘이나 애니메이션 힌트를 제공한다.
❌ Don't#
threshold를 너무 낮게 설정하지 않기
// ❌ 가볍게 건드리기만 해도 해제됨
CouiSwiper(
threshold: 0.05,
onDismiss: handleDismissed,
child: ImportantCard(),
)
임계값이 너무 낮으면 의도치 않은 해제가 발생해 사용자 실수를 유발한다.
✅ Do#
해제 취소(undo) 기능을 함께 제공
CouiSwiper(
onDismiss: () {
handleNotificationDismissed();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('알림이 삭제되었습니다.'),
action: SnackBarAction(
label: '취소',
onPressed: handleUndoDismiss,
),
),
);
},
child: NotificationTile(notification: notification),
)
실수로 해제한 경우 복구할 수 없으면 사용자 불만이 발생한다. Toast + undo 패턴으로 실수를 만회할 기회를 제공한다.
❌ Don't#
중요한 콘텐츠에 실수 방지 없이 Swiper 사용
// ❌ 확인 없이 바로 삭제
CouiSwiper(
onDismiss: handlePermanentDelete, // 복구 불가
child: ImportantDocument(),
)
복구 불가능한 동작은 확인 Dialog나 undo 기능 없이는 스와이프로만 처리하면 안 된다.
✅ Do#
슬라이드 전환 방향을 일관되게 유지하세요.
CouiSwiper(
direction: Axis.horizontal, // 일관된 수평 스와이프
items: pageItems,
onPageChanged: handlePageChanged,
)
슬라이드 방향이 일관되면 사용자가 스와이프 제스처를 자연스럽게 예측하고 사용할 수 있습니다.
❌ Don't#
자동 재생 중 수동 스와이프 충돌을 방치하지 마세요.
// ❌ 자동 재생 중 수동 스와이프 시 경쟁 조건 발생
CouiSwiper(
items: items,
autoPlay: true,
interval: Duration(seconds: 2), // 수동 조작 후에도 타이머 계속
// pauseOnDrag: false (기본)
)
자동 재생 중 사용자가 수동으로 스와이프하면 타이머와 충돌할 수 있습니다. pauseOnDrag: true로 수동 조작 시 자동 재생을 일시 정지하세요.
접근성 (Accessibility)#
키보드 인터랙션#
| 키 | 동작 |
|---|---|
Delete / Backspace | 포커스된 항목 해제 |
Tab | 다음 포커스 가능 요소로 이동 |
스크린 리더#
- Flutter:
Semantics(customSemanticsActions: ...)으로 "스와이프하여 삭제" 액션 추가 - Web: 별도 버튼(삭제 버튼)을 함께 제공하여 키보드/스크린 리더 사용자도 해제 가능하게
터치 타겟#
- 스와이프 영역은 전체 위젯 크기
- 최소 높이: 48dp
크로스 플랫폼 차이점 (Platform Differences)#
| 항목 | Flutter | Web |
|---|---|---|
| 클래스명 | CouiSwiper | Swiper |
| 제스처 감지 | GestureDetector + DragUpdateDetails |
Pointer Events API |
| 애니메이션 | AnimationController + spring |
CSS transition + JS 애니메이션 |
| 터치 지원 | 네이티브 터치 지원 | touchstart/touchmove 이벤트 |
관련 컴포넌트 (Related Components)#
조합 예제#
// 알림 목록에 스와이프 해제 적용
ListView.builder(
itemCount: notifications.length,
itemBuilder: (context, index) {
final notification = notifications[index];
return CouiSwiper(
key: ValueKey(notification.id),
onDismiss: () => handleNotificationDismissed(notification.id),
child: NotificationTile(notification: notification),
);
},
)