RefreshTrigger | CoUI

RefreshTrigger

스크롤을 당겨서 콘텐츠를 새로고침하는 Pull-to-Refresh 컴포넌트

RefreshTrigger#

스크롤 가능한 콘텐츠를 아래로 당겨서 새로고침을 트리거하는 컴포넌트입니다. iOS와 Android 스타일의 당겨서 새로고침 패턴을 구현합니다.

Live Preview#

Web
Pull to refresh
Pull to refresh
Flutter
Loading Flutter...
RefreshTrigger(
  stage: TriggerStage.idle,
  child: text('Pull to refresh'),
)
// RefreshTrigger requires scroll context
RefreshTrigger(
  onRefresh: handleRefresh,
  child: Text('Pull to refresh'),
)

사용 시기 (When to Use)#

이 컴포넌트를 사용하세요:

  • 소셜 피드, 뉴스 목록처럼 최신 데이터를 수동으로 불러와야 할 때
  • 모바일 앱에서 네이티브 pull-to-refresh 패턴을 구현할 때
  • 스크롤 가능한 영역의 내용을 사용자가 직접 갱신할 수 있어야 할 때

대신 다른 컴포넌트를 사용하세요:

  • Loading: 자동 새로고침 또는 페이지 로딩 중 표시에
  • Progress: 작업 진행 상태를 퍼센트로 표시할 때

기본 사용법 (Basic Usage)#

// 기본 당겨서 새로고침
RefreshTrigger(
  onRefresh: handleRefresh,
  child: ListView.builder(
    itemCount: _items.length,
    itemBuilder: (context, index) => ListTile(
      title: Text(_items[index]),
    ),
  ),
)

// 변위 및 색상 커스터마이징
RefreshTrigger(
  onRefresh: handleRefresh,
  displacement: 60.0,
  color: Colors.blue,
  child: SingleChildScrollView(
    child: Column(
      children: _buildContentList(),
    ),
  ),
)
RefreshTrigger(
  onRefresh: handleRefresh,
  child: ListView.builder(
    itemCount: _items.length,
    itemBuilder: (context, index) => ListTile(
      title: Text(_items[index]),
    ),
  ),
)

RefreshTrigger(
  onRefresh: handleRefresh,
  displacement: 60.0,
  color: Colors.blue,
  child: ContentList(items: _items),
)

Props / Parameters#

속성타입기본값설명
onRefresh Future<void> Function() 필수 새로고침 트리거 시 호출되는 비동기 핸들러
childWidget필수새로고침 대상 스크롤 가능한 위젯
displacement double 40.0 인디케이터가 표시되는 거리(px)
color Color? null 새로고침 인디케이터 색상

변형 (Variants)#

기본 (Default)#

시스템 기본 새로고침 인디케이터를 사용합니다.

RefreshTrigger(
  onRefresh: handleRefresh,
  child: ListView(children: _buildItems()),
)

커스텀 색상#

브랜드 색상에 맞게 인디케이터 색상을 변경합니다.

RefreshTrigger(
  onRefresh: handleRefresh,
  color: Theme.of(context).colorScheme.primary,
  child: ListView(children: _buildItems()),
)

넓은 변위#

인디케이터가 더 많이 당겨져야 새로고침이 트리거됩니다.

RefreshTrigger(
  onRefresh: handleRefresh,
  displacement: 80.0,
  child: ListView(children: _buildItems()),
)

동작 스펙 (Behavior)#

인터랙션#

  • 당기기: 스크롤 최상단에서 아래로 당기면 인디케이터 표시
  • 충분히 당기기: displacement 이상 당기면 새로고침 트리거 표시
  • 해제: 당기기를 놓으면 onRefresh() 호출, 완료까지 인디케이터 표시
  • 중단: displacement 미만에서 놓으면 복귀

상태 전환#

  • idledragging: 최상단에서 아래로 드래그 시작
  • draggingready: displacement 초과
  • readyrefreshing: 드래그 해제 후 onRefresh 실행
  • refreshingidle: Future 완료 후

애니메이션#

  • 인디케이터 등장: 드래그 거리에 비례한 회전 애니메이션
  • 새로고침 중: 무한 회전 스피너
  • 복귀: 스프링 효과 300ms

사용 가이드라인 (Usage Guidelines)#

✅ Do#

onRefresh를 async/await로 올바르게 구현

