coachmark 0.1.1
coachmark: ^0.1.1 copied to clipboard
A Flutter package for creating beautiful, customizable coach marks and onboarding overlays with dynamic target highlighting.
ðŊ Coachmark #
Beautiful, customizable coach marks and onboarding overlays for Flutter
Create stunning product tours with dynamic target highlighting
Installation âĒ Quick Start âĒ Features âĒ Examples âĒ API Reference
ðļ Screenshots #
âĻ Features #
- ðŊ Dynamic Highlighting - Wrap any widget to highlight it with a coach mark overlay
- ðĻ Fully Customizable - Control colors, sizes, borders, shadows, and positioning
- ðą Smart Positioning - Automatically adjusts bubble position based on available screen space
- ð§ Position Control - Auto-detect the best position or manually specify preferred placement
- ð Dimmed Overlay - Creates focus by dimming the background with a cut-out hole
- ð Highlight Padding - Add extra space around highlighted targets
- ð Safe Area Support - Optional control over safe area behavior
- ð§Đ Sequential Tours - Easily create multi-step onboarding experiences
- ðŠķ Lightweight - Zero external dependencies beyond Flutter SDK
- ⥠Simple API - Widget-based, easy to integrate
ðĶ Installation #
Add this to your package's pubspec.yaml file:
dependencies:
coachmark: ^0.1.0
Then run:
flutter pub get
Latest Version: Check pub.dev for the most recent version.
ð Quick Start #
1. Import the Package #
import 'package:coachmark/coachmark.dart';
2. Wrap Your Widget #
Coachmark(
isVisible: true,
config: CoachmarkConfig(
description: 'Tap this button to perform an action!',
),
onDismiss: () {
// Handle dismiss
},
child: ElevatedButton(
onPressed: () {},
child: Text('My Button'),
),
)
That's it! Your first coachmark is ready. ð
ð Examples #
Basic Usage #
Show a simple coachmark on any widget:
import 'package:flutter/material.dart';
import 'package:coachmark/coachmark.dart';
class BasicExample extends StatefulWidget {
@override
State<BasicExample> createState() => _BasicExampleState();
}
class _BasicExampleState extends State<BasicExample> {
bool _showCoachmark = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Coachmark Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Coachmark(
isVisible: _showCoachmark,
config: const CoachmarkConfig(
description: 'Tap this button to perform an action!',
highlightBorderColor: Colors.blue,
),
onDismiss: () => setState(() => _showCoachmark = false),
child: ElevatedButton(
onPressed: () {
// Your action
},
child: const Text('Action Button'),
),
),
const SizedBox(height: 20),
TextButton(
onPressed: () => setState(() => _showCoachmark = true),
child: const Text('Show Coachmark'),
),
],
),
),
);
}
}
Custom Styling #
Create a beautifully styled coachmark:
Coachmark(
isVisible: true,
config: CoachmarkConfig(
description: 'This feature helps you accomplish your goals faster!',
descriptionStyle: const TextStyle(
fontSize: 16,
color: Colors.white,
fontWeight: FontWeight.w500,
),
bubbleBackgroundColor: const Color(0xFF2C3E50),
bubbleBorderColor: Colors.blueAccent,
highlightBorderColor: Colors.greenAccent,
highlightBorderWidth: 3.0,
highlightCornerRadius: 16.0,
highlightPadding: const EdgeInsets.all(8.0), // Add space around target
bubbleCornerRadius: 12.0,
bubblePadding: const EdgeInsets.all(20.0),
bubbleMaxWidth: 320.0,
overlayColor: const Color(0xAA000000),
spacing: 16.0,
preferredPosition: CoachmarkBubblePosition.bottom,
bubbleShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
blurRadius: 20,
offset: const Offset(0, 10),
),
],
),
onDismiss: () {
// Handle dismiss
},
child: YourWidget(),
)
Sequential Tour #
Create a multi-step onboarding tour:
class OnboardingTour extends StatefulWidget {
const OnboardingTour({super.key});
@override
State<OnboardingTour> createState() => _OnboardingTourState();
}
class _OnboardingTourState extends State<OnboardingTour> {
bool _showStep1 = false;
bool _showStep2 = false;
bool _showStep3 = false;
void _startTour() {
setState(() => _showStep1 = true);
}
void _nextStep() {
setState(() {
if (_showStep1) {
_showStep1 = false;
_showStep2 = true;
} else if (_showStep2) {
_showStep2 = false;
_showStep3 = true;
} else {
_showStep3 = false;
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Product Tour'),
actions: [
IconButton(
icon: const Icon(Icons.help_outline),
onPressed: _startTour,
),
],
),
body: Column(
children: [
// Step 1: Highlight a menu item
Coachmark(
isVisible: _showStep1,
config: const CoachmarkConfig(
description: 'ð Welcome! Tap the help icon to restart this tour.',
bubbleBackgroundColor: Colors.blue,
descriptionStyle: TextStyle(color: Colors.white),
),
onDismiss: _nextStep,
child: ListTile(
leading: const Icon(Icons.home),
title: const Text('Home'),
onTap: () {},
),
),
// Step 2: Highlight settings
Coachmark(
isVisible: _showStep2,
config: const CoachmarkConfig(
description: 'âïļ Access your settings here.',
highlightBorderColor: Colors.orange,
),
onDismiss: _nextStep,
child: ListTile(
leading: const Icon(Icons.settings),
title: const Text('Settings'),
onTap: () {},
),
),
// Step 3: Highlight profile
Coachmark(
isVisible: _showStep3,
config: const CoachmarkConfig(
description: 'ðĪ View and edit your profile information.',
bubbleBackgroundColor: Colors.green,
descriptionStyle: TextStyle(color: Colors.white),
),
onDismiss: _nextStep,
child: ListTile(
leading: const Icon(Icons.person),
title: const Text('Profile'),
onTap: () {},
),
),
],
),
);
}
}
Highlight Padding Example #
Add space around small tap targets:
Coachmark(
isVisible: true,
config: const CoachmarkConfig(
description: 'Tap here to add a new item',
highlightPadding: EdgeInsets.all(12.0), // Expands the highlight area
highlightBorderColor: Colors.purple,
),
onDismiss: () {},
child: IconButton(
icon: const Icon(Icons.add),
onPressed: () {},
),
)
Safe Area Control #
Control whether the bubble respects safe areas:
Coachmark(
isVisible: true,
config: const CoachmarkConfig(
description: 'This bubble can draw behind the status bar',
drawOverSafeArea: true, // Allow bubble to extend into safe areas
),
onDismiss: () {},
child: YourWidget(),
)
ð API Reference #
Coachmark Widget #
Coachmark({
required Widget child,
required CoachmarkConfig config,
bool isVisible = false,
VoidCallback? onDismiss,
GlobalKey? childKey,
})
| Property | Type | Required | Description |
|---|---|---|---|
child |
Widget |
â Yes | The widget to be highlighted |
config |
CoachmarkConfig |
â Yes | Configuration for appearance and behavior |
isVisible |
bool |
No | Whether to show the coachmark (default: false) |
onDismiss |
VoidCallback? |
No | Called when the overlay is dismissed |
childKey |
GlobalKey? |
No | Optional key for the child widget |
CoachmarkConfig #
| Property | Type | Default | Description |
|---|---|---|---|
description |
String |
Required | Text shown in the bubble |
descriptionStyle |
TextStyle? |
null |
Style for the description text |
bubbleBackgroundColor |
Color |
Colors.white |
Background color of the bubble |
bubbleBorderColor |
Color? |
null |
Border color of the bubble |
highlightBorderColor |
Color |
Colors.green |
Border color around the target |
highlightBorderWidth |
double |
2.0 |
Border width around the target |
highlightCornerRadius |
double |
12.0 |
Corner radius of the highlight |
highlightPadding |
EdgeInsets |
EdgeInsets.zero |
Padding around the highlighted target |
bubbleCornerRadius |
double |
12.0 |
Corner radius of the bubble |
bubblePadding |
EdgeInsets |
EdgeInsets.all(16.0) |
Padding inside the bubble |
bubbleMaxWidth |
double |
300.0 |
Maximum width of the bubble |
overlayColor |
Color |
Color(0x8C000000) |
Dim overlay background color |
bubbleShadow |
List<BoxShadow>? |
null |
Shadow for the bubble |
spacing |
double |
12.0 |
Space between target and bubble |
preferredPosition |
CoachmarkBubblePosition |
auto |
Preferred bubble position |
drawOverSafeArea |
bool |
false |
Whether bubble can draw over safe areas |
CoachmarkBubblePosition #
enum CoachmarkBubblePosition {
auto, // Automatically choose the best position
left, // Place bubble to the left of the target
right, // Place bubble to the right of the target
top, // Place bubble above the target
bottom, // Place bubble below the target
}
ðĄ Best Practices #
â Do's #
- â Use sequential coachmarks to guide users through complex flows
- â Keep descriptions short and actionable (under 50 words)
- â Test your coachmarks on different screen sizes
- â
Use
highlightPaddingto add breathing room around small targets - â Dismiss coachmarks on user interaction for better UX
- â
Use
GlobalKeywhen highlighting widgets in complex layouts
â Don'ts #
- â Don't show too many coachmarks at once (3-5 max per tour)
- â Don't block critical UI interactions with coachmarks
- â Don't use tiny font sizes that are hard to read
- â Don't forget to handle coachmark state properly
- â Don't show the same coachmark repeatedly to returning users
ðĪ 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 some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
ð License #
This project is licensed under the MIT License - see the LICENSE file for details.
ð Links #
- Repository: github.com/youseflabs-k/coachmark
- Issues: github.com/youseflabs-k/coachmark/issues
- pub.dev: pub.dev/packages/coachmark
- Example App: example/
Made with âĪïļ by Ahmed Yousef
If you find this package helpful, please consider giving it a âïļ!