Skip to content

Exception in _handleCardChanged#2265

Open
uristrimber wants to merge 5 commits intoflutter-stripe:mainfrom
uristrimber:dev
Open

Exception in _handleCardChanged#2265
uristrimber wants to merge 5 commits intoflutter-stripe:mainfrom
uristrimber:dev

Conversation

@uristrimber
Copy link
Copy Markdown

@uristrimber uristrimber commented Nov 18, 2025

Fix: Revert card parsing logic in _handleCardChanged

This PR reverts the changes made to the input parsing logic in card_field.dart. The arguments map does not contain a nested 'card' key inside it.

Instead of incorrectly attempting to extract map['card'], this change reverts to passing the entire map directly to CardFieldInputDetails.fromJson.

Summary by CodeRabbit

  • Bug Fixes
    • Improved the Stripe card input widget to more robustly handle varying payment data formats, reducing errors and improving stability when users enter or update card details.

@remonh87 remonh87 self-requested a review December 16, 2025 20:54
try {
final map = Map<String, dynamic>.from(arguments);
final update = CardFieldInputDetails.fromJson(
Map<String, dynamic>.from(map['card']),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see this nesting is still necesarry tested it with latest sdk on android

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

the map doesn't have the 'card' value, it creates an exception and crashes.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Does the UI works for you?.
We had to fork the package in PROD as it was throwing an eception

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I tested both android and ios and for the ui works. Can you give me some more details of the version of the package that you are using?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Sorry I've been very busy, I'll send a demo ASAP.

Copy link
Copy Markdown
Author

@uristrimber uristrimber Mar 27, 2026

Choose a reason for hiding this comment

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

Perfect, I have an easy day so I can help you a little bit (with debbuging for ex.) if you need me.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Want me do do the changes in the PR?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@uristrimber when do you think you can make the changes. Would like to integrate it and take it along next version

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@remonh87 updated in f2f506c — now checks for map['card'] and falls through to the flat map when it isn't there, so both payload shapes parse correctly. Ready for another look.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

My AI code-checker wrote this, what do you think @remonh87?

consider applying the same fix to card_form_field.dart.

The nested is Map check handles both payload shapes safely:

Wrapped {card: {...}} → parses nested map.
Flat {...} (or missing card key / non-Map value) → falls back to the outer map, avoiding the Null is not a subtype of Map crash reported in this issue.
null is Map is false, so a missing card key gracefully hits the fallback.
One follow-up: packages/stripe/lib/src/widgets/card_form_field.dart (_handleCardChanged, lines ~505–527) still unconditionally does Map<String, dynamic>.from(map['card']) and would crash the same way if the platform ever emits a flat payload for the card form. Worth mirroring this defensive check there so the two widgets behave consistently.

@uristrimber uristrimber requested a review from remonh87 January 30, 2026 13:48
@remonh87 remonh87 added the Awaiting response Awaiting response from the issuer label Mar 27, 2026
Native layers send {card: {...}} for some card states and the flat
fields for others. Extract map['card'] when present and fall through
to the outer map otherwise so incomplete-card events stop crashing.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

📝 Walkthrough

Walkthrough

The card-change handler in the card field widget now conditionally checks the runtime type of the incoming card payload before converting it to Map<String, dynamic>, falling back to the original payload when the nested card is not a Map.

Changes

Cohort / File(s) Summary
Card Field Platform Handler
packages/stripe/lib/src/widgets/card_field.dart
Make JSON parsing of the card payload defensive: check if map['card'] is a Map and convert to Map<String, dynamic> when appropriate, otherwise pass the original payload to CardFieldInputDetails.fromJson.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A cautious fix hops into place,
It inspects each payload with gentle grace,
If card is a Map it reshapes the nest,
Else it keeps the original for the test,
Hoppity—no crash, just tidy rest.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Exception in _handleCardChanged' directly references the function being fixed and indicates the specific issue being resolved.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
packages/stripe/lib/src/widgets/card_field.dart (1)

544-547: Defensive unwrap handles both payload shapes.

Native bridges on both iOS (CardFieldView.swift/CardFormView.swift) and Android (CardChangeEvent.kt/CardFormCompleteEvent.kt) wrap the data under "card", so the nested is Map branch will be the normal path, while the else map branch preserves compatibility with the flat payload that @uristrimber reported seeing in production. This lines up with the earlier suggestion to support both wrapped and flat shapes, and avoids the exception/crash described in #2265.

One small note: once the dust settles, it would be worth confirming on a recent SDK whether the flat-payload case still actually occurs, and if not, logging (via dev.log) when the fallback branch is taken so a future cleanup can remove it safely.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stripe/lib/src/widgets/card_field.dart` around lines 544 - 547,
Handle both wrapped ("card") and flat payload shapes when parsing card event
data: read map['card'] into a local nested variable, set cardJson to Map<String,
dynamic>.from(nested) if nested is Map else fallback to the original map, and
pass cardJson into CardFieldInputDetails.fromJson to avoid crashes; keep the
current defensive branch using the nested and cardJson variables and consider
adding a dev.log when the fallback branch is taken for future cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/stripe/lib/src/widgets/card_field.dart`:
- Around line 544-547: Handle both wrapped ("card") and flat payload shapes when
parsing card event data: read map['card'] into a local nested variable, set
cardJson to Map<String, dynamic>.from(nested) if nested is Map else fallback to
the original map, and pass cardJson into CardFieldInputDetails.fromJson to avoid
crashes; keep the current defensive branch using the nested and cardJson
variables and consider adding a dev.log when the fallback branch is taken for
future cleanup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4270cdd0-39a1-4788-ba4b-9a77dc7d746d

📥 Commits

Reviewing files that changed from the base of the PR and between 574acfb and f2f506c.

📒 Files selected for processing (1)
  • packages/stripe/lib/src/widgets/card_field.dart

Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
packages/stripe/lib/src/widgets/card_field.dart (1)

556-559: Conditional fallback looks correct; consider applying the same fix to card_form_field.dart.

The nested is Map check handles both payload shapes safely:

  • Wrapped {card: {...}} → parses nested map.
  • Flat {...} (or missing card key / non-Map value) → falls back to the outer map, avoiding the Null is not a subtype of Map crash reported in this issue.
  • null is Map is false, so a missing card key gracefully hits the fallback.

One follow-up: packages/stripe/lib/src/widgets/card_form_field.dart (_handleCardChanged, lines ~505–527) still unconditionally does Map<String, dynamic>.from(map['card']) and would crash the same way if the platform ever emits a flat payload for the card form. Worth mirroring this defensive check there so the two widgets behave consistently.

♻️ Optional: mirror the same pattern in card_form_field.dart
-      } else {
-        final details = CardFieldInputDetails.fromJson(
-          Map<String, dynamic>.from(map['card']),
-        );
-        controller._updateDetails(details);
-        widget.onCardChanged?.call(details);
-      }
+      } else {
+        final nested = map['card'];
+        final cardJson =
+            nested is Map ? Map<String, dynamic>.from(nested) : map;
+        final details = CardFieldInputDetails.fromJson(cardJson);
+        controller._updateDetails(details);
+        widget.onCardChanged?.call(details);
+      }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stripe/lib/src/widgets/card_field.dart` around lines 556 - 559, In
_handleCardChanged in card_form_field.dart the code currently does an
unconditional Map<String, dynamic>.from(map['card']) which can throw if
map['card'] is null or not a Map; change it to mirror card_field.dart by first
reading final nested = map['card']; then set final cardJson = nested is Map ?
Map<String, dynamic>.from(nested) : map; and pass cardJson into
CardFormFieldInputDetails.fromJson (or the existing parser used in
_handleCardChanged) so the handler safely supports both wrapped {card: {...}}
and flat payloads without crashing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/stripe/lib/src/widgets/card_field.dart`:
- Around line 556-559: In _handleCardChanged in card_form_field.dart the code
currently does an unconditional Map<String, dynamic>.from(map['card']) which can
throw if map['card'] is null or not a Map; change it to mirror card_field.dart
by first reading final nested = map['card']; then set final cardJson = nested is
Map ? Map<String, dynamic>.from(nested) : map; and pass cardJson into
CardFormFieldInputDetails.fromJson (or the existing parser used in
_handleCardChanged) so the handler safely supports both wrapped {card: {...}}
and flat payloads without crashing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 936376d2-1882-4929-aab9-3ff6723cade1

📥 Commits

Reviewing files that changed from the base of the PR and between f2f506c and 413f297.

📒 Files selected for processing (1)
  • packages/stripe/lib/src/widgets/card_field.dart

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Awaiting response Awaiting response from the issuer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants