Flutter Welcome Kit 🎉
A beautiful, customizable onboarding/tour guide kit for Flutter apps. Highlight widgets, display tooltips, and guide your users step by step — perfect for tutorials and product tours.
📸 Demo

✨ Features
| Feature | Description |
|---|---|
| 🎯 Spotlight Overlay | Highlight any widget with a dark overlay cutout |
| ✨ 8 Animation Types | fade, slideUp/Down/Left/Right, scale, bounce, rotate |
| 🔵 4 Highlight Shapes | Circle, rectangle, pill, rounded rectangle |
| 💫 Pulse Effect | Animated pulse ring around highlighted widget |
| 📊 Progress Indicators | Dots, text ("Step 2 of 5"), or compact ("2/5") |
| ⏮️ Full Navigation | Previous, Next, and Skip buttons |
| 🎨 Customizable | Colors, icons, styles, custom content per step |
| 📍 Smart Positioning | Tooltip auto-positions to avoid edges |
| ⌨️ Keyboard Support | Arrow keys, ESC, Enter/Space navigation |
| 🔔 Callbacks | onComplete, onSkip, onStepChange events |
| ⏱️ Auto-advance | Configurable timing per step |
| 🌐 RTL Support | Works with right-to-left languages |
📦 Installation
Add to your pubspec.yaml:
dependencies:
flutter_welcome_kit: ^2.0.0
Then run:
flutter pub get
🚀 Quick Start
import 'package:flutter_welcome_kit/flutter_welcome_kit.dart';
class MyApp extends StatefulWidget {
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// 1. Create GlobalKeys for widgets to highlight
final addButtonKey = GlobalKey();
final searchKey = GlobalKey();
final profileKey = GlobalKey();
late TourController _tourController;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
// 2. Create the tour controller
_tourController = TourController(
context: context,
steps: [
TourStep(
key: addButtonKey,
title: '➕ Create New',
description: 'Tap here to add a new item.',
backgroundColor: Colors.blue,
animation: StepAnimation.fadeSlideUp,
showPulse: true,
),
TourStep(
key: searchKey,
title: '🔍 Search',
description: 'Find anything instantly.',
backgroundColor: Colors.orange,
highlightShape: HighlightShape.circle,
),
TourStep(
key: profileKey,
title: '👤 Your Profile',
description: 'Manage your account settings.',
backgroundColor: Colors.purple,
isLast: true,
buttonLabel: 'Get Started!',
),
],
onComplete: () => print('Tour finished!'),
onSkip: () => print('Tour skipped'),
);
// 3. Start the tour
_tourController.start();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
IconButton(
key: searchKey, // Attach the key
icon: Icon(Icons.search),
onPressed: () {},
),
IconButton(
key: profileKey, // Attach the key
icon: Icon(Icons.person),
onPressed: () {},
),
],
),
floatingActionButton: FloatingActionButton(
key: addButtonKey, // Attach the key
onPressed: () {},
child: Icon(Icons.add),
),
);
}
}
📚 API Reference
TourStep
Configuration for a single tour step.
| Parameter | Type | Default | Description |
|---|---|---|---|
key |
GlobalKey |
required | Widget to highlight |
title |
String |
required | Tooltip title |
description |
String |
required | Tooltip description |
backgroundColor |
Color |
Colors.white |
Tooltip background |
duration |
Duration |
4 seconds |
Auto-advance delay |
buttonLabel |
String? |
"Next"/"Done" |
Button text |
isLast |
bool |
false |
Is this the final step? |
animation |
StepAnimation |
fadeSlideUp |
Entry animation |
highlightShape |
HighlightShape |
rounded |
Spotlight shape |
showPulse |
bool |
false |
Show pulse animation |
customContent |
Widget? |
null |
Replace description with widget |
showProgress |
bool |
true |
Show progress indicator |
progressStyle |
ProgressIndicatorStyle |
dots |
Progress style |
showPreviousButton |
bool |
true |
Show back button |
showSkipButton |
bool |
true |
Show close/skip button |
spotlightPadding |
double |
8.0 |
Padding around highlight |
spotlightBorderRadius |
double |
12.0 |
Border radius (rounded shape) |
allowTargetTap |
bool |
false |
Tap widget to advance |
preferredPosition |
TooltipPosition |
auto |
Force tooltip position |
titleStyle |
TextStyle? |
null |
Custom title style |
descriptionStyle |
TextStyle? |
null |
Custom description style |
icon |
IconData? |
null |
Icon before title |
iconColor |
Color? |
null |
Icon color |
onDisplay |
VoidCallback? |
null |
Callback fired when step is shown |
TourController
Manages the tour lifecycle.
TourController(
context: context,
steps: steps,
// Callbacks
onComplete: () {}, // Tour finished
onSkip: () {}, // Tour skipped
onStepChange: (index, step) {}, // Step changed
// Configuration
startDelay: Duration(milliseconds: 500),
overlayColor: Colors.black.withValues(alpha: 0.7),
dismissOnBarrierTap: false,
);
Methods
| Method | Description |
|---|---|
start() |
Start tour from step 0 |
startFrom(int index) |
Start from specific step |
next() |
Go to next step |
previous() |
Go to previous step |
goToStep(int index) |
Jump to specific step |
skip() |
Skip/end the tour |
end() |
Alias for skip |
Properties
| Property | Type | Description |
|---|---|---|
currentStepIndex |
int |
Current step (0-based) |
totalSteps |
int |
Total number of steps |
isRunning |
bool |
Is tour currently active |
currentStep |
TourStep? |
Current step object |
Enums
StepAnimation
StepAnimation.fadeSlideUp // Fade + slide from bottom
StepAnimation.fadeSlideDown // Fade + slide from top
StepAnimation.fadeSlideLeft // Fade + slide from right
StepAnimation.fadeSlideRight // Fade + slide from left
StepAnimation.scale // Scale up from center
StepAnimation.bounce // Elastic bounce
StepAnimation.rotate // Rotate while fading
StepAnimation.fade // Simple fade in
StepAnimation.none // No animation
HighlightShape
HighlightShape.rounded // Rounded rectangle (default)
HighlightShape.circle // Perfect circle
HighlightShape.pill // Capsule/pill shape
HighlightShape.rectangle // Sharp corners
TooltipPosition
TooltipPosition.auto // Automatic (default)
TooltipPosition.top // Force above target
TooltipPosition.bottom // Force below target
TooltipPosition.left // Force left of target
TooltipPosition.right // Force right of target
ProgressIndicatorStyle
ProgressIndicatorStyle.dots // ●● ○ ○ (animated dots)
ProgressIndicatorStyle.text // "Step 2 of 5"
ProgressIndicatorStyle.textCompact // "2/5"
ProgressIndicatorStyle.none // No indicator
🎨 Advanced Usage
Custom Content in Tooltip
TourStep(
key: profileKey,
title: 'Complete Your Profile',
description: '',
customContent: Column(
children: [
CircleAvatar(radius: 30, child: Icon(Icons.person)),
SizedBox(height: 12),
Text('Add a photo to personalize your account'),
SizedBox(height: 8),
OutlinedButton(
onPressed: () {},
child: Text('Upload Photo'),
),
],
),
)
Different Shapes Demo
// Circle - great for FABs and icons
TourStep(
key: fabKey,
highlightShape: HighlightShape.circle,
spotlightPadding: 4,
...
)
// Pill - great for buttons and chips
TourStep(
key: buttonKey,
highlightShape: HighlightShape.pill,
...
)
// Rounded - great for cards
TourStep(
key: cardKey,
highlightShape: HighlightShape.rounded,
spotlightBorderRadius: 16,
...
)
Callbacks for Analytics
TourController(
context: context,
steps: steps,
onStepChange: (index, step) {
analytics.logEvent('tour_step', {
'step_index': index,
'step_title': step.title,
});
},
onComplete: () {
analytics.logEvent('tour_completed');
prefs.setBool('has_seen_tour', true);
},
onSkip: () {
analytics.logEvent('tour_skipped');
},
);
Feature Discovery with onDisplay
Use the onDisplay callback to track when users see specific features — perfect for progressive disclosure and feature discovery UX patterns:
// Track which features users have discovered
final Set<String> discoveredFeatures = {};
void markFeatureSeen(String featureId) {
discoveredFeatures.add(featureId);
prefs.setStringList('discovered_features', discoveredFeatures.toList());
}
// In your tour steps:
TourStep(
key: inboxKey,
title: 'Inbox',
description: 'You can access incoming messages here.',
onDisplay: () => markFeatureSeen('inbox'),
),
TourStep(
key: settingsKey,
title: 'Settings',
description: 'Customize your app preferences.',
onDisplay: () => markFeatureSeen('settings'),
),
⌨️ Keyboard Navigation
| Key | Action |
|---|---|
Enter / Space |
Next step |
← / ↑ |
Previous step |
ESC |
Skip tour |
🔧 Troubleshooting
Widget not highlighting?
Make sure the GlobalKey is attached to a widget that's currently visible:
// ✅ Correct - key is on a visible widget
IconButton(
key: myKey,
icon: Icon(Icons.star),
...
)
// ❌ Wrong - key is on a widget inside a closed menu
PopupMenuItem(
key: myKey, // Won't work if menu is closed!
...
)
Tour starts before UI is ready?
Use startDelay to wait for widgets to render:
TourController(
...
startDelay: Duration(milliseconds: 500),
);
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📄 License
MIT License - see LICENSE for details.
👨💻 Author
Mohammad Usman
- GitHub: @Usman-bhat
- Package: pub.dev/packages/flutter_welcome_kit
Libraries
- core/enums
- core/overlay_painter
- core/tour_controller
- core/tour_step
- flutter_welcome_kit
- Flutter Welcome Kit - A beautiful, customizable onboarding/tour guide kit
- widgets/progress_indicator
- widgets/spotlight
- widgets/tooltip_card
- widgets/tooltip_overlay_wrapper