A production-ready Flutter package for creating guided tutorial overlays, onboarding walkthroughs, and step-by-step UI showcases.
Highlight any widget, show contextual tooltips, and guide users through your app with smooth animations -- all with minimal setup.
- Highlight any widget using
GlobalKeywith rectangle, rounded rectangle, or circle shapes - Step-by-step navigation -- next, previous, skip, jump to step, finish
- Auto-scroll targets into view when inside scrollable widgets
- Smooth animations -- overlay fade, highlight transition, tooltip scale-in, pulse effect
- Fully custom tooltips via builder callbacks, or use the polished default tooltip
- Persistence -- optional interface to remember completed tutorials across sessions
- Responsive -- adapts to orientation changes, different screen sizes, and safe areas
- Conditional steps -- show or hide steps dynamically with
showIf - Auto-advance -- automatically progress after a configurable duration
- Zero dependencies beyond Flutter SDK
Add to your pubspec.yaml:
dependencies:
tutorial_overlay_flutter: ^1.0.0Then run:
flutter pub getimport 'package:tutorial_overlay_flutter/tutorial_overlay_flutter.dart';
// 1. Attach GlobalKeys to your widgets.
final searchKey = GlobalKey();
final menuKey = GlobalKey();
final fabKey = GlobalKey();
// 2. Use the keys on your widgets.
IconButton(
key: searchKey,
icon: const Icon(Icons.search),
onPressed: () {},
)
// 3. Show the tutorial (e.g. in initState via addPostFrameCallback).
TutorialOverlay.show(
context,
steps: [
TutorialStep(
targetKey: searchKey,
title: 'Search',
description: 'Find anything in the app.',
),
TutorialStep(
targetKey: menuKey,
title: 'Menu',
description: 'Access navigation and settings.',
shape: HighlightShape.circle,
),
TutorialStep(
targetKey: fabKey,
title: 'Create',
description: 'Add a new item.',
shape: HighlightShape.circle,
padding: 12,
),
],
onFinish: () => print('Tutorial completed!'),
onSkip: () => print('Tutorial skipped.'),
);That's it. Three lines of setup, one call to show.
Customize the entire overlay with TutorialConfig:
TutorialOverlay.show(
context,
steps: steps,
config: const TutorialConfig(
// Overlay
overlayColor: Color(0xBB000000),
animationDuration: Duration(milliseconds: 400),
animationCurve: Curves.easeOutCubic,
// Highlight
enablePulseAnimation: true,
highlightBorderColor: Color(0xFF2196F3),
highlightBorderWidth: 3.0,
// Interaction
dismissOnOverlayTap: true,
enableAutoScroll: true,
// Tooltip
tooltipBackgroundColor: Color(0xFF1E1E2E),
tooltipBorderRadius: 16.0,
tooltipMaxWidth: 340.0,
tooltipSpacing: 20.0,
// Buttons
nextText: 'Continue',
previousText: 'Back',
skipText: 'Skip Tour',
finishText: 'Got it!',
showStepIndicator: true,
showNavigationButtons: true,
showSkipButton: true,
// Theme
primaryColor: Color(0xFF6C63FF),
),
);Replace the default tooltip for any step with your own widget:
TutorialStep(
targetKey: myKey,
tooltipBuilder: (context, info) => Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF6C63FF), Color(0xFF3F3D9E)],
),
borderRadius: BorderRadius.circular(16),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(info.title ?? '', style: const TextStyle(color: Colors.white)),
const SizedBox(height: 12),
GestureDetector(
onTap: info.isLast ? info.finish : info.next,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Text(info.isLast ? 'Done' : 'Next'),
),
),
],
),
),
)The StepInfo object gives you everything you need: next(), previous(), skip(), finish(), currentIndex, totalSteps, isFirst, isLast, and progress.
Get a reference to the controller for external navigation:
final controller = await TutorialOverlay.show(context, steps: steps);
// Navigate programmatically from anywhere.
controller?.next();
controller?.previous();
controller?.goToStep(2);
controller?.skip();Or create your own controller for full control:
final controller = TutorialController(
steps: steps,
config: const TutorialConfig(dismissOnOverlayTap: false),
onFinish: () => print('Done'),
);
TutorialOverlay.showWithController(context, controller);
// Later...
controller.next();Prevent showing the tutorial to users who have already completed it:
// 1. Implement TutorialPersistence (e.g. with SharedPreferences).
class MyPersistence implements TutorialPersistence {
@override
Future<bool> isCompleted(String id) async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool('tutorial_$id') ?? false;
}
@override
Future<void> markCompleted(String id) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('tutorial_$id', true);
}
@override
Future<void> reset(String id) async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('tutorial_$id');
}
}
// 2. Pass it when showing the tutorial.
TutorialOverlay.show(
context,
steps: steps,
tutorialId: 'home_onboarding',
persistence: MyPersistence(),
);An InMemoryPersistence class is included for development and testing.
Share a controller across the widget tree using TutorialScope:
TutorialScope(
controller: myController,
child: MyApp(),
)
// In any descendant widget:
final controller = TutorialScope.of(context);
controller.next();Show or hide steps dynamically:
TutorialStep(
targetKey: premiumKey,
title: 'Premium Feature',
description: 'Unlock advanced analytics.',
showIf: () => user.isPremium,
)Automatically move to the next step after a delay:
TutorialStep(
targetKey: splashKey,
title: 'Welcome!',
description: 'This tour will guide you through the app.',
autoAdvanceAfter: const Duration(seconds: 3),
)| Method | Description |
|---|---|
show(context, steps, ...) |
Show tutorial with simple API. Returns Future<TutorialController?>. |
showWithController(context, controller) |
Show tutorial with an existing controller. |
| Property | Type | Default | Description |
|---|---|---|---|
targetKey |
GlobalKey |
required | Key of the widget to highlight |
title |
String? |
null |
Tooltip title |
description |
String? |
null |
Tooltip description |
tooltipBuilder |
Widget Function(...)? |
null |
Custom tooltip widget |
shape |
HighlightShape |
roundedRectangle |
Cutout shape |
padding |
double |
8.0 |
Padding around highlight |
borderRadius |
double |
8.0 |
Corner radius (rounded rect) |
tooltipPosition |
TooltipPosition |
auto |
Preferred tooltip side |
autoAdvanceAfter |
Duration? |
null |
Auto-advance delay |
showIf |
bool Function()? |
null |
Conditional display |
onShow |
VoidCallback? |
null |
Called when step shown |
onDismiss |
VoidCallback? |
null |
Called when step dismissed |
| Property/Method | Description |
|---|---|
currentIndex |
Current step index |
status |
idle, active, completed, or skipped |
isActive |
Whether tutorial is currently showing |
currentStepInfo |
StepInfo for the current step |
start() |
Begin the tutorial |
next() |
Advance to next step |
previous() |
Go back one step |
goToStep(index) |
Jump to a specific step |
skip() |
Skip the entire tutorial |
complete() |
Complete the tutorial |
HighlightShape:rectangle,roundedRectangle,circleTooltipPosition:auto,top,bottom,left,rightTutorialStatus:idle,active,completed,skipped
lib/
tutorial_overlay_flutter.dart # Barrel exports
src/
enums.dart # HighlightShape, TooltipPosition, TutorialStatus
step_info.dart # Step metadata passed to tooltip builders
tutorial_step.dart # Step configuration model
tutorial_config.dart # Global configuration
tutorial_controller.dart # State management (ChangeNotifier)
tutorial_overlay.dart # Overlay lifecycle + animated widget
overlay_painter.dart # CustomPainter for dimmed overlay with cutout
tooltip_positioner.dart # Smart tooltip positioning
default_tooltip.dart # Built-in tooltip widget
tutorial_scope.dart # InheritedWidget for tree-level access
tutorial_persistence.dart # Persistence interface + in-memory impl
- Arrow/pointer connecting tooltip to target
- Tooltip entry/exit animations (slide, bounce)
- Keyboard navigation and accessibility labels
- Haptic feedback option
- Step groups and branching flows
- Built-in SharedPreferences persistence adapter
- Analytics hooks (step viewed, skipped, completed)
- Backdrop blur option
- Multiple simultaneous highlights
- Widget-level API (
TutorialTargetwrapper widget as alternative to GlobalKey)
MIT License - Copyright (c) 2026 Brewnbeer
See LICENSE for details.