Skip to content

feat(splash): fix reactive task error handling to prevent unnecessary splash screen display#18

Merged
pckimlong merged 1 commit into
mainfrom
dev
Dec 7, 2025
Merged

feat(splash): fix reactive task error handling to prevent unnecessary splash screen display#18
pckimlong merged 1 commit into
mainfrom
dev

Conversation

@pckimlong
Copy link
Copy Markdown
Owner

Convert SplashBuilder to StatefulWidget to track trigger-caused refreshes. Add _triggerCausedRefresh flag to distinguish between trigger changes (show splash) and other refreshes like errors or dependency changes (don't show splash). Update reactive task completion logic to skip splash when errors occur without trigger change. Set trigger flag on manual retry. Clear flag after reactive task completes using

… splash screen display

Convert SplashBuilder to StatefulWidget to track trigger-caused refreshes. Add _triggerCausedRefresh flag to distinguish between trigger changes (show splash) and other refreshes like errors or dependency changes (don't show splash). Update reactive task completion logic to skip splash when errors occur without trigger change. Set trigger flag on manual retry. Clear flag after reactive task completes using
Copilot AI review requested due to automatic review settings December 7, 2025 17:29
@pckimlong pckimlong merged commit 6d36925 into main Dec 7, 2025
3 of 4 checks passed
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @pckimlong, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refines the splash screen behavior in the application by introducing more granular control over when the splash screen is displayed during reactive task execution. Previously, errors or dependency reloads could inadvertently cause the splash screen to flash, disrupting the user experience. By converting the SplashBuilder to a stateful widget and tracking the origin of task refreshes, the system now intelligently suppresses the splash screen for non-trigger-related errors, ensuring it only appears when genuinely needed for initial loads or explicit trigger changes.

Highlights

  • Stateful Widget Conversion: The SplashBuilder has been converted from a ConsumerWidget to a ConsumerStatefulWidget to allow for internal state management, specifically to track the cause of reactive task refreshes.
  • Trigger-Caused Refresh Tracking: A new internal state variable, _triggerCausedRefresh, has been introduced to distinguish between refreshes initiated by a trigger change (which should show the splash screen) and other types of refreshes, such as errors or dependency changes (which should not).
  • Reactive Task Error Handling: The logic for determining reactiveComplete has been updated to prevent the splash screen from displaying when a reactive task encounters an error that was not caused by a trigger change, ensuring a smoother user experience.
  • Flag Management: The _triggerCausedRefresh flag is now set to true when the reactive task trigger changes or when a manual retry is initiated. It is subsequently cleared using Future.microtask once the reactive task completes, whether successfully or with an error.
  • New Test Case: A new testWidgets case has been added to test/reactive_task_test.dart to specifically verify that the splash screen does not reappear when a reactive task errors without a corresponding trigger change.
  • Version Update: The package version in pubspec.yaml has been incremented from 0.1.0 to 0.1.1.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively addresses an issue where the splash screen was unnecessarily displayed during reactive task errors that were not caused by a trigger change. The conversion of SplashBuilder to a ConsumerStatefulWidget and the introduction of the _triggerCausedRefresh flag is a solid approach to distinguish between different refresh causes. The accompanying widget test is well-written and correctly verifies the fix.

My main feedback concerns the implementation of the state management for the _triggerCausedRefresh flag. The current approach involves side effects within the build method and state mutations without setState, which goes against Flutter's best practices and can lead to fragile, hard-to-maintain code. I've provided a detailed suggestion to refactor this using a more idiomatic Riverpod and Flutter pattern, which will improve the robustness and clarity of the implementation.

Comment on lines 31 to +46
ref.listen(_reactiveTaskTriggerProvider, (_, _) {
_triggerCausedRefresh = true;
ref.invalidate(_reactiveTaskRunProvider);
});

final reactiveTaskRun = ref.watch(_reactiveTaskRunProvider);

// Clear trigger flag once reactive task completes (has value or error)
if ((reactiveTaskRun.hasValue || reactiveTaskRun.hasError) && _triggerCausedRefresh) {
// Use Future.microtask to avoid modifying state during build
Future.microtask(() {
if (mounted) {
_triggerCausedRefresh = false;
}
});
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current implementation for managing the _triggerCausedRefresh flag has a couple of architectural issues:

  1. Implicit State Updates: On line 32, _triggerCausedRefresh is mutated directly. The UI updates correctly only because ref.invalidate happens to trigger a rebuild immediately after. This is fragile and hard to reason about. State changes that affect the UI should be explicit, using setState.
  2. Side Effect in build: The if block on lines 39-46 performs state mutation as a side effect of the build method. This is a significant violation of Flutter's declarative UI principles, which require the build method to be pure (i.e., free of side effects).

I recommend refactoring this logic to use ref.listen for both setting and resetting the flag. This is the idiomatic Riverpod approach for reacting to provider changes and performing side effects like updating widget state. This change will make the build method pure and the state management more robust and maintainable.

    ref.listen<void>(_reactiveTaskTriggerProvider, (_, __) {
      if (!mounted) return;
      setState(() {
        _triggerCausedRefresh = true;
      });
      ref.invalidate(_reactiveTaskRunProvider);
    });

    final reactiveTaskRun = ref.watch(_reactiveTaskRunProvider);

    ref.listen<AsyncValue<void>>(_reactiveTaskRunProvider, (previous, next) {
      final wasLoading = previous?.isLoading ?? false;
      final isDone = next.hasValue || next.hasError;

      if (wasLoading && isDone && _triggerCausedRefresh) {
        // Use a post-frame callback to avoid calling setState during a build,
        // as the listener can fire while a build is in progress.
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (mounted) {
            setState(() {
              _triggerCausedRefresh = false;
            });
          }
        });
      }
    });

@pckimlong pckimlong review requested due to automatic review settings March 23, 2026 19:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant