Skip to content

Card field delay#2337

Open
russellsjensen wants to merge 2 commits intoflutter-stripe:mainfrom
puurk:card-field-delay
Open

Card field delay#2337
russellsjensen wants to merge 2 commits intoflutter-stripe:mainfrom
puurk:card-field-delay

Conversation

@russellsjensen
Copy link

@russellsjensen russellsjensen commented Feb 16, 2026

Use MutationObserver to wait for the DOM element to be inserted, matching the pattern used by PaymentElement. This is necessary because postFrameCallback timing is unreliable in deployed environments so sometimes the card field would not render.

Summary by CodeRabbit

  • Bug Fixes
    • Improved Stripe card field initialization with enhanced timing handling for better cross-browser compatibility, particularly in Safari and post-frame rendering scenarios.

@coderabbitai
Copy link

coderabbitai bot commented Feb 16, 2026

📝 Walkthrough

Walkthrough

This PR modifies build artifact exclusion in .gitignore and refactors the Card element initialization in the Stripe web widget from a post-frame callback pattern to a MutationObserver-based approach for improved timing reliability across browsers.

Changes

Cohort / File(s) Summary
Build Configuration
.gitignore
Adds ignore pattern for build directories across the project hierarchy.
Card Element Initialization
packages/stripe_web/lib/src/widgets/card_field.dart
Replaces post-frame initialization with MutationObserver-based DOM detection. Introduces _mutationObserver field and _mountCard() helper method. Updates disposal logic to disconnect observer and prevent memory leaks. Preserves existing lifecycle hooks and error messaging.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Down the build path I did hop,
Adding rules so clutter won't stop!
With mutations watched and observers keen,
The Card element setup's pristine!
Timing troubles? Now they're tame,
Safari struggles fade from fame! 🌳

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Card field delay' is vague and does not clearly convey the actual change. The PR replaces post-frame callback timing with MutationObserver for DOM element detection—not about adding delays. Consider a more specific title like 'Use MutationObserver for CardField DOM element detection' or 'Fix CardField rendering with MutationObserver-based initialization' to clearly describe the implementation change.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
Before applying any fix, first verify the finding against the current code and
decide whether a code change is actually needed. If the finding is not valid or
no change is required, do not modify code for that item and briefly explain why
it was skipped.

In `@packages/stripe_web/lib/src/widgets/card_field.dart`:
- Around line 88-111: The MutationObserver and initial mount logic are
incorrectly nested inside the if (!widget.dangerouslyUpdateFullCardDetails)
block, so when dangerouslyUpdateFullCardDetails is true the card never mounts;
refactor the build/init path so only the debug/dev.log warning (which checks
controller.details vs CardFieldInputDetails) remains conditional, and move the
MutationObserver creation/observe and the call to _mountCard() out of that
conditional so they always run during initialization (while preserving the
existing _mutationObserver variable and the disconnect logic).

Comment on lines +88 to +111
if (!widget.dangerouslyUpdateFullCardDetails) {
if (kDebugMode &&
controller.details !=
const CardFieldInputDetails(complete: false)) {
dev.log('WARNING! Initial card data value has been ignored. \n'
'$kDebugPCIMessage');
}
});
// Use MutationObserver to wait for the DOM element to be inserted,
// matching the pattern used by PaymentElement. This is necessary
// because postFrameCallback timing is unreliable on Safari in
// deployed environments.
_mutationObserver = web.MutationObserver(
((JSArray<web.MutationRecord> entries, web.MutationObserver observer) {
if (web.document.getElementById('card-element') != null) {
_mutationObserver?.disconnect();
_mountCard();
}
}.toJS),
);
_mutationObserver!.observe(
web.document,
web.MutationObserverInit(childList: true, subtree: true),
);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Card element is never mounted when dangerouslyUpdateFullCardDetails is true.

The entire MutationObserver setup (including the call to _mountCard()) is inside the if (!widget.dangerouslyUpdateFullCardDetails) block. When this flag is true, the card element will never be mounted, causing the field to not render at all.

The mounting logic should execute regardless of this flag; only the debug warning for initial card data should be conditional.

🐛 Proposed fix
   void initStripe() {
     attachController(controller);
     if (!widget.dangerouslyUpdateFullCardDetails) {
       if (kDebugMode &&
           controller.details !=
               const CardFieldInputDetails(complete: false)) {
         dev.log('WARNING! Initial card data value has been ignored. \n'
             '$kDebugPCIMessage');
       }
-      // Use MutationObserver to wait for the DOM element to be inserted,
-      // matching the pattern used by PaymentElement. This is necessary
-      // because postFrameCallback timing is unreliable on Safari in
-      // deployed environments.
-      _mutationObserver = web.MutationObserver(
-        ((JSArray<web.MutationRecord> entries, web.MutationObserver observer) {
-          if (web.document.getElementById('card-element') != null) {
-            _mutationObserver?.disconnect();
-            _mountCard();
-          }
-        }.toJS),
-      );
-      _mutationObserver!.observe(
-        web.document,
-        web.MutationObserverInit(childList: true, subtree: true),
-      );
     }
+    // Use MutationObserver to wait for the DOM element to be inserted,
+    // matching the pattern used by PaymentElement. This is necessary
+    // because postFrameCallback timing is unreliable on Safari in
+    // deployed environments.
+    _mutationObserver = web.MutationObserver(
+      ((JSArray<web.MutationRecord> entries, web.MutationObserver observer) {
+        if (web.document.getElementById('card-element') != null) {
+          _mutationObserver?.disconnect();
+          _mountCard();
+        }
+      }.toJS),
+    );
+    _mutationObserver!.observe(
+      web.document,
+      web.MutationObserverInit(childList: true, subtree: true),
+    );
   }
🤖 Prompt for AI Agents
Before applying any fix, first verify the finding against the current code and
decide whether a code change is actually needed. If the finding is not valid or
no change is required, do not modify code for that item and briefly explain why
it was skipped.
In `@packages/stripe_web/lib/src/widgets/card_field.dart` around lines 88 - 111,
The MutationObserver and initial mount logic are incorrectly nested inside the
if (!widget.dangerouslyUpdateFullCardDetails) block, so when
dangerouslyUpdateFullCardDetails is true the card never mounts; refactor the
build/init path so only the debug/dev.log warning (which checks
controller.details vs CardFieldInputDetails) remains conditional, and move the
MutationObserver creation/observe and the call to _mountCard() out of that
conditional so they always run during initialization (while preserving the
existing _mutationObserver variable and the disconnect logic).

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

Comments