hintful 0.7.0 copy "hintful: ^0.7.0" to clipboard
hintful: ^0.7.0 copied to clipboard

Onboarding tours and tooltips for Flutter. Registry-based targets, theme-aware coach marks, smart positioning, accessible by default — zero cost when idle.

hintful #

pub.flutter-io.cn pub points license CI

Hints & onboarding tours for Flutter. Spotlight targets, tooltips, coach marks, guided walkthroughs — a single source of truth for teaching users your product.

You wrap one widget in HintTarget, describe what to show in a HintTour, and the engine renders, repositions and remembers it — without a single hand-written overlay, scroll math or duplicated per-screen styling.

See it in action #

A hintful tour running over the example app

Recorded on the example/ app. Try it live: fellmonkey.github.io/hintful.

Index #

Start hereSee it in action · Why hintful · What you write · Fast

What it doesZero-config, then total control · Diagnosis over mystery · Accessibility · Works anywhere · Features · Server-driven tours

Install & docsGetting started · Documentation · best practices · FAQ · Performance


Why hintful #

Every Flutter hint/tour library you've seen is built on the same two ideas: GlobalKey + a full-screen OverlayEntry that the library manually positions, scrolls and lays out. That is exactly where tours break: the tooltip drifts a pixel off or covers the control it points at, the overlay goes off-screen mid-scroll and dies with This widget has been unmounted, and on a first run it silently gives up because the target isn't built yet.

hintful throws that model away.

What's different #

Old way (GlobalKey + overlay) hintful
Manual position / scroll / re-layout CompositedTransform — tooltip and scrim follow the target every frame, zero scroll math, overflow impossible
References to widget contexts Registry by idHintTarget(id: 'filters') registers/unregisters itself; nothing to unmount
"Wait until the widget is built" by hand Wait-for-target — a tour waits for a deferred target instead of dying
Per-hint hard-coded styling ThemeExtension — hint inherits your design system, light and dark, from Theme.of
Tied to Bloc/Riverpod/… Framework-agnostic core — vanilla ValueListenable<HintState>, no state-management imports
Overlay mounted even when idle Zero-idle cost — zero engine widgets in the tree until a tour actually starts

Zero-idle is about the engine: no overlay, entry or listener exists until a tour starts. The thin HintTarget wrapper around your widget is the only idle footprint — that's the 4 nodes in the S1 row of the benchmark table below.

What you write #

// 1. Wrap the thing you want to explain
HintTarget(
  id: 'exerciseSelector',
  child: ExerciseSelector(),
)
// ...or the one-liner sugar: ExerciseSelector().withHint('exerciseSelector')

// 2. Declare the tour — data, not widgets
final introTour = HintTour(
  id: 'intro',
  steps: [
    HintStep(
      targetId: 'exerciseSelector',
      title: 'Pick a movement',
      description: 'Filter by muscle, equipment or name.',
    ),
    HintStep(
      targetId: 'addSet',
      title: 'Log your set',
      description: 'Weight × reps, one tap.',
    ),
  ],
);

// 3. Wire once, show once
final controller = HintController(
  overlayHostBuilder: defaultOverlayHost(),
);
controller.start(introTour);

No GlobalKey, no OverlayEntry, no ScrollController, no manual position. That's the whole tour — and it already handles light/dark, scrolling and deferred targets.

Localizing? Swap title/description for titleBuilder/descriptionBuilder ((c) => AppLocalizations.of(c)!.introTitle): the copy stays in your AppTours file and the BuildContext arrives from the overlay.

// Just one tip? No tour needed:
controller.showHint(
  HintStep(targetId: 'addSet', title: 'Swipe left to delete a set'),
);

Fast — measured, not promised #

One scene, three libraries, profile Android emulator — recorded by CI into benchmark/benchmarks.json, rendered straight from that file into the table below — one source of truth for every number. Table, charts, methodology: Performance.

Zero-config, then total control #

Out of the box, title/description steps render in a default tooltip under a default theme — the tour above is already complete. When you need more, the API grows rung by rung, each optional: HintTheme styles → HintTooltipLabels (button texts, waiting placeholder, screen-reader announcements) → titleBuilder/descriptionBuilder for l10n → a fully custom tooltip through tooltipBuilder. Your design system, your call.

Diagnosis over mystery #

When a hint doesn't show, you'll know why in one log line:

[hintful] statsIntro step 2 not shown: timeout (target 'statsPeriodSelector') — target 'statsPeriodSelector' did not appear within 0:00:03.000000

Not "it just didn't appear." If you typo a targetId, hintful tells you loudly in debug — with the closest candidates.

The reasons and their fixes: FAQ §1; wiring your own handler for analytics: best practices §12.

Accessibility, on by default #

  • Screen readers: every step is announced as "Step N of M:
1
likes
160
points
459
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Onboarding tours and tooltips for Flutter. Registry-based targets, theme-aware coach marks, smart positioning, accessible by default — zero cost when idle.

Repository (GitHub)
View/report issues

Topics

#flutter #onboarding #tooltip #coach-marks #walkthrough

License

MIT (license)

Dependencies

flutter

More

Packages that depend on hintful