cmx_onboarding 0.0.1
cmx_onboarding: ^0.0.1 copied to clipboard
A highly customizable Flutter onboarding package with 16 built-in UI templates, themeable indicators and buttons, per-page styling, and full controller support.
import 'package:cmx_onboarding/cmx_onboarding.dart';
import 'package:flutter/material.dart';
void main() => runApp(const DemoApp());
class DemoApp extends StatelessWidget {
const DemoApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'cmx_onboarding demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
colorSchemeSeed: const Color(0xFF6750A4),
),
home: const TemplateGallery(),
);
}
}
class TemplateGallery extends StatelessWidget {
const TemplateGallery({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('cmx_onboarding templates')),
body: ListView.separated(
padding: const EdgeInsets.all(16),
itemCount: OnboardTemplate.values.length,
separatorBuilder: (_, __) => const SizedBox(height: 12),
itemBuilder: (context, i) {
final template = OnboardTemplate.values[i];
return Card(
child: ListTile(
leading: CircleAvatar(
backgroundColor: _accentFor(template),
child: Text('${i + 1}',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
)),
),
title: Text(_humanize(template.name)),
subtitle: Text(_descriptionFor(template)),
trailing: const Icon(Icons.chevron_right),
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => DemoScreen(template: template),
),
);
},
),
);
},
),
);
}
Color _accentFor(OnboardTemplate t) {
switch (t) {
case OnboardTemplate.vibrant:
return const Color(0xFFFFC107);
case OnboardTemplate.pastel:
return const Color(0xFFFFB7C5);
case OnboardTemplate.darkPro:
return const Color(0xFF111827);
case OnboardTemplate.editorial:
return const Color(0xFF7C3AED);
default:
return const Color(0xFF6750A4);
}
}
String _humanize(String s) {
final result = StringBuffer();
for (var i = 0; i < s.length; i++) {
final c = s[i];
if (i > 0 && c == c.toUpperCase()) result.write(' ');
result.write(i == 0 ? c.toUpperCase() : c);
}
return result.toString();
}
String _descriptionFor(OnboardTemplate t) {
switch (t) {
case OnboardTemplate.classic:
return 'Image top, text bottom, dots, CTAs';
case OnboardTemplate.parallax:
return 'Background drifts at a different speed';
case OnboardTemplate.fade:
return 'Pages crossfade — no swipe';
case OnboardTemplate.zoom:
return 'Active page scales in';
case OnboardTemplate.cardStack:
return 'Stacked cards reveal one at a time';
case OnboardTemplate.splitScreen:
return 'Hero on top, white panel below';
case OnboardTemplate.fullscreen:
return 'Full-bleed background, text overlay';
case OnboardTemplate.minimal:
return 'Big icon + title only';
case OnboardTemplate.glassmorphism:
return 'Frosted glass card on a gradient';
case OnboardTemplate.story:
return 'Instagram-style auto-progressing';
case OnboardTemplate.slideUp:
return 'Text slides up from bottom';
case OnboardTemplate.lottie:
return 'Lottie animations per page';
case OnboardTemplate.editorial:
return 'Bold headline, tilted card, circular CTA';
case OnboardTemplate.vibrant:
return 'Solid bold color, mascot, full-width pill';
case OnboardTemplate.pastel:
return 'Pastel bg, avatars + square arrow';
case OnboardTemplate.darkPro:
return 'Dark, dual CTA (Sign In + Get Started)';
}
}
}
class DemoScreen extends StatelessWidget {
final OnboardTemplate template;
const DemoScreen({super.key, required this.template});
@override
Widget build(BuildContext context) {
switch (template) {
case OnboardTemplate.editorial:
return _EditorialDemo();
case OnboardTemplate.vibrant:
return _VibrantDemo();
case OnboardTemplate.pastel:
return _PastelDemo();
case OnboardTemplate.darkPro:
return _DarkProDemo();
default:
return _GenericDemo(template: template);
}
}
}
// ----- Generic demo used by the original 12 templates -----
class _GenericDemo extends StatelessWidget {
final OnboardTemplate template;
const _GenericDemo({required this.template});
static const _pages = [
OnboardPage(
title: 'Discover',
description:
'Find products and ideas tailored to what you care about.',
image: Icon(Icons.explore_outlined,
size: 120, color: Colors.white),
backgroundColor: Color(0xFF6750A4),
),
OnboardPage(
title: 'Connect',
description:
'Reach friends, family, and a community that gets you.',
image: Icon(Icons.people_outline, size: 120, color: Colors.white),
backgroundColor: Color(0xFF1E88E5),
),
OnboardPage(
title: 'Create',
description:
'Bring your ideas to life with tools that work the way you do.',
image: Icon(Icons.brush_outlined, size: 120, color: Colors.white),
backgroundColor: Color(0xFF43A047),
),
OnboardPage(
title: 'Get Started',
description: 'Tap below to dive in. It only takes a moment.',
image: Icon(Icons.rocket_launch_outlined,
size: 120, color: Colors.white),
backgroundColor: Color(0xFFE53935),
),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Onboardly(
template: template,
pages: _pages,
indicatorType: OnboardIndicatorType.expandingDot,
activeColor: Colors.white,
inactiveColor: Colors.white38,
foregroundColor: Colors.white,
onDone: () => _close(context, 'Done!'),
onSkip: () => _close(context, 'Skipped'),
),
);
}
}
// ----- Editorial demo (ref 2) -----
class _EditorialDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Onboardly(
template: OnboardTemplate.editorial,
pages: const [
OnboardPage(
tag: 'Step 1',
title: 'Empower yourself\nwith quick knowledge',
subtitle: 'Bite-size lessons every day',
description:
'Browse handpicked reads from authors, creators and experts in your pocket.',
image: Icon(Icons.menu_book_rounded,
size: 80, color: Colors.white),
backgroundColor: Color(0xFF7C3AED),
),
OnboardPage(
tag: 'Step 2',
title: 'Elevate your reading\nwith quick insights',
subtitle: 'Hand-picked recommendations',
description: 'Curated content tuned for what you love.',
image: Icon(Icons.lightbulb_rounded,
size: 80, color: Colors.white),
backgroundColor: Color(0xFFE11D48),
),
OnboardPage(
tag: 'Step 3',
title: 'Stay motivated\nand achieve goals',
subtitle: "We'll keep you on track",
description: 'Daily streaks, gentle reminders, no spam.',
image: Icon(Icons.emoji_events_rounded,
size: 80, color: Colors.white),
backgroundColor: Color(0xFF0EA5E9),
),
],
theme: const OnboardTheme(
primaryColor: Color(0xFF111111),
onPrimaryColor: Colors.white,
secondaryColor: Color(0xFFE4E4E7),
surfaceColor: Colors.white,
onSurfaceColor: Color(0xFF111111),
primaryButtonShape: OnboardButtonShape.circle,
),
headerType: OnboardHeaderType.none,
indicatorType: OnboardIndicatorType.expandingDot,
nextLabel: 'Next',
doneLabel: 'Start',
onDone: () => _close(context, 'Editorial done'),
onSkip: () => _close(context, 'Skipped'),
),
);
}
}
// ----- Vibrant demo (ref 3) -----
class _VibrantDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Onboardly(
template: OnboardTemplate.vibrant,
pages: const [
OnboardPage(
title: 'Speak\nwith confidence',
description: 'Master conversation skills with daily prompts.',
image: Icon(Icons.record_voice_over_rounded,
size: 140, color: Color(0xFF111111)),
backgroundColor: Color(0xFFFFD93D),
),
OnboardPage(
title: 'Learn anytime,\neasily anywhere',
description: 'Bite-sized lessons crafted for busy people.',
image: Icon(Icons.translate_rounded,
size: 140, color: Color(0xFF111111)),
backgroundColor: Color(0xFFC4B5FD),
),
OnboardPage(
title: 'Lessons that\nwork for you',
description: 'AI-tuned content matches your learning style.',
image: Icon(Icons.psychology_rounded,
size: 140, color: Color(0xFF111111)),
backgroundColor: Color(0xFFFB923C),
),
],
theme: OnboardTheme.vibrant,
indicatorType: OnboardIndicatorType.none,
nextLabel: 'Get Started',
doneLabel: 'Get Started',
headerType: OnboardHeaderType.skip,
onDone: () => _close(context, 'Vibrant done'),
onSkip: () => _close(context, 'Skipped'),
),
);
}
}
// ----- Pastel demo (ref 4) -----
class _PastelDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Onboardly(
template: OnboardTemplate.pastel,
pages: const [
OnboardPage(
title: 'We will\ntake care',
subtitle: 'of tickets, transfers and a cool place to stay',
image: Icon(Icons.luggage_rounded,
size: 140, color: Color(0xFF8B4513)),
backgroundColor: Color(0xFFFFD93D),
),
OnboardPage(
title: 'Relax\n& enjoy',
subtitle: 'Sunbathe, swim, eat and drink deliciously',
image: Icon(Icons.beach_access_rounded,
size: 140, color: Color(0xFF1E40AF)),
backgroundColor: Color(0xFF7DD3FC),
),
OnboardPage(
title: 'Flexible\npayment',
subtitle: 'credit card and transfer, cryptocurrency',
image: Icon(Icons.payments_rounded,
size: 140, color: Color(0xFFBE185D)),
backgroundColor: Color(0xFFFBCFE8),
),
],
theme: const OnboardTheme(
primaryColor: Color(0xFF111111),
secondaryColor: Color(0xFF111111),
onSurfaceColor: Color(0xFF111111),
skipColor: Color(0xFF111111),
),
headerType: OnboardHeaderType.skip,
brandLogo: const _MiniLogo(),
indicatorType: OnboardIndicatorType.line,
onDone: () => _close(context, 'Pastel done'),
onSkip: () => _close(context, 'Skipped'),
),
);
}
}
// ----- DarkPro demo (ref 5) -----
class _DarkProDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Onboardly(
template: OnboardTemplate.darkPro,
pages: const [
OnboardPage(
title: 'Gym workout generator',
description:
'Get workouts with real-time tracking, heart-rate monitoring and personalised experience.',
image: Icon(Icons.fitness_center_rounded,
size: 140, color: Color(0xFF60A5FA)),
),
OnboardPage(
title: 'Your Workout Plan',
description:
'Personalised plans, real-time tracking and motivating reminders.',
image: Icon(Icons.timer_outlined,
size: 140, color: Color(0xFF60A5FA)),
),
OnboardPage(
title: 'About You',
subtitle: 'What is your name?',
description:
"We'll personalise your journey using your inputs.",
image: Icon(Icons.person_outline_rounded,
size: 140, color: Color(0xFF60A5FA)),
),
],
theme: OnboardTheme.dark,
headerType: OnboardHeaderType.skip,
brandLogo: const Text(
'fitonist',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w800,
letterSpacing: 1,
),
),
indicatorType: OnboardIndicatorType.dot,
nextLabel: 'Get Started',
doneLabel: 'Get Started',
secondaryLabel: 'Sign In',
onSecondaryAction: () => _close(context, 'Sign in tapped'),
onDone: () => _close(context, 'DarkPro done'),
onSkip: () => _close(context, 'Skipped'),
),
);
}
}
class _MiniLogo extends StatelessWidget {
const _MiniLogo();
@override
Widget build(BuildContext context) {
return Container(
width: 32,
height: 32,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: const Center(
child: Text('B',
style: TextStyle(
fontWeight: FontWeight.w900,
color: Colors.black,
)),
),
);
}
}
void _close(BuildContext context, String msg) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(msg), duration: const Duration(seconds: 1)),
);
Navigator.of(context).pop();
}