패키지, 저장소 및 앱 카테고리 검색

패키지 상세v0.6.2

features_tour

Features Tour는 앱 내 위젯 기능을 순서대로 소개하기 위해 쉽게 투어를 만들 수 있도록 해주는 패키지입니다.

AndroidiOSWebmacOSWindowsLinux
30일 다운로드
1.5천
좋아요
52
Pub 점수
150

패키지 설치

flutter pub add features_tour

패키지 개요

공개 정보를 바탕으로 생성한 선택 가이드입니다.

Features Tour는 Flutter 패키지로, 개발자가 앱 기능을 단계적으로 소개하는 안내 투어를 만들 수 있도록 도와줍니다. 사용자는 특정 위젯을 사용자 정의 가능한 툴팁, 화살표 및 애니메이션으로 강조 표시하여 구조적인 순서로 핵심 기능을 안내할 수 있습니다. 새로운 사용자 온보딩이나 고급 기능을 소개하는 데 이상적이며, Android, iOS, Web, macOS, Windows, Linux 등 여러 플랫폼을 지원합니다. 이 패키지는 가볍고 기존의 Flutter 프로젝트에 쉽게 통합할 수 있습니다.

사용 사례

  • 신규 사용자 온보딩
  • 앱 기능 강조
  • 안내형 튜토리얼
  • 사용자 교육
  • 기능 안내

주요 기능

  • 단계별 투어 탐색
  • 사용자 정의 가능한 툴팁 및 하이라이트
  • 다중 플랫폼 지원
  • 부드러운 애니메이션
  • 기존 위젯과의 간편한 통합
  • 사용자 정의 가능한 투어 흐름

적합한 대상

  • 앱 온보딩 화면
  • 교육 앱
  • 제품 데모
  • 새 기능 소개
  • 사용자 참여 도구

고려 사항

  • 복잡한 UI의 경우 추가 스타일링이 필요할 수 있습니다
  • 투어 순서는 사전에 정의되어야 합니다
  • 많은 단계로 인한 성능 영향
  • 제한된 내장 분석
  • 사용자 정의 로직 없이 동적 콘텐츠에 적합하지 않습니다

원본 README

연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.

Features Tour

codecov

Features Tour is a package that enables you to easily create tours to introduce your widget features in your app with order. It provides a straightforward and flexible API that allows you to customize the look and feel of the tour to match your app's design.

Introduction

Demo: pub.lamnhan.dev/features_tour

Quick Start

Create a controller
final tourController = FeaturesTourController<HomeTourStep>('HomePage');

Use a stable page name. The controller persists tour progress by page, so changing the name will make the tour look new again.

Wrap a feature

Prefer enum-based steps. The enum declaration order becomes the tour order, and the step name becomes the cache key.

enum HomeTourStep {
    drawer,
    drawerButton,
    settings,
    list,
    firstItem,
    lastItem,
    dialogButton,
    restart,
    addButton,
}

FeaturesTour<HomeTourStep>(
    controller: tourController,
    step: HomeTourStep.drawer,
    nextStep: HomeTourStep.drawerButton,
    introduce: const Text('Tap here to open the drawer'),
    onAfterAction: (action) {
        if (action case TourAction.next || TourAction.done) {
            scaffoldKey.currentState?.openDrawer();
        }
    },
    child: IconButton(
        icon: const Icon(Icons.menu),
        onPressed: () => scaffoldKey.currentState?.openDrawer(),
    ),
)

Use onBeforeAction to prepare the current surface, for example by scrolling a widget into view. Use onAfterAction when the current step should open a drawer, dialog, or another surface before the next step appears.

Start the tour
@override
void initState() {
    super.initState();
    tourController.start(
        context,
        firstStep: HomeTourStep.drawer,
        firstStepTimeout: const Duration(seconds: 3),
    );
}

firstStep is the preferred entry point. The controller still accepts firstIndex and firstIndexTimeout for migration, but new code should use the enum-based API. Instantiate the controller and widgets with the same enum type so step, nextStep, and firstStep stay type-safe.

Global Configuration

Set global defaults in main() before the app renders. The example app uses custom dialog labels, custom buttons, rounded introduce styling, and disabled child animation:

void main() {
    FeaturesTour.setGlobalConfig(
        preDialogConfig: PreDialogConfig(
            enabled: true,
            title: 'Welcome to Features Tour',
            content: 'This tour will guide you through the main features of the app.',
            applyToAllCheckboxLabel: 'Apply to all pages',
            acceptButtonLabel: 'Start Tour',
            laterButtonLabel: 'Later',
            dismissButtonLabel: 'Dismiss',
        ),
        introduceConfig: RoundedRectIntroduceConfig(),
        childConfig: ChildConfig(isAnimateChild: false),
        skipConfig: SkipConfig(
            builder: (context, onPressed) => ElevatedButton.icon(
                onPressed: onPressed,
                icon: const Icon(Icons.skip_next),
                label: const Text('SKIP'),
            ),
        ),
        nextConfig: NextConfig(
            builder: (context, onPressed) => FilledButton.icon(
                onPressed: onPressed,
                icon: const Icon(Icons.arrow_forward),
                label: const Text('NEXT'),
            ),
        ),
        previousConfig: PreviousConfig(
            builder: (context, onPressed) => FilledButton.icon(
                onPressed: onPressed,
                icon: const Icon(Icons.arrow_back),
                label: const Text('PREVIOUS'),
            ),
        ),
        doneConfig: DoneConfig(
            builder: (context, onPressed) => FilledButton.icon(
                onPressed: onPressed,
                icon: const Icon(Icons.done),
                label: const Text('DONE'),
            ),
        ),
        debugLog: true,
    );

    runApp(const ChangeableThemeMaterialApp());
}