Future<void> handleRefresh() async {
  final newData = await fetchLatestData();
  setState(() {
    _items = newData;
  });
  // Future가 완료되면 인디케이터 자동 숨김
}

CouiRefreshTrigger(
  onRefresh: handleRefresh,
  child: itemList,
)

onRefresh가 올바르게 완료되어야 인디케이터가 자동으로 숨겨진다.


❌ Don't#

스크롤할 수 없는 위젯에 RefreshTrigger 사용

// ❌ 스크롤 불가능한 위젯에 사용
CouiRefreshTrigger(
  onRefresh: handleRefresh,
  child: Container(
    child: Text('스크롤 없는 콘텐츠'),
  ),
)

스크롤 가능한 위젯이 없으면 pull 제스처가 작동하지 않는다.

✅ Do#

브랜드 색상으로 인디케이터 색상 지정

CouiRefreshTrigger(
  onRefresh: handleRefresh,
  color: Theme.of(context).colorScheme.primary,
  child: feedList,
)

브랜드 색상의 인디케이터는 앱의 일관된 시각 언어를 유지한다.


❌ Don't#

새로고침 중에도 UI가 완전히 차단되지 않도록 주의

// ❌ 새로고침 중 전체 화면을 가리는 로딩 오버레이
CouiRefreshTrigger(
  onRefresh: () async {
    showFullScreenLoader(); // 중복 로딩 표시
    await fetchData();
    hideFullScreenLoader();
  },
  child: feedList,
)

RefreshTrigger 자체가 로딩 인디케이터를 표시하므로 별도 전체 화면 로딩은 중복이다.

✅ Do#

새로고침 완료 후 명확한 피드백을 제공하세요.

CouiRefreshTrigger(
  onRefresh: () async {
    await dataRepository.refresh();
    // 새로고침 완료 후 스낵바 또는 업데이트 시간 표시
    showRefreshCompleted();
  },
  child: ContentList(),
)

새로고침이 완료되면 사용자가 데이터가 업데이트되었음을 인식할 수 있도록 명확한 피드백을 제공하세요.


❌ Don't#

새로고침 중 추가 인터랙션을 허용하지 마세요.

// ❌ 새로고침 중 버튼 클릭 가능
CouiRefreshTrigger(
  onRefresh: handleRefresh,
  child: Column(
    children: [
      ContentList(),
      CouiButton(
        onPressed: handleLoadMore,  // 새로고침 중에도 활성화됨
        child: Text('더 보기'),
      ),
    ],
  ),
)

새로고침 중에는 다른 데이터 요청을 막아야 데이터 충돌이나 중복 요청을 방지할 수 있습니다.

접근성 (Accessibility)#

키보드 인터랙션#

동작
해당 없음제스처 기반 컴포넌트; 키보드 대안은 새로고침 버튼으로 제공 권장

스크린 리더#

  • Flutter: 새로고침 상태 변경 시 SemanticsService.announce("새로고침 완료") 호출
  • Web: aria-live="polite" 영역에 새로고침 상태 메시지 업데이트

터치 타겟#

  • 제스처 기반 컴포넌트로 별도 터치 타겟 없음
  • 접근성을 위해 화면 상단에 새로고침 버튼을 추가로 제공 권장

크로스 플랫폼 차이점 (Platform Differences)#

항목FlutterWeb
클래스명CouiRefreshTriggerRefreshTrigger
인디케이터CircularProgressIndicatorCSS 스피너 또는 커스텀
제스처 감지RefreshIndicator 래핑Touch/Pointer Events
오버스크롤플랫폼 기본 오버스크롤overscroll-behavior CSS
  • Loading: 페이지 또는 섹션 로딩 중 스피너/스켈레톤 표시
  • Progress: 데이터 로딩 진행률을 퍼센트로 표시

조합 예제#

// 피드 화면에 RefreshTrigger 적용
Scaffold(
  appBar: AppBar(
    title: const Text('피드'),
    actions: [
      // 키보드 접근성을 위한 새로고침 버튼
      IconButton(
        onPressed: handleManualRefresh,
        icon: const Icon(Icons.refresh),
        tooltip: '새로고침',
      ),
    ],
  ),
  body: CouiRefreshTrigger(
    onRefresh: handleRefresh,
    color: Theme.of(context).colorScheme.primary,
    child: ListView.builder(
      itemCount: feedItems.length,
      itemBuilder: (context, index) => FeedCard(item: feedItems[index]),
    ),
  ),
)