showcase_tutorial 1.15.0
showcase_tutorial: ^1.15.0 copied to clipboard
A Flutter package to Showcase/Highlight widgets step by step.
Changelog #
1.15.0 #
- PERF: a step change no longer rebuilds every
Showcaseon the screen._InheritedShowCaseViewpublished the active step's key as a plainInheritedWidget, so everyShowcasein the tree was a dependent and every one of them rebuilt whenever the tour advanced — along with theAnchoredOverlayandOverlayBuildereach wraps. The cost grew with the tour: measured at119 + 3Nelement rebuilds per "next" for anN-step tour, or 299 rebuilds in a 60-step tour, where only two steps can actually have changed. It is now anInheritedModeland eachShowcasedepends on its own key as an aspect, so a step change notifies just the step being left and the step being entered. The rebuild count is a flat 125 at every tour length (-58% at 60 steps, -40% at 30), and the same fix takes starting a tour from84 + 3Nto a flat 102. - FEAT:
ShowCaseWidget.activeTargetWidgettakes an optionalaspect. Pass a step'sGlobalKeyto depend on that step alone, so your widget rebuilds only when that step becomes active or inactive rather than on every step change. Omitting it keeps the previous behaviour of rebuilding on any step change, so existing callers are unaffected. - Honest scope: this removes work, not frame time. Frame CPU, step latency and idle element counts are unchanged, and on an Infinix X687 (Android 10, profile mode) the before and after are indistinguishable — raster dominates the frame and the rebuilds removed here were cheap ones. The win is UI-thread headroom that no longer scales with the number of steps on screen.
- Adds two regression tests: that advancing a step rebuilds exactly two
Showcasewidgets whatever the tour's length, and that a dependant which does not name a step still rebuilds on every step change. - FEAT: tour-wide action buttons.
ShowCaseWidget.globalActionsdeclares the tooltip's Previous / Stop / Next row once for the whole tour instead of repeatingactions:on everyShowcase.globalActionSettingsandglobalActionButtonsPositiondo the same for the styling and the absolute placement, andhideActionsForShowcasetakes theGlobalKeys of steps that should not show them — the same patternglobalFloatingActionWidgetandhideFloatingActionWidgetForShowcasealready use. A per-stepShowcase.actionsstill wins, and the styling and placement fall back independently, so one step can change only its buttons and keep the tour's look. - FEAT:
TooltipActionPosition— action buttons can now be drawn inside the tooltip box, flowing below the description and the progress footer, with the tooltip growing to contain them. Set it tour-wide withShowCaseWidget.actionsPositionor per step withShowcase.actionsPosition. Defaults toTooltipActionPosition.outside, the absolute placement actions have always used, so nothing changes for existing tours. Inside actions reserve 40 logical pixels of height unlessActionsSettings.containerHeightsays otherwise, because a widget you supply cannot be measured the way the title and description are. Showcase.actionSettingsnow defaults tonullrather than an all-nullActionsSettings(), which is what lets a step fall back toglobalActionSettings. The field was already nullable and every read inside the package was null-safe, so rendering is unchanged.-
- FIX:
enableShowcasenow takes effect at runtime. Flipping it tofalsewhile a tour was running left the overlay on screen:ShowCaseWidget.builderis a single stored widget instance, so nothing below it rebuilt and noShowcaseever learned the tour had been switched off. The flag is published through_InheritedShowCaseViewnow, so disabling mid-tour hides the overlay and ends the tour — the step that was showing gets itsShowcase.onDismiss, and the tour-levelShowCaseWidget.onDismissreports the step the user was left on, keeping the "exactly one ofonFinish/onDismissper tour" rule. Re-enabling does not resume the dismissed tour; callstartShowCaseagain. This matches what the flag already meant at construction time, wherestartShowCasethrows outright. Covered by four new tests.
- FIX:
1.14.1 #
A performance pass over the showcase overlay. No API changes — every item below is internal, and behaviour is unchanged.
- PERF: an idle
Showcaseno longer mounts an overlay entry. EveryShowcaseused to insert its ownOverlayEntryas soon as it was built and keep it for the life of the route, so a screen with 30 steps carried 30 entries — each rebuilt on every ancestor rebuild only to return an empty box. Only the step that is actually showing mounts one now, and it is removed when the tour moves on. - PERF: the target snapshot is captured once per step instead of once per
rebuild.
highlightExactShapeand multi-widget (keys) steps used aFutureBuilderwhose future was created insidebuild, so every rebuild re-ranRenderRepaintBoundary.toImagefollowed by a full PNG encode and decode — several times a second while the tooltip animates — and leaked everyui.Imageit produced. The capture now happens once when the step opens, the PNG round trip is gone (the image is painted directly), and the images are disposed when the step closes. The snapshot's position is still re-read each build, so it keeps tracking a target that scrolls. - PERF: the target's geometry is measured once per frame. Laying out a tooltip asks for the target's center, top, bottom and height dozens of times, and each ask used to walk the render tree again. A single measurement is now shared for the frame — roughly 23x fewer render-tree walks per rebuild in a 20-step tour.
- PERF: the tour's state is resolved once per overlay build. Building the
overlay read ~20 values off
ShowCaseWidget.of(context), and each one was a freshfindAncestorStateOfTypewalk to the top of the element tree. - PERF: no more
LayoutBuilderaround every showcased widget. It added a render object perShowcaseand rebuilt the widget's whole subtree during layout on any constraint change (rotation, keyboard, window resize), while its constraints were never used. - PERF: the tooltip is painted on its own layer, so the looping "moving"
animation translates a cached layer instead of re-rasterising the tooltip's
text, arrow and background every frame; the dimmed scrim is a plain
ColoredBoxrather than a decoratedContainer; and theTextPainterused to measure tooltip text is disposed instead of leaking its native paragraph.
1.14.0 #
- FEAT: dynamic callback registration — register step listeners on the
controller at runtime with
addOnStartCallback/removeOnStartCallbackandaddOnCompleteCallback/removeOnCompleteCallback. UnlikeShowCaseWidget.onStart/onComplete, which are fixed when theShowCaseWidgetis built, these let a screen, controller, or analytics service deeper in the tree observe the tour for as long as it lives (register ininitState, remove indispose). Listeners run in addition to the widget-level callbacks, in registration order, and receive the same(int? index, GlobalKey key)— now typed asShowcaseStepCallback. Removing a listener while it is being dispatched is safe. - FEAT:
Showcase.onTargetRectUpdate— called with the highlighted target's bounds (aRectin global coordinates) whenever they change while the step is active: after a scroll, a rotation, the keyboard opening, or the target resizing. Fires once with the initial bounds when the step becomes active and is delivered after layout, so it is safe tosetStatefrom it. Useful for anchoring your own UI — e.g. afloatingActionWidgetthat should follow just below the highlight. The rect describes the target itself;targetPaddingis not included. Defaults tonull. - FEAT:
ShowCaseWidget.of(context).isTargetRendered(key)— controller helper that reports whether a step's target is currently mounted and laid out, replacing manualkey.currentContext != nullchecks (which are alsotruefor a mounted but not-yet-laid-out widget). Handy beforegoToKey/startShowCaseon steps whose targets render conditionally. - FEAT: per-step barrier override —
Showcase.barrierInteractionoverrides the tour-wideShowCaseWidget.barrierInteractionfor a single step, so one step can make the background inert (BarrierInteraction.none) while the rest of the tour advances on a background tap. Takes precedence over the tour-wide value, including the legacydisableBarrierInteractionflag;onBarrierClickstill fires on every barrier tap. Additive and backward-compatible — defaults tonull(use the tour-wide behaviour). - FEAT: pointer cursor on hover (web / desktop) — hovering a part of the
showcase that reacts to a click now shows
SystemMouseCursors.clickinstead of the plain arrow, so a tour reads as interactive on web and desktop. It applies to the highlighted target, a tooltip that hasonToolTipClickordisposeOnTap, and the built-in "Skip" button. A tooltip that does nothing on tap deliberately keeps the default cursor, so the pointer never promises a click that isn't there. The buttons inShowCaseDefaultActionsare Material buttons and already behaved this way. No-op on mobile (no pointer). - FEAT:
ShowCaseWidget.enablePointerCursor— tour-wide switch for the above, defaulting totrue(matchingenableKeyboardNavigationandenableAutoAnnouncements, the package's other on-by-default polish flags). Set it tofalseto keep the previous cursor behaviour everywhere. - FEAT:
Showcase.targetMouseCursor/Showcase.tooltipMouseCursor— per-step overrides of the resolved cursor, e.g.SystemMouseCursors.forbiddenon a "look, don't touch" step orMouseCursor.deferto leave one step alone. An explicit value wins even whenenablePointerCursorisfalse. Both default tonull(resolve automatically). - The target's hover region is not opaque, so the real widget underneath still receives hover events and keeps its own hover states while the showcase cursor applies.
- FEAT: animated step transitions — the highlight cut-out now glides from the
previous step's target to the next one when the tour advances, instead of
cutting there instantly. The optional highlight border and the pulse ring
follow the moving cut-out; the tooltip keeps its existing scale transition and
appears at the new target. Opt in tour-wide with
ShowCaseWidget.enableStepTransition(defaultfalse, so nothing changes for existing tours), and tune it withstepTransitionDuration(default 300 ms) andstepTransitionCurve(defaultCurves.easeInOut).- Applies to every forward and backward move —
next,previous,goTo,goToKey, a branch, a barrier tap, autoplay. The first step of a tour has nothing to glide from, so it simply appears. - Honors the platform "reduce motion" accessibility setting by jumping straight to the target, like the pulsing ring already does.
- No-op for a
highlightExactShapestep, which paints a snapshot of the target rather than a cut-out.
- Applies to every forward and backward move —
- FEAT:
ShowCaseWidget.of(context).previousTargetRect— the global bounds of the step the tour just left (nullwhile the tour is starting or once it ends). This is what drives the glide, and it is only recorded whileenableStepTransitionis on. - No shared-overlay rewrite was needed: every step already paints its own full-screen scrim, so the scrim is continuous across a step change and only the cut-out moves. The step being entered animates its own cut-out from where the previous target was, and the step being left simply stops painting.
1.13.0 #
- FEAT: floating action widget — pin a screen-anchored control (e.g. a fixed
Skip / Next button or a progress chip) above the overlay so it stays put while
the tour runs, instead of moving with each tooltip. Set one tour-wide with
ShowCaseWidget.globalFloatingActionWidget(aWidgetBuilder, so it can read the tour viaShowCaseWidget.of(context)), override it per step withShowcase.floatingActionWidget, and suppress the global one on specific steps withShowCaseWidget.hideFloatingActionWidgetForShowcase. You position the widget yourself (e.g. withAlign/Positioned); it is painted above the tooltip and receives taps. Additive and backward-compatible (defaultsnull/ empty). - FEAT: per-step
autoPlayDelay—Showcase.autoPlayDelayoverrides the tour-wideShowCaseWidget.autoPlayDelayfor a single step, so one step can linger longer (or advance quicker) than the rest during auto-play. Defaults tonull(use the tour-wide delay). As part of this, the auto-play delay now uses the fullDurationinstead of being truncated to whole seconds. - FEAT:
targetTooltipGap—Showcase.targetTooltipGapadds extra space (in logical pixels) between the target and its tooltip, on top of the default offset. It applies to every tooltip position (top / bottom / left / right). Additive and backward-compatible — defaults to0, which keeps the original spacing. - FEAT:
toolTipMargin—Showcase.toolTipMargin(anEdgeInsets, defaultEdgeInsets.all(20)) sets the minimum margin kept between the tooltip and the screen edges: the tooltip is clamped to stay at least this far from each edge, and its width/height are capped to fit within the margins. Useful to leave room for a status bar, notch, or your own fixed UI. Completes the tooltip-spacing pair withtargetTooltipGap. Backward-compatible — the default reproduces the previous edge spacing for ordinary tooltips. Also applies to the customShowcase.withWidgetcontainer, which is now clamped within the same margins. - FEAT:
scrollAlignment— control where an auto-scrolled target lands in the viewport.ShowCaseWidget.scrollAlignment(adouble, default0.5) sets it tour-wide andShowcase.scrollAlignmentoverrides it per step:0.0rests the target at the leading edge (top / left),0.5centers it,1.0rests it at the trailing edge (bottom / right). Forwarded toScrollable.ensureVisiblewhenShowCaseWidget.enableAutoScrollbrings an off-screen target into view. Additive and backward-compatible — the default0.5reproduces the previous centered behavior. - CHORE: the example app's "Feature demos" page now demonstrates the floating
action widget (a pinned "End tour" button, hidden on the last step), per-step
autoPlayDelay(an "Auto-play" toggle where the star step lingers longer), a "Wide tooltip gap" toggle fortargetTooltipGapon the center step, and a "Wide tooltip margin" toggle that pushes the edge-hugging "R" step's tooltip further in from the screen edge. - CHORE: the example app adds an "Auto-scroll alignment" demo page (reachable from
the "Feature demos" screen) where three far-apart targets in a scroll view each
land at a different spot — leading edge, center, trailing edge — via per-step
scrollAlignment.
1.12.0 #
- FEAT:
onBarrierClick— a newShowCaseWidget.onBarrierClickcallback fires whenever the dimmed background (barrier) is tapped, in addition to the configuredbarrierInteraction. It runs even whenbarrierInteractionisBarrierInteraction.none, so you can react to "the user tapped outside the highlight" (a hint nudge, a sound, analytics) without changing what the tap does; with.next/.dismissit runs first, then the configured action follows. Additive and backward-compatible (defaultnull).
1.11.0 #
- FEAT: tour-level
onDismiss— a newShowCaseWidget.onDismiss(GlobalKey? dismissedAt)callback fires whenever a tour is closed early (a barrier tap withBarrierInteraction.dismiss, theEsckey, the built-in skip button, adisposeOnTaptap, or a manualdismiss()), and reports theGlobalKeyof the step the user left off on. It is not called when the tour finishes normally by advancing past the last step —onFinishstill covers that, and exactly one of the two runs per tour. Handy for measuring onboarding drop-off. Additive and backward-compatible; distinct from the per-stepShowcase.onDismiss.
1.10.1 #
- DOCS: rewrote the README as standalone documentation — added a table of
contents, a fuller API reference (controller methods and getters, complete
ShowCaseWidgetandShowcase/Showcase.withWidgetproperty tables).
1.10.0 #
- FEAT: conditional / branching tours — a new
ShowCaseWidget.onResolveNextStepcallback lets a step decide the next step at runtime, so a tour can skip ahead or branch based on app state (e.g. "if the user already has items, jump to the checkout step"). It's consulted on every forward path (the Next button, a tap, the barrier, the keyboard, auto-play, andnext()); return theGlobalKeyof the step to jump to, ornullto advance normally. Backward and forward jumps are both allowed, and a branch is treated as an explicit jump (likegoTo).previous(),goTo(), andgoToKey()are unaffected. Additive and backward-compatible — the default isnull(no branching).
1.9.0 #
- FEAT: numeric progress indicator —
ShowCaseWidget.progressStylechooses how the built-in step indicator looks whenshowProgressis on:ShowcaseProgressStyle.dots(one dot per step, the existing default) orShowcaseProgressStyle.numeric(a compact1/6counter, handy for long tours). Additive and backward-compatible — the default is unchanged.
1.8.0 #
- FEAT: tooltip & highlight styling — finer visual control for the default
tooltip without a custom
container. New per-Showcaseoptions (each also settable tour-wide viaShowcaseStyle):arrowColor,arrowWidth,arrowHeightfor the tooltip arrow, andhighlightBorderColor/highlightBorderWidthto draw a colored border around the highlighted target. All additive and opt-in; the border follows the highlight shape and works alongsidehighlightExactShape. (Per-step overlay color is already supported viaShowcase.overlayColor.)
1.7.0 #
- FEAT: pulsing highlight ring — opt in per step with
Showcase(enablePulseAnimation: true)to draw an animated ring that pings outward around the highlight, drawing the eye to the target. Tune it withpulseColor(also settable tour-wide viaShowcaseStyle.pulseColor) andpulseDuration. Additive and off by default; the ring follows the highlight shape, works alongsidehighlightExactShape, and falls back to a single static ring when the platform "reduce motion" setting is on.
1.6.2 #
- DOCS: full dartdoc coverage of the public API — every exported class, field,
enum value, and method now has a
///comment — plus a fix for a few stale doc references. Improves the pub.dev documentation score and the docs tab. - DOCS: add an "Upgrading (1.4 → 1.6)" section to the README summarising what landed across those releases and how to opt in (all additive, no code changes).
- DOCS: tidy the README markdown-lint warnings (aligned the property-table pipes and fixed the ordered-list prefix in the Installing section).
- CHORE: losslessly optimise the preview GIFs with
gifsicle -O3—demo.gif4.6 MB → 0.4 MB andshowcase_tutorial.gif2.6 MB → 0.5 MB (pixel-identical) — and shipdemo.gifas a second pub.dev screenshot. - CHORE: add a
.pubignorethat excludes the example app's native scaffolding, the maintainer publish script, and internal docs from the published archive, dropping the package download from ~7 MB to ~0.9 MB.
1.6.1 #
- DOCS: fix the README preview GIFs.
1.6.0 #
- FEAT: built-in progress indicator and skip button in the default
tooltip, via
ShowCaseWidget.showProgressandShowCaseWidget.showSkip(label customizable withskipButtonText). The progress shows one dot per step with the active step highlighted; the skip button dismisses the whole tour. Both default tofalseand only affect the default tooltip — customcontainertooltips are untouched.
1.5.1 #
- DOCS: add a second example page ("Feature demos"), reachable from a button on
the original mail demo, that walks through the newer features (left/right
tooltip positions, progress indicator, multi-widget highlight, custom action
text,
highlightExactShape,onShow/onDismiss,barrierInteraction, auto-skip). Add a feature-walkthrough GIF to the README preview and complete the README's feature list. No library changes.
1.5.0 #
- FEAT: keyboard navigation (
ShowCaseWidget.enableKeyboardNavigation, defaulttrue) — drive the active step with a hardware keyboard:Escdismisses,→/↓/Entergo to the next step,←/↑go back. Focus-scoped, so it only acts while the overlay holds focus (never hijacks app-wide keys). Relevant on web/desktop, harmless on mobile. - FEAT: screen-reader announcements (
ShowCaseWidget.enableAutoAnnouncements, defaulttrue) — each step's title and description are announced to TalkBack/VoiceOver as it becomes active.Showcase.semanticLabeloverrides the announced text (useful for custom-containertooltips). - FIX:
Showcase.onShow/onDismiss(added in 1.4.0) could throw "setState() called during build" when the callback calledsetState(e.g. to update a "Step x of y" indicator), which cascaded into "GlobalKey used multiple times" errors. The callbacks are now dispatched after the frame.
1.4.0 #
- FEAT:
TooltipPosition.leftand.right— place the default tooltip to the side of the target, with a horizontal arrow. - FEAT: progress + navigation API on
ShowCaseWidget.of(context):currentIndex,totalSteps,isShowcaseRunning,goTo(index)andgoToKey(key)(build "Step 2 of 5" indicators and skip-to controls). - FEAT:
ShowCaseWidget.autoSkipUnmountedSteps— skip steps whose target widget isn't currently in the tree instead of showing an empty overlay. - FEAT: RTL support — the tooltip inherits the app's text direction and measures/lays out RTL text correctly.
- FEAT:
Showcase.highlightExactShape— highlight the target by its actual painted shape (a star, a pill, an icon, an irregular logo) instead of a geometrictargetShapeBorder. The target is captured as a snapshot and drawn above the dimmed overlay, so any shape is hugged exactly with no need to settargetShapeBorder/targetBorderRadiusto match it. - FEAT: per-step lifecycle callbacks
Showcase.onShowandShowcase.onDismiss— fired when a step becomes the active showcase and when it stops being active (advanced past, navigated away, or the tour is dismissed). Handy for analytics. - FEAT:
ShowCaseWidget.barrierInteraction(BarrierInteraction.next/.dismiss/.none) — choose whether tapping the dimmed background advances to the next step (default), dismisses the whole tour, or does nothing. The legacydisableBarrierInteraction: truestill works and maps to.none.
1.3.0 #
- FEAT: "show once" support for onboarding tours.
ShowCaseWidgetgains ashowcaseIdand anonShouldStartShowcaseguard (sync or async).startShowCaseconsults the guard and starts only when it returnstrue, so a tour can be shown a single time. PassstartShowCase(..., force: true)to replay (e.g. a "show tutorial again" button). The package stays storage-agnostic — persist completion yourself inonFinish.
1.2.1 #
- DOCS: rewrite the README with a Features overview and runnable examples for
every feature (custom tooltips, action buttons, multi-widget steps,
ShowcaseStyle, auto-play, programmatic control, target interactions, blur, tooltip position, enable/disable). Correct the property tables and remove the stale pre-1.0.0 migration guide.
1.2.0 #
- FEAT: add
ShowCaseWidget(style: ShowcaseStyle(...))to set default tooltip styling (tooltipBackgroundColor,textColor,titleTextStyle,descTextStyle,tooltipBorderRadius) once for everyShowcasein the tree. An individualShowcasestill overrides any value it sets. - FEAT:
Showcase.descriptionis now optional. A showcase can show just a title (or a customcontainer) without passingdescription: null. - BREAKING (minor):
Showcase.tooltipBackgroundColorandShowcase.textColorare now nullable (Color?) so they can fall back toShowcaseStyle. Code that passes these as named arguments is unaffected; only code that read the fields expecting a non-nullColorneeds a null check. - FIX: the overlay barrier was being painted twice, so the default
(non-blurred) overlay rendered at roughly double the configured opacity.
It is now drawn once at the requested
overlayColor/overlayOpacity. - FIX:
ActionsSettings.containerColoris now honoured for tooltip action buttons. Previously the action container used a hardcoded background (Colors.white/Colors.lightBlueAccent) and ignored the setting. - FIX: guard
GetPosition.getRect()against a null/unsized render object so it returnsRect.zeroinstead of throwing during teardown. - FIX: multi-widget showcases (
Showcase(keys: ...)) now skip an individual missing/unmounted widget instead of dropping every highlight for the step. - PERF:
MeasureSizenow reads its size during layout via aRenderProxyBoxinstead of scheduling a post-frame measurement on every build, and the overlay no longer schedules a rebuild callback while no showcase is active. - DOCS: document the multi-widget
keysparameter. - CHORE: correct the
flutterSDK constraint (>=3.27.0, required byColor.withValues) and addtopicsandscreenshotsto the pubspec.
1.1.2 #
- FIX: guard
_scrollIntoViewagainst a use-after-dispose crash. AShowcasedisposed within a frame of its first build (for example, a redirect right after the first build) no longer throws "Null check operator used on a null value" from its post-frame callback. - CHORE: upgrade
flutter_lintsto^6.0.0and resolve the newly surfaced lints.
1.1.1 #
- Example app: add an
isImportantfield to the mail model and refine theMailTileand detail screen styling. - Docs: fix the GitHub stars link in the README.
1.1.0 #
- FEAT: update dependency constraints to Dart SDK 3.9.0.
- Refactor the code structure for improved readability and maintainability.
- Fix minor bugs and improve performance.
- Update the documentation for new features.
1.0.4 #
- Update Flutter to 3.16.0.
1.0.0 #
- Initial release (14 Sep 2023).