Use ChildConfig for the highlighted area, IntroduceConfig for the callout, and the button configs for custom navigation affordances. PreDialogConfig controls the pre-tour confirmation dialog, including the optional apply-to-all-pages behavior.

Flow and Callbacks

TourAction describes the user action that reached or ended a step: introduce, next, previous, done, skip, disabled, and notMounted.

onBeforeAction runs before the step is introduced. The widget must already be mounted when this hook runs, so use it only for visible preparation like scrolling a list into view or reopening a surface that is already part of the widget tree. Do not use it to open a dialog for the current widget; trigger that from the previous step's onAfterAction instead. The first appearance of a step receives TourAction.introduce; returning to it from the next step receives TourAction.previous.

onAfterAction runs after the user chooses a navigation action from the current step. This is where the example app opens the drawer for the next surface, closes the drawer when going back, or opens a dialog after the last list item.

onStateChanged lets you observe the tour lifecycle. Common states include TourInProgress, TourIntroducing, TourPreDialogShownDefault, TourPreDialogShownCustom, TourCompleted, and TourEmpty.

tourController.start(
    context,
    onStateChanged: (state) {
        switch (state) {
            case TourIntroducing(step: final step):
                debugPrint('Introducing: $step');
            case TourCompleted():
                debugPrint('Tour finished');
            default:
                break;
        }
    },
);

Advanced Flows

Drawer and dialog tours

Open the target surface from the previous step and let nextStep wait for the new widget to appear. This is the pattern used in the example app and in the drawer/dialog tests.

FeaturesTour<HomeTourStep>(
    controller: tourController,
    step: HomeTourStep.drawerButton,
    nextStep: HomeTourStep.settings,
    onAfterAction: (action) {
        if (action case TourAction.next || TourAction.done) {
            scaffoldKey.currentState?.openDrawer();
        }
    },
    child: ElevatedButton(
        onPressed: () => scaffoldKey.currentState?.openDrawer(),
        child: const Text('Open drawer'),
    ),
)

For dialogs, open the dialog from the prior step, then use a FeaturesTour inside the dialog content for the dialog-specific step.

Scroll-driven steps

Use onBeforeAction to scroll the current item into view before the introduction starts or when the user returns from the next step. The widget must already be mounted when this callback runs, so it is not a good place to open a dialog for the current step.

FeaturesTour<HomeTourStep>(
    controller: tourController,
    step: HomeTourStep.lastItem,
    onBeforeAction: (action) async {
        if (action case TourAction.next || TourAction.previous) {
            await scrollController.animateTo(
                scrollController.position.maxScrollExtent,
                duration: const Duration(milliseconds: 300),
                curve: Curves.easeInOut,
            );
        }
    },
    child: Padding(
        padding: const EdgeInsets.all(16),
        child: Text('Item 90'),
    ),
)
Conditional steps in lists

When multiple list items share the same step, set enabled: true on the one item that should participate in the tour and enabled: false on the rest.

Bottom edge padding

Use FeaturesTourPadding to keep navigation buttons from overlapping bottom-aligned widgets. Prefer steps for enum-based tours.

FeaturesTourPadding(
    controller: tourController,
    steps: const {
        HomeTourStep.restart,
        HomeTourStep.addButton,
    },
)

indexes is still supported for migration, but steps is the preferred API.

Customization Notes

IntroduceConfig can be customized with a builder, barrier color builder, padding, quadrant alignment, alignment, and root overlay support. RoundedRectIntroduceConfig() is the preset to use when you want a rounded callout with better contrast out of the box.

ChildConfig supports a custom wrapper builder, background color, shape border, animation settings, zoom scale, and border inflation around the highlighted child.

PreDialogConfig supports custom labels, button styles, callbacks, and a customDialogBuilder if you want to replace the default dialog entirely.

Migration Notes

0.6.0-0.7.0
  • Instantiate the new generic API as FeaturesTourController<YourStepEnum>('PageName') and FeaturesTour<YourStepEnum>(...).
  • Prefer step over index.
  • Prefer nextStep and nextStepTimeout over nextIndex and nextIndexTimeout.
  • Prefer FeaturesTourPadding.steps over FeaturesTourPadding.indexes.
  • Prefer onBeforeAction and onAfterAction over the deprecated onBeforeIntroduce and onAfterIntroduce callbacks.
  • Prefer onStateChanged over the deprecated onState callback. The older onState parameter is still supported for migration but will be removed in a future release.
  • If you don't want to use the previous action button, you can use this setting to keeps the old behavior:
    FeaturesTour.setGlobalConfig(
      previousConfig: PreviousConfig(enabled: false),
      skipConfig: SkipConfig(alignment: Alignment.bottomLeft),
    );
    

Reference Examples

Contributions

Contributions to this project are welcome. Open an issue or submit a pull request if you find a bug or want to improve the package.

카테고리

의존성

의존성 목록 2 개

소스 저장소

이 패키지를 사용하는 프로젝트