Showcase Coach
Modern, design-forward showcase coach overlays for Flutter with smooth motion, glassmorphism, and sensible validation so you can guide users through product tours with confidence.
Preview

Try it live: Interactive Demo
Why use Showcase Coach?
- Design-first: Glassmorphism, elevated cards, and balanced typography that fit Material 3.
- Safe by default: Duplicate key detection, visibility checks, and user-friendly error dialogs.
- Flexible logic: Per-step and global conditions (
shouldShow/showIf) plus smart scrolling. - Motion-aware: Reduced-motion mode to turn off blur and heavy effects, plus customizable transition animations.
- Accessible: Built-in accessibility support with semantic labels and proper tap targets.
- Well-documented: Comprehensive API documentation with examples and best practices.
- Drop-in: Simple API that works with any widget that has a
GlobalKey.
Installation
Add to pubspec.yaml:
dependencies:
save_points_showcaseview: ^1.0.3
Then install:
flutter pub get
Quick start (3 steps)
- Create keys:
final _buttonKey = GlobalKey();
final _cardKey = GlobalKey();
- Attach keys:
FilledButton(key: _buttonKey, onPressed: () {}, child: const Text('Click me'));
Card(key: _cardKey, child: const Text('Important card'));
- Show the coach:
await ShowcaseCoach.show(
context,
steps: [
CoachStep(
targetKey: _buttonKey,
title: 'Welcome!',
description: ['This is your first step.'],
),
CoachStep(
targetKey: _cardKey,
title: 'Feature Card',
description: [
'This card contains important information.',
'Swipe to see more tips.',
],
),
],
);
Full example
import 'package:flutter/material.dart';
import 'package:save_points_showcaseview/save_points_showcaseview.dart';
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
final _buttonKey = GlobalKey();
final _cardKey = GlobalKey();
Future<void> _startTour() async {
await ShowcaseCoach.show(
context,
steps: [
CoachStep(
targetKey: _buttonKey,
title: 'Action Button',
description: ['Tap this button to perform an action.'],
),
CoachStep(
targetKey: _cardKey,
title: 'Information Card',
description: ['This card displays important information.'],
),
],
onSkip: () => debugPrint('Tour skipped'),
onDone: () => debugPrint('Tour completed'),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
FilledButton(
key: _buttonKey,
onPressed: _startTour,
child: const Text('Start Tour'),
),
Card(
key: _cardKey,
child: const Padding(
padding: EdgeInsets.all(12),
child: Text('Card content'),
),
),
],
),
);
}
}
Configuration highlights
ShowcaseCoachConfiglets you tune:primaryColor,buttonColor,fontFamilycardStyle:glass(default) ornormaloverlayTintOpacityreduceMotion: disables blur/heavy effects- Transition animations (new):
enableTransitions: enable/disable transition animations (defaults based onreduceMotion)transitionDuration: global duration for all transitionstransitionCurve: global curve for all transitions- Individual transition durations:
backdropTransitionDuration: backdrop hole transitions (default: 450ms)gradientTransitionDuration: gradient overlay transitions (default: 500ms)highlightTransitionDuration: highlight position transitions (default: 700ms)cardTransitionDuration: tooltip card transitions (default: 600ms)
- Per-step logic:
shouldShow: function returning bool (priority)showIf: simple bool (defaults to true)
Customizing transition animations
ShowcaseCoachConfig(
// Enable/disable transitions (defaults to true unless reduceMotion is true)
enableTransitions: true,
// Set global duration for all transitions
transitionDuration: Duration(milliseconds: 300),
// Set global curve for all transitions
transitionCurve: Curves.easeInOut,
// Or customize individual transitions
backdropTransitionDuration: Duration(milliseconds: 400),
cardTransitionDuration: Duration(milliseconds: 500),
// Disable transitions entirely
// enableTransitions: false,
// Or let reduceMotion handle it automatically
// reduceMotion: true, // This will disable transitions
)
Validation and safety
- Duplicate GlobalKey detection before showing.
- Visibility checks ensure targets are attached and scroll into view.
- Friendly dialogs instead of silent failures or crashes.
- Comprehensive error messages with actionable guidance.
Accessibility
- Semantic labels for screen readers.
- Minimum tap target sizes (48x48) for better touch accessibility.
- Proper button semantics for assistive technologies.
- Full keyboard navigation support.
Tips & best practices
- Wait for layout before showing:
WidgetsBinding.instance.addPostFrameCallback((_) {
ShowcaseCoach.show(context, steps: steps);
});
- Unique keys: every step needs its own
GlobalKey. - Concise copy: short titles and descriptions improve completion.
- Respect motion: use
reduceMotion: truewhere needed, or customize transition animations withenableTransitionsand duration/curve options. - Accessibility: The library includes built-in accessibility support, but ensure your target widgets are also accessible.
Troubleshooting
- Nothing shows: confirm
Overlay.of(context)is available (e.g., use inside aMaterialApp), and run after the first frame. - Step skipped: check
shouldShow/showIffor that step. - Target not found: ensure the widget has a unique
GlobalKeyand is mounted.
Contributing
Issues and PRs are welcome! Open one at: https://github.com/m7hamed-dev/save-points-showcaseview/issues
License
MIT License. See LICENSE for details.
Libraries
- main
- Main library entry point for the showcase coach package.
- models/phase
- save_points_showcaseview
- A beautiful, modern showcase coach overlay for Flutter.
- showcase_coach
- Deprecated entry point. Use
package:save_points_showcaseview/save_points_showcaseview.dart. - widgets/coach_step
- widgets/explainable_action