Skip to content

Software Development at Program Tom LTD

Place for coding, programming, development and software in general.

Menu
  • Blog
  • PDF Booklets
  • Dev Utils & Content
  • Java Spring Boot Or Web Apps
  • English
    • български
    • English
    • Español
    • Português
    • हिन्दी
    • Русский
    • Deutsch
    • Français
    • Italiano
    • العربية
  • About Us
Menu
Flutter One Time Home Screen Feature

Flutter One Time Home Screen Feature

Posted on January 25, 2026 by Toma Velev

Presenting a tutorial or feature showcase conditionally (only the first time) in a Flutter BLoC-managed app requires a clean separation between persistence logic (checking if they’ve seen it), state management (the BLoC event flow), and the UI (the showcaseview library).

Here is a step-by-step implementation guide.

1. Dependencies

Add these to your pubspec.yaml:

dependencies:
  flutter_bloc: ^8.1.0
  showcaseview: ^3.0.0
  shared_preferences: ^2.2.0 # To remember if the user saw it

2. The Persistence Layer

You need a way to store whether the user has already completed the tutorial. A simple repository handles this:

class TutorialRepository {
  static const _key = 'has_seen_home_showcase';

  Future<bool> hasSeenTutorial() async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getBool(_key) ?? false;
  }

  Future<void> markTutorialAsSeen() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setBool(_key, true);
  }
}

3. The BLoC / Cubit

The BLoC handles the logic of checking the repository when the Home Screen loads and updating it when the showcase ends.

// State
class TutorialState {
  final bool shouldShowTutorial;
  TutorialState({this.shouldShowTutorial = false});
}

// Cubit
class TutorialCubit extends Cubit<TutorialState> {
  final TutorialRepository repository;

  TutorialCubit(this.repository) : super(TutorialState());

  Future<void> checkTutorialStatus() async {
    final hasSeen = await repository.hasSeenTutorial();
    if (!hasSeen) {
      emit(TutorialState(shouldShowTutorial: true));
    }
  }

  Future<void> completeTutorial() async {
    await repository.markTutorialAsSeen();
    emit(TutorialState(shouldShowTutorial: false));
  }
}

4. The Home Screen Implementation

The Home Screen needs to be wrapped in a ShowCaseWidget. We use BlocListener to trigger the showcase only when the Cubit says so.

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

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

class _HomeScreenState extends State<HomeScreen> {
  // 1. Create GlobalKeys for the features you want to highlight
  final GlobalKey _profileKey = GlobalKey();
  final GlobalKey _addFeatureKey = GlobalKey();

  @override
  void initState() {
    super.initState();
    // 2. Check if tutorial should run
    context.read<TutorialCubit>().checkTutorialStatus();
  }

  @override
  Widget build(BuildContext context) {
    // 3. Wrap everything in ShowCaseWidget
    return ShowCaseWidget(
      onFinish: () => context.read<TutorialCubit>().completeTutorial(),
      builder: Builder(
        builder: (showcaseContext) {
          return BlocListener<TutorialCubit, TutorialState>(
            listener: (context, state) {
              if (state.shouldShowTutorial) {
                // 4. Start showcase after the frame is rendered
                WidgetsBinding.instance.addPostFrameCallback((_) {
                  ShowCaseWidget.of(showcaseContext).startShowCase([
                    _profileKey,
                    _addFeatureKey,
                  ]);
                });
              }
            },
            child: Scaffold(
              appBar: AppBar(
                title: const Text('Home'),
                actions: [
                  // 5. Wrap specific widgets with Showcase
                  Showcase(
                    key: _profileKey,
                    title: 'Profile',
                    description: 'Click here to edit your profile',
                    child: const Icon(Icons.person),
                  ),
                ],
              ),
              body: Center(
                child: Showcase(
                  key: _addFeatureKey,
                  title: 'New Feature',
                  description: 'This is the cool new feature!',
                  child: ElevatedButton(
                    onPressed: () {},
                    child: const Text('Explore'),
                  ),
                ),
              ),
            ),
          );
        },
      ),
    );
  }
}

Key Considerations

  • The Context Trap: ShowCaseWidget.of(context) requires a context below the ShowCaseWidget in the tree. That is why the builder: Builder(...) pattern is used above—it provides a fresh BuildContext that can “see” the ShowCaseWidget.
  • PostFrameCallback: You cannot trigger the showcase during the build phase. WidgetsBinding.instance.addPostFrameCallback ensures the UI is fully rendered before the overlay appears.
  • Login Flow: Ensure the TutorialCubit is provided high enough in the tree (e.g., at the MaterialApp level or as soon as the user logs in) so it is ready when the HomeScreen is pushed.
  • Resetting: For testing, you can add a “Reset Tutorial” button in your settings that calls a method in the Repository to clear the SharedPreferences key.

Pro-Tip: Hydrated BLoC

If you want to skip the TutorialRepository and SharedPreferences boilerplate, you can use the hydrated_bloc package. It will automatically persist your TutorialState to local storage, allowing you to simply check state.hasSeen directly without manual async fetching.

  • Join iOS Beta Testing Explained
  • Firebase App Distribution Setup
  • iOS App Lifetime Unverified
  • Flutter Bottom Border
  • Get Flutter current time zone

Categories

  • Apps (25)
  • ChatGPT (24)
  • Choosing a Framework (38)
  • Flutter (279)
  • Graphical User Interface (14)
  • Marketing (118)
  • Software Development (288)
  • Spring (45)
  • StartUp (22)
  • Uncategorized (4)
  • Uncategorized (14)
  • Vaadin (15)

Tags

Algorithms (9) crypto (29) flutterdev (39) General (86) Java (7) QR & Bar Codes (3) Software Dev Choices (33) Spring Boot (1) standards (1) Theme (3) User Authentication & Authorization (9) User Experience (10) Utilities (19) WordPress (11)

Product categories

  • All Technologies (86)
    • Flutter Apps (26)
    • GPT (4)
    • Java (38)
    • Native Android (3)
    • PHP (9)
    • Spring (Boot) / Quarkus (35)
    • Utils (15)
    • Vaadin 24+ (27)
    • Vaadin 8 (1)
  • Apps (18)
    • Employees DB (1)
    • Notes (6)
    • Personal Budget (1)
    • Recipes Book (1)
    • Stuff Organizer (1)
    • To-Do (2)
  • PDF Books (3)
  • Source Code Generators (8)

Recent Posts

  • Join iOS Beta Testing Explained
  • Firebase App Distribution Setup
  • iOS App Lifetime Unverified
  • Flutter Bottom Border
  • Get Flutter current time zone

Post Categories

  • Apps (25)
  • ChatGPT (24)
  • Choosing a Framework (38)
  • Flutter (279)
  • Graphical User Interface (14)
  • Marketing (118)
  • Software Development (288)
  • Spring (45)
  • StartUp (22)
  • Uncategorized (4)
  • Uncategorized (14)
  • Vaadin (15)