A flexible, production-ready Flutter package for creating beautiful promotional carousels with support for user-specific widget injection. Perfect for onboarding flows, feature announcements, and personalized promotions.
β¨ Features
Core Features
- π― Modal Overlay - Beautiful dialog-style presentation with dimmed background
- π± Swipeable Carousel - Smooth PageView-based navigation
- π¨ Built-in Visual Types - Images, animations, search bars, videos, and more
- π§ Custom Widget Injection - Inject user-specific widgets without coupling
- πΎ Persistent State - Tracks "show once" slides using SharedPreferences
- π Theme Support - Automatically adapts to light and dark themes
New in v1.2.0
- π Hero Animations - Smooth transitions between the carousel and host app
- π Audio Feedback - Global and per-slide sound effects
- π§ 3D Transitions - Cube and Flip 3D page transitions
- πΊοΈ Geolocation Rules - Filter slides by country and region
- π Internationalization - Built-in support for 14 languages (EN, MY, ES, FR, DE, JA, ZH, KO, HI, AR, PT, RU, TH, VI)
- πΌοΈ Enhanced Images - Transparently handle local assets OR network images with caching
π± Platform Support
| Feature | Android | iOS | Web | macOS | Windows | Linux |
|---|---|---|---|---|---|---|
| Core Carousel | β | β | β | β | β | β |
| Images (Local/Network) | β | β | β | β | β | β |
| Animations | β | β | β | β | β | β |
| Video Support | β | β | β | β | β | β |
| Audio Feedback | β | β | β | β | β | β |
πΈ Preview
π Getting Started
Installation
Add to your pubspec.yaml:
dependencies:
promo_carousel: ^1.2.0
Then run:
flutter pub get
Basic Usage
import 'package:promo_carousel/promo_carousel.dart';
PromoCarousel.show(
context: context,
slides: [
PromoSlide(
id: 'welcome',
title: 'Welcome to Our App',
subtitle: 'Discover amazing features',
visualType: PromoVisualType.featureHighlight,
cta: PromoCTA(
text: 'Get Started',
action: PromoAction.close,
),
rules: PromoRules(showOnce: true),
),
],
onAction: (action, target) {
print('Action: $action');
},
);
π Documentation
PromoSlide
The core model representing a single carousel slide:
PromoSlide(
id: 'unique_id', // Unique identifier
title: 'Main Title', // Required title text
subtitle: 'Optional description', // Optional subtitle
visualType: PromoVisualType.image, // Built-in visual type
cta: PromoCTA( // Call-to-action button
text: 'Button Text',
action: PromoAction.navigate,
target: '/route-name',
),
rules: PromoRules( // Display rules
showOnce: true,
minAppVersion: '1.2.0',
maxAppVersion: '2.0.0',
showAfterDate: DateTime(2025, 1, 1),
showBeforeDate: DateTime(2025, 12, 31),
userSegments: ['premium', 'beta'],
deviceTypes: [DeviceType.mobile],
countries: ['US', 'CA'], // Geolocation: Countries
regions: ['California', 'NY'], // Geolocation: Regions
),
audioAsset: 'assets/sounds/promo.mp3', // Slide-specific sound
heroTag: 'promo_hero_image', // Hero animation tag
customContentBuilder: (context) { // Optional custom widget
return YourCustomWidget();
},
semanticLabel: 'Welcome screen', // Accessibility
metadata: {'experiment_id': '001'}, // A/B testing
)
Visual Types
Built-in visual types available:
PromoVisualType.image- Display an image assetPromoVisualType.animation- Show an animated visualPromoVisualType.searchBar- Search bar animationPromoVisualType.featureHighlight- Gradient highlightPromoVisualType.video- Video player (requires video asset)Important
Video support (
PromoVisualType.video) is currently not available on Windows and Linux platforms due tovideo_playerpackage limitations.PromoVisualType.custom- Use withcustomContentBuilder
Actions
Available CTA actions:
PromoAction.navigate- Navigate to a routePromoAction.openFeature- Open a specific featurePromoAction.openPaywall- Show paywall/upgrade screenPromoAction.close- Simply close the modalPromoAction.custom- Custom action with callback
Transition Types
TransitionType.slide- Horizontal slideTransitionType.fade- Cross-fade effectTransitionType.scale- Scaling animationTransitionType.rotate- Rotation effectTransitionType.cube- (v1.2) 3D Cube transitionTransitionType.flip3D- (v1.2) 3D Flip transition
Configuration
Customize appearance and behavior:
PromoCarousel.show(
context: context,
slides: slides,
config: PromoCarouselConfig(
// Appearance
borderRadius: 24.0,
elevation: 8.0,
barrierColor: Color(0x80000000),
backdropBlur: 10.0,
displayMode: DisplayMode.dialog,
// Behavior
barrierDismissible: true,
showCloseButton: true,
showDontShowAgain: false,
showSkipButton: true,
enableSwipeGestures: true,
enableTapToAdvance: true,
// Auto-advance
autoAdvance: true,
autoAdvanceDuration: Duration(seconds: 5),
pauseOnInteraction: true,
// Audio Feedback (v1.2)
slideChangeSound: 'assets/sounds/page_turn.mp3',
ctaClickSound: 'assets/sounds/click.mp3',
// Hero Animations (v1.2)
enableHeroAnimations: true,
// Progress
showProgressBar: true,
progressBarPosition: ProgressPosition.top,
// Accessibility
enableHaptics: true,
hapticFeedbackType: HapticType.light,
respectReducedMotion: true,
),
);
Pre-built Configs
Use factory constructors for common scenarios:
// Onboarding flow
config: PromoCarouselConfig.onboarding()
// Feature announcement
config: PromoCarouselConfig.announcement()
// Marketing promotion
config: PromoCarouselConfig.marketing()
π― Advanced Usage
Custom User-Specific Widgets
The key feature - inject personalized content without coupling the package to your data models:
PromoSlide(
id: 'zodiac_preview',
title: 'Your Personal Insights',
subtitle: 'Based on your zodiac sign',
visualType: PromoVisualType.custom,
cta: PromoCTA(
text: 'Reveal',
action: PromoAction.openFeature,
target: 'zodiac_detail',
),
rules: PromoRules(showOnce: true),
customContentBuilder: (context) {
// Access your app's user data here
final user = Provider.of<User>(context);
return ZodiacPreviewCard(
sign: user.zodiacSign,
topSearch: user.topSearch,
);
},
)
Analytics Tracking
Track user interactions with comprehensive callbacks:
PromoCarousel.show(
context: context,
slides: slides,
analytics: PromoCarouselAnalytics(
onSlideViewed: (slideId, index) {
print('Viewed: $slideId at index $index');
analytics.logEvent('promo_slide_viewed', {'slide_id': slideId});
},
onSlideSkipped: (slideId, index) {
analytics.logEvent('promo_slide_skipped');
},
onCTAClicked: (slideId, action, target) {
analytics.logEvent('promo_cta_clicked', {
'slide_id': slideId,
'action': action.name,
'target': target,
});
},
onCarouselCompleted: (viewedSlides) {
analytics.logEvent('promo_completed', {
'slides_viewed': viewedSlides.length,
});
},
onCarouselDismissed: (lastIndex, viewedSlides) {
analytics.logEvent('promo_dismissed', {
'last_index': lastIndex,
'completion_rate': viewedSlides.length / slides.length,
});
},
onSkipAll: () {
analytics.logEvent('promo_skipped_all');
},
),
);
Auto-Advance with Pause
Automatically progress through slides:
PromoCarousel.show(
context: context,
slides: slides,
config: PromoCarouselConfig(
autoAdvance: true,
autoAdvanceDuration: Duration(seconds: 5),
pauseOnInteraction: true, // Pause when user interacts
showProgressBar: true,
),
);
Display Modes
Choose how to present the carousel:
// Dialog (default)
PromoCarousel.show(context: context, slides: slides);
// Bottom sheet
PromoCarousel.showBottomSheet(context: context, slides: slides);
// Fullscreen
PromoCarousel.showFullscreen(context: context, slides: slides);
Remote Config Integration
Load slides from JSON:
// From your Firebase Remote Config, API, etc.
final jsonData = await remoteConfig.getJson('promo_slides');
// Parse to slides
final slides = PromoCarousel.fromJson(jsonData);
// Show carousel
PromoCarousel.show(context: context, slides: slides);
Conditional Display Rules
Advanced filtering based on multiple criteria:
PromoSlide(
id: 'premium_offer',
title: 'Upgrade to Premium',
rules: PromoRules(
showOnce: true,
minAppVersion: '1.5.0',
maxAppVersion: '2.0.0',
showAfterDate: DateTime(2025, 1, 1),
showBeforeDate: DateTime(2025, 12, 31),
userSegments: ['free_tier', 'trial'],
deviceTypes: [DeviceType.mobile, DeviceType.tablet],
customCondition: () {
// Custom logic
return user.hasUsedAppForDays(7);
},
),
// ...
)
Custom Page Indicators
Replace the default page counter:
PromoCarousel.show(
context: context,
slides: slides,
config: PromoCarouselConfig(
indicatorBuilder: (context, currentPage, totalPages) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(totalPages, (index) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 4),
width: index == currentPage ? 12 : 8,
height: 8,
decoration: BoxDecoration(
color: index == currentPage ? Colors.blue : Colors.grey,
borderRadius: BorderRadius.circular(4),
),
);
}),
);
},
),
);
Preview Mode
Test all slides regardless of rules:
PromoCarousel.showPreview(
context: context,
slides: slides,
// Ignores showOnce, date ranges, version checks, etc.
);
Manual State Management
Control slide visibility programmatically:
// Check if slide was seen
final seen = await PromoCarousel.hasSeenSlide('welcome');
// Reset a specific slide
await PromoCarousel.resetSlide('welcome');
// Reset multiple slides
await PromoCarousel.resetSlides(['slide1', 'slide2']);
// Reset all slides
await PromoCarousel.resetAll();
// Get all seen slides
final seenSlides = await PromoCarousel.getSeenSlides();
// Export analytics
final analytics = await PromoCarousel.exportAnalytics();
Debug Mode
Enable detailed logging during development:
void main() {
PromoCarousel.debugMode = true;
runApp(MyApp());
}
Internationalization (v1.2)
The package includes built-in localization. To enable it, add the delegates to your MaterialApp:
MaterialApp(
localizationsDelegates: [
PromoLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
// ...
],
supportedLocales: [
Locale('en'),
Locale('my'),
],
)
π¨ Theming
The carousel automatically adapts to your app's theme:
MaterialApp(
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
// Carousel will adapt to both themes
)
π± Use Cases
Perfect for:
- β Onboarding flows - Welcome new users
- β Feature announcements - Showcase new capabilities
- β Personalized promotions - Zodiac insights, user stats, achievements
- β Contextual tips - Show relevant guidance
- β Marketing campaigns - Promote offers and upgrades
- β Product tours - Guide users through features
- β A/B testing - Test different messaging with metadata tracking
- β Seasonal campaigns - Date-based promotions
ποΈ Architecture
The package follows clean architecture principles:
lib/
βββ models/
β βββ promo_slide.dart # Core data models
β βββ promo_carousel_config.dart # Configuration classes
βββ controllers/
β βββ promo_carousel_controller.dart # State management
βββ widgets/
β βββ promo_carousel_modal.dart # Main modal widget
β βββ promo_slide_content.dart # Content renderer
βββ promo_carousel.dart # Public API
π« What This Package Does NOT Do
To keep the package generic and reusable:
- β No Firebase or remote config integration (but provides JSON parsing)
- β No analytics tracking implementation (but provides callbacks)
- β No app-specific navigation logic
- β No user data models or assumptions
- β No network requests
These features should be implemented in your app layer using the provided callbacks and hooks.
π€ Contributing
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.
π License
This project is licensed under the MIT License - see the LICENSE file for details.
π Changelog
See CHANGELOG.md for release history.
π Acknowledgments
Built with β€οΈ for the Flutter community.
π Support
- π§ Email: kyawzayartun.contact@gmail.com
- π Issues: GitHub Issues
Made with Flutter π
Libraries
- promo_carousel
- Promo Carousel