smooth_onboarding 0.2.1 copy "smooth_onboarding: ^0.2.1" to clipboard
smooth_onboarding: ^0.2.1 copied to clipboard

A reusable, animated Flutter onboarding package with first-launch persistence and dark mode support.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:smooth_onboarding/smooth_onboarding.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const ExampleApp());
}

class ExampleApp extends StatelessWidget {
  const ExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'smooth_onboarding example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      darkTheme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
            seedColor: Colors.blue, brightness: Brightness.dark),
        useMaterial3: true,
        brightness: Brightness.dark,
      ),
      themeMode: ThemeMode.light,
      home: const _AppShell(),
    );
  }
}

class _AppShell extends StatefulWidget {
  const _AppShell();

  @override
  State<_AppShell> createState() => _AppShellState();
}

class _AppShellState extends State<_AppShell> {
  int _reloadToken = 0;

  Future<void> _resetOnboarding() async {
    await OnboardingStorage.reset();
    if (!mounted) {
      return;
    }

    setState(() {
      _reloadToken++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return OnboardingGate(
      reloadTrigger: _reloadToken,
      storageKey: OnboardingStorage.defaultStorageKey,
      pages: const <OnboardingPage>[
        OnboardingPage(
          title: 'Welcome to smooth_onboarding',
          body: Text(
            'A clean and reusable onboarding flow for your Flutter projects.',
          ),
        ),
        OnboardingPage(
          title: 'Modern motion',
          body: _OnboardingIllustration(),
        ),
        OnboardingPage(
          title: 'Production ready',
          body: Text(
            'First-launch persistence, dark mode, customizable theme, and simple API.',
          ),
        ),
      ],
      nextButtonLabel: 'Next',
      doneButtonLabel: 'Get started',
      backButtonTooltip: 'Back',
      progressSemanticsLabel: 'Onboarding progress',
      showBackButton: true,
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Demo app'),
          actions: <Widget>[
            IconButton(
              tooltip: 'Reset onboarding',
              onPressed: _resetOnboarding,
              icon: const Icon(Icons.restart_alt_rounded),
            ),
          ],
        ),
        body: Center(
          child: ConstrainedBox(
            constraints: const BoxConstraints(maxWidth: 520),
            child: Padding(
              padding: const EdgeInsets.all(24),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: const <Widget>[
                  Text(
                    'Onboarding completed.',
                    style: TextStyle(fontSize: 24, fontWeight: FontWeight.w700),
                    textAlign: TextAlign.center,
                  ),
                  SizedBox(height: 12),
                  Text(
                    'Use the top-right action to reset the flag and replay the full flow.',
                    textAlign: TextAlign.center,
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class _OnboardingIllustration extends StatelessWidget {
  const _OnboardingIllustration();

  @override
  Widget build(BuildContext context) {
    final ThemeData theme = Theme.of(context);
    final ColorScheme colorScheme = theme.colorScheme;

    return AspectRatio(
      aspectRatio: 1.15,
      child: DecoratedBox(
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(32),
          gradient: LinearGradient(
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
            colors: <Color>[
              colorScheme.primary.withValues(alpha: 0.18),
              colorScheme.secondary.withValues(alpha: 0.16),
              colorScheme.tertiary.withValues(alpha: 0.12),
            ],
          ),
          border: Border.all(
            color: colorScheme.outlineVariant.withValues(alpha: 0.45),
          ),
        ),
        child: Stack(
          children: <Widget>[
            Positioned(
              left: 18,
              top: 18,
              child: _FloatingBubble(
                size: 64,
                color: colorScheme.primary.withValues(alpha: 0.15),
              ),
            ),
            Positioned(
              right: 16,
              top: 28,
              child: _FloatingBubble(
                size: 42,
                color: colorScheme.tertiary.withValues(alpha: 0.22),
              ),
            ),
            Align(
              alignment: Alignment.center,
              child: Container(
                width: 176,
                height: 250,
                decoration: BoxDecoration(
                  color: colorScheme.surface,
                  borderRadius: BorderRadius.circular(36),
                  boxShadow: <BoxShadow>[
                    BoxShadow(
                      color: Colors.black.withValues(alpha: 0.12),
                      blurRadius: 24,
                      offset: const Offset(0, 14),
                    ),
                  ],
                  border: Border.all(
                    color: colorScheme.outlineVariant.withValues(alpha: 0.5),
                  ),
                ),
                child: Padding(
                  padding: const EdgeInsets.all(16),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Row(
                        children: <Widget>[
                          Container(
                            width: 42,
                            height: 42,
                            decoration: BoxDecoration(
                              color: colorScheme.primaryContainer,
                              borderRadius: BorderRadius.circular(14),
                            ),
                            child: Icon(
                              Icons.auto_awesome_rounded,
                              color: colorScheme.onPrimaryContainer,
                            ),
                          ),
                          const Spacer(),
                          Container(
                            width: 18,
                            height: 18,
                            decoration: BoxDecoration(
                              color:
                                  colorScheme.primary.withValues(alpha: 0.18),
                              shape: BoxShape.circle,
                            ),
                          ),
                        ],
                      ),
                      const SizedBox(height: 18),
                      Text(
                        'Shared axis',
                        style: theme.textTheme.titleMedium?.copyWith(
                          fontWeight: FontWeight.w700,
                        ),
                      ),
                      const SizedBox(height: 8),
                      Text(
                        'Smooth transitions with a polished finish.',
                        style: theme.textTheme.bodyMedium?.copyWith(
                          color: colorScheme.onSurfaceVariant,
                          height: 1.35,
                        ),
                      ),
                      const Spacer(),
                      Container(
                        height: 14,
                        decoration: BoxDecoration(
                          color: colorScheme.surfaceContainerHighest,
                          borderRadius: BorderRadius.circular(999),
                        ),
                        child: FractionallySizedBox(
                          alignment: Alignment.centerLeft,
                          widthFactor: 0.72,
                          child: DecoratedBox(
                            decoration: BoxDecoration(
                              gradient: LinearGradient(
                                colors: <Color>[
                                  colorScheme.primary,
                                  colorScheme.tertiary,
                                ],
                              ),
                              borderRadius: BorderRadius.circular(999),
                            ),
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
            Positioned(
              left: 24,
              bottom: 20,
              child: _MiniBadge(
                label: 'No assets',
                icon: Icons.verified_outlined,
                color: colorScheme.secondary,
              ),
            ),
            Positioned(
              right: 24,
              bottom: 26,
              child: _MiniBadge(
                label: 'Built in',
                icon: Icons.layers_outlined,
                color: colorScheme.primary,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class _FloatingBubble extends StatelessWidget {
  const _FloatingBubble({required this.size, required this.color});

  final double size;
  final Color color;

  @override
  Widget build(BuildContext context) {
    return Container(
      width: size,
      height: size,
      decoration: BoxDecoration(
        color: color,
        shape: BoxShape.circle,
      ),
    );
  }
}

class _MiniBadge extends StatelessWidget {
  const _MiniBadge({
    required this.label,
    required this.icon,
    required this.color,
  });

  final String label;
  final IconData icon;
  final Color color;

  @override
  Widget build(BuildContext context) {
    final ThemeData theme = Theme.of(context);
    return DecoratedBox(
      decoration: BoxDecoration(
        color: theme.colorScheme.surface.withValues(alpha: 0.88),
        borderRadius: BorderRadius.circular(999),
        border: Border.all(
          color: theme.colorScheme.outlineVariant.withValues(alpha: 0.4),
        ),
      ),
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Icon(icon, size: 14, color: color),
            const SizedBox(width: 6),
            Text(
              label,
              style: theme.textTheme.labelMedium?.copyWith(
                fontWeight: FontWeight.w600,
              ),
            ),
          ],
        ),
      ),
    );
  }
}
1
likes
0
points
331
downloads

Publisher

verified publishereliazavatta.it

Weekly Downloads

A reusable, animated Flutter onboarding package with first-launch persistence and dark mode support.

Repository (GitHub)
View/report issues

Topics

#onboarding #flutter #ui #animation #first-launch

License

unknown (license)

Dependencies

flutter, shared_preferences

More

Packages that depend on smooth_onboarding