flutter_stripe icon indicating copy to clipboard operation
flutter_stripe copied to clipboard

Error rendering second CardFormField screen on Android

Open daviddavis83 opened this issue 3 years ago • 4 comments

Describe the bug I am trying to implement the CardFormField widget from the flutter_stripe package. The code works as expected on iOS but on android I am getting an error when trying to render the widget for a second time.

To Reproduce Steps to reproduce the behavior:

  1. user purchases a product using the CardFormField
  2. user navigates to another part of the app
  3. user returns to store and proceeds to checkout screen
  4. the CardFormField does not render (android only)

NOTE ** if the user navigates to the checkout screen but does not submit the form then they can navigate away and return any number of times and the CardFormField renders, this only occurs if we submit.

Expected behavior The CardFromField renders any time the checkout screen is visited

[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(error, java.lang.IllegalStateException: The Android view returned from PlatformView#getView() was already added to a parent view. E/flutter (26589): at io.flutter.plugin.platform.PlatformViewsController$1.createForTextureLayer(PlatformViewsController.java:238) E/flutter (26589): at io.flutter.embedding.engine.systemchannels.PlatformViewsChannel$1.create(PlatformViewsChannel.java:122) E/flutter (26589): at io.flutter.embedding.engine.systemchannels.PlatformViewsChannel$1.onMethodCall(PlatformViewsChannel.java:60) E/flutter (26589): at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:262) E/flutter (26589): at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295) E/flutter (26589): at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:319) E/flutter (26589): at io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(Unknown Source:12) E/flutter (26589): at android.os.Handler.handleCallback(Handler.java:942) E/flutter (26589): at android.os.Handler.dispatchMessage(Handler.java:99) E/flutter (26589): at android.os.Looper.loopOnce(Looper.java:201) E/flutter (26589): at android.os.Looper.loop(Looper.java:288) E/flutter (26589): at android.app.ActivityThread.main(ActivityThread.java:7898) E/flutter (26589): at java.lang.reflect.Method.invoke(Native Method) E/flutter (26589): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) E/flutter (26589): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936) E/flutter (26589): , null, null)

daviddavis83 avatar Oct 15 '22 16:10 daviddavis83

+1 I'm facing this issue in production, I didn't upgraded nothing the app was already released. This is very scaring! The first time was 14/10/22. ISSUE:

Non-fatal Exception: io.flutter.plugins.firebase.crashlytics.FlutterError: LocalizedErrorMessage(code: FailureCode.Canceled, localizedMessage: null, message: null, stripeErrorCode: null, declineCode: null, type: null). Error thrown null.
       at ResultParser.parse(result_parser.dart:16)
       at MethodChannelStripe.confirmSetupIntent(method_channel_stripe.dart:116)
       at Stripe.confirmSetupIntent(stripe.dart:297)
       at PaymentSetupState._handlePayPress(payment_setup.dart:312)

@jamesblasco

UPDATE 16/10/2022 9:30 UTC+2 Today I tested and seems to work upgrading version from 3.3.X to ^5.1.0 , it's scary because I didn't change anything on my code and now works. Plus someone more skilled can explain me how it's possible?

kekko7072 avatar Oct 15 '22 16:10 kekko7072

I tried to reproduce it on latest master in the example app but failed to do so. I am able to have multiple successful payments with the cardfield. Seems like a race condition to me. Can you provide a reproduction scenario so we can triage this more closely?

remonh87 avatar Oct 16 '22 11:10 remonh87

I'm working with proprietary code so I can't post the exact example but here is something I put together. A user is navigated to the payment screen with the CardFieldForm and once payment is complete we push them to a new OrderDetailsScreen. I'm wondering if the push to the new screen isn't doing some kind of cleanup.

import 'package:dirt_slingin_tech/widgets/shopping_cart/order_details_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_stripe/flutter_stripe.dart' as stripe;

class FlutterStripeExampleScreen extends StatefulWidget {
  const FlutterStripeExampleScreen({Key? key}) : super(key: key);

  @override
  State<FlutterStripeExampleScreen> createState() =>
      _FlutterStripeExampleScreenState();
}

class _FlutterStripeExampleScreenState
    extends State<FlutterStripeExampleScreen> {
  stripe.CardFormEditController cardController =
      stripe.CardFormEditController();
  bool processingPayment = false;

  @override
  void initState() {
    super.initState();
    cardController.addListener(update);
  }

  void update() => setState(() {});

  @override
  void dispose() {
    cardController.removeListener(update);
    cardController.dispose();
    super.dispose();
  }

  Future<void> _handlePayPress() async {
    if (!cardController.details.complete) {
      return;
    }

    setState(() {
      processingPayment = true;
    });

    /// capture the navigator state so we can push later
    NavigatorState navigator = Navigator.of(context);

    // customer info
    const billingDetails = stripe.BillingDetails(email: '[email protected]');

    // create payment method
    dynamic paymentMethod = await stripe.Stripe.instance.createPaymentMethod(
      const stripe.PaymentMethodParams.card(
        paymentMethodData: stripe.PaymentMethodData(
          billingDetails: billingDetails,
        ),
      ),
    );

    // dummy value for payment intent
    dynamic paymentIntentResult = await Future<String>.value('payment intent');

    // dummy api request to confirm payment
    dynamic response = await Future<String>.value('success');

    navigator.pushNamed(OrderDetailsScreen.routeName);
  }

  @override
  Widget build(BuildContext context) {
    ThemeData theme = Theme.of(context);

    return Column(
      children: <Widget>[
        Padding(
          padding: const EdgeInsets.only(bottom: 30),
          child: stripe.CardFormField(
            controller: cardController,
          ),
        ),
        if (cardController.details.complete == true)
          processingPayment
              ? Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text(
                      'Processing',
                      style: theme.textTheme.bodyMedium,
                    ),
                    const SizedBox(
                      width: 10,
                    ),
                    const SizedBox(
                      width: 14,
                      height: 14,
                      child: CircularProgressIndicator(
                        strokeWidth: 2,
                      ),
                    )
                  ],
                )
              : ElevatedButton(
                  onPressed: () async {
                    _handlePayPress();
                  },
                  style: ElevatedButton.styleFrom(
                    backgroundColor: Colors.blue,
                    minimumSize: const Size(200, 50),
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(25),
                    ),
                  ),
                  child: const Text('Pay'),
                ),
      ],
    );
  }
}

daviddavis83 avatar Oct 16 '22 14:10 daviddavis83

Having same issue - I am intentionally loading CardField twice in in the same navigation stack (billing (first load) -> plan selection -> enter credit card (second load), and it crashes

Prayer-RecycleSmart avatar Oct 17 '22 01:10 Prayer-RecycleSmart

this is still an issue

Prayer-RecycleSmart avatar Nov 22 '22 07:11 Prayer-RecycleSmart

duplicate of #899

remonh87 avatar Oct 31 '23 15:10 remonh87