feature_guide 0.1.1 copy "feature_guide: ^0.1.1" to clipboard
feature_guide: ^0.1.1 copied to clipboard

Feature-discovery toolkit for Flutter — tips, banners, coach marks, and badges with session-aware gating.

example/lib/main.dart

import 'package:feature_guide/feature_guide.dart';
import 'package:flutter/material.dart';
import 'package:overlay_support/overlay_support.dart';

/// Events this demo emits. `FeatureDiscoveryService` is generic on this enum.
enum DemoEvent { onStartupTick, onProductViewed }

late final FeatureDiscoveryService<DemoEvent> discovery;

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await TipLedger().init();

  discovery = FeatureDiscoveryService<DemoEvent>()
    ..registerAll([
      DiscoveryTip<DemoEvent>(
        id: 'welcome_banner',
        priority: DiscoveryPriority.standard,
        triggers: {DemoEvent.onStartupTick},
        shouldShow: (ctx) async => true,
        presentation: BannerPresentation(
          title: 'Welcome!',
          body: 'Try tapping the bookmark to see a coach mark.',
          cardColor: Colors.white,
          accentColor: Colors.indigo,
        ),
      ),
      DiscoveryTip<DemoEvent>(
        id: 'bookmark_coach',
        // `standard` (not `tip`) so the welcome banner's session lock doesn't
        // suppress this coach mark when the user taps "View product".
        priority: DiscoveryPriority.standard,
        triggers: {DemoEvent.onProductViewed},
        shouldShow: (ctx) async => true,
        presentation: const CoachMarkPresentation(
          anchorKey: 'bookmarkBtn',
          title: 'Save for later',
          message: 'Tap the bookmark to keep this product for your next visit.',
          bubbleColor: Colors.indigo,
          style: CoachMarkStyle.compact,
        ),
      ),
    ]);

  runApp(const _App());
}

class _App extends StatefulWidget {
  const _App();

  @override
  State<_App> createState() => _AppState();
}

class _AppState extends State<_App> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.paused:
        discovery.onAppPaused();
        break;
      case AppLifecycleState.resumed:
        discovery.onAppResumed();
        break;
      default:
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    return OverlaySupport.global(
      child: MaterialApp(
        title: 'feature_guide demo',
        theme: ThemeData(
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
          useMaterial3: true,
        ),
        home: const _HomeScreen(),
      ),
    );
  }
}

class _HomeScreen extends StatefulWidget {
  const _HomeScreen();

  @override
  State<_HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<_HomeScreen> {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      discovery.emit(
        context,
        DemoEvent.onStartupTick,
        const DiscoveryContext(appLaunches: 1),
      );
    });
  }

  void _openProduct() {
    discovery.emit(
      context,
      DemoEvent.onProductViewed,
      const DiscoveryContext(),
    );
  }

  Future<void> _clearSeenTips() async {
    // `clearSeen` is the public API for undoing a "seen" mark. For full state
    // reset in a test, use `TipLedger().resetForTests()` (test-only).
    await TipLedger().clearSeen('welcome_banner');
    await TipLedger().clearSeen('bookmark_coach');
    await TipLedger().clearSeen('reset_badge');
    await TipLedger().clearSeen('bookmark_badge');
    discovery.resetSession();
    if (!mounted) return;
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(
        content: Text('Seen state cleared — restart to see tips again.'),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('feature_guide'),
        actions: [
          // Wrapping the action icon in a DiscoveryBadgeWrapper puts a small
          // "New" dot in its corner until markSeen fires.
          DiscoveryBadgeWrapper(
            tipId: 'reset_badge',
            dotColor: Colors.redAccent,
            child: IconButton(
              icon: const Icon(Icons.refresh),
              onPressed: () async {
                await TipLedger().markSeen('reset_badge');
                await _clearSeenTips();
              },
            ),
          ),
        ],
      ),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            FilledButton(
              onPressed: _openProduct,
              child: const Text('View product'),
            ),
            const SizedBox(height: 24),
            // DiscoveryAnchor registers against anchorKey 'bookmarkBtn' so the
            // coach-mark in main() can find this widget.
            DiscoveryBadgeWrapper(
              tipId: 'bookmark_badge',
              dotColor: Colors.redAccent,
              child: IconButton(
                onPressed: () async {
                  await TipLedger().markSeen('bookmark_badge');
                  _openProduct();
                },
                icon: const DiscoveryAnchor(
                  anchorKey: 'bookmarkBtn',
                  child: Icon(Icons.bookmark_outline, size: 32),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
0
likes
160
points
32
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Feature-discovery toolkit for Flutter — tips, banners, coach marks, and badges with session-aware gating.

Repository (GitHub)
View/report issues

Topics

#feature-discovery #onboarding #tooltips #coach-marks

License

GPL-3.0 (license)

Dependencies

flutter, overlay_support, shared_preferences

More

Packages that depend on feature_guide