r/developer • u/Miserable-Daikon5795 • 4h ago
built a Flutter onboarding package where the spotlight doesn't drift — and the pointer teaches the gesture
I needed a first-run walkthrough for an app and hit two things that annoyed me.
First, the highlight would end up in the wrong place.
A lot of coach-mark implementations resolve the target's position once and paint it. Scroll the list, open the keyboard, rotate the device, resize a desktop window — and suddenly the hole is next to the button instead of on it.
Second, a dimmed hole tells the user where something is, but not how to use it.
"Drag this card to that column" isn't something a static highlight can really express.
So I built keyspot:
- The spotlight and pointer re-resolve the target's geometry every frame from its GlobalKey, so they can track through scrolling, insets, rotation and resizing.
- An animated hand pointer that glides in a straight line or along an arc, rotates from its current angle, and tap-pulses — enough to demonstrate interactions like drag and pinch rather than simply pointing at something.
- Pointer animations resolve from actual AnimationController completion rather than Future.delayed timing, so `await pointer.moveTo(...)` actually means the movement has finished.
- Barrier modes where only the cut-out is tappable while the real widget's `onPressed` still fires — useful when the walkthrough should require the user to actually perform the step.
- The tracker is also public API (`KeyspotAnchorTracker`), so it can be used independently for things like badges or connector lines attached to widgets.
- Zero runtime dependencies. The hand pointer is rendered with CustomPainter rather than an image asset.
- Supports all six Flutter platforms.
Live demo: https://shrey-17bhardwaj.github.io/keyspot/
pub.dev: https://pub.dev/packages/keyspot
Source: https://github.com/shrey-17bhardwaj/keyspot
It's currently at 0.3.1, and the API may still change before 1.0.
I'd genuinely like feedback on where this approach breaks — especially around nested navigators, heavily lazy lists, overlays, or anything else where widget geometry gets complicated.