4

I am trying to integrate stripe payment in flutter mobile app. After finishing the payment, I want the app to execute Navigator.pop(context). However, I get error message that says

Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe. E/flutter ( 2773): At this point the state of the widget's element tree is no longer stable.

This is my code for stripe payment

class ExistingCardsPage extends StatefulWidget {
 static const String id = 'exsiting-card';
 ExistingCardsPage({Key key}) : super(key: key);

 @override
 ExistingCardsPageState createState() => ExistingCardsPageState();
}

class ExistingCardsPageState extends State<ExistingCardsPage> {
 List cards = [];
 StripeService _service = StripeService();

 Future<StripeTransactionResponse> payViaExistingCard(
     BuildContext context, card, amount) async {
    await EasyLoading.show(status: "Please wait....");
    var expiryArr = card['expiryDate'].split('/');
    CreditCard stripeCard = CreditCard(
     number: card['cardNumber'],
     expMonth: int.parse(expiryArr[0]),
     expYear: int.parse(expiryArr[1]),
   );
   var response = await StripeService.payViaExistingCard(
      amount: '${amount}00', currency: 'USD', card: stripeCard);
    await EasyLoading.dismiss();
    ScaffoldMessenger.of(context).showSnackBar(
       SnackBar(
         content: Text(response.message),
         duration: new Duration(milliseconds: response.success == true ? 1200 : 3000),
       )
   ).closed.then((_) {
     Navigator.pop(context); /////THIS IS THE ERROR/////

   });

   return response;
}

 @override
 Widget build(BuildContext context) {
   final orderProvider = Provider.of<OrderProvider>(context);
    return Scaffold(
     appBar: AppBar(
    iconTheme: IconThemeData(color: Colors.white),
    title: Text('Choose existing card', style: TextStyle(color: Colors.white),),
  ),
  body: FutureBuilder<QuerySnapshot>(
    future: _service.cards.get(),
    builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
      if (snapshot.hasError) {
        return Text('Something went wrong');
      }

      if (snapshot.connectionState == ConnectionState.waiting) {
        return Center(child: CircularProgressIndicator());
      }

      if(snapshot.data.size==0){
        return Center(
          child: Text('No Credit Card in your account'));
      }

      return Container(
        padding: EdgeInsets.only(left: 8, top: 10, right: 8, bottom: 10),
        child: ListView.builder(
          itemCount: snapshot.data.docs.length,
          itemBuilder: (BuildContext context, int index) {
            var card = snapshot.data.docs[index];
            return InkWell(
              onTap: () {
                payViaExistingCard(context, card, orderProvider.amount).then((response) {
                  if(response.success==true){
                    orderProvider.paymentStatus(response.success);
                  }
                });
              },
              child: CreditCardWidget(
                cardNumber: card['cardNumber'],
                expiryDate: card['expiryDate'],
                cardHolderName: card['cardHolderName'],
                cvvCode: card['cvvCode'],
                showBackView: false,
                onCreditCardWidgetChange: (brand) {
                  print(brand);
                },
              ),
            );
          },
        ),
      );
    },
  )
);
}
}

Does anyone know what is wrong with the code?

Kins
  • 547
  • 1
  • 5
  • 22
ryan chandra
  • 321
  • 3
  • 11

2 Answers2

6

Use global context for SnackBar

// declare as global
GlobalKey<ScaffoldMessengerState> scaffoldMessengerKey=GlobalKey<ScaffoldMessengerState>();

Simply add this in main.dart file in

   @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'App Name',
      scaffoldMessengerKey: scaffoldMessengerKey, // add this
    );
  }

Then we can use this anywhere in-app without passing each page context.

scaffoldMessengerKey.currentState.showSnackBar(SnackBar(
        content: Text(
          "Your Message",
        ),
      ));
Bholendra Singh
  • 980
  • 7
  • 14
0

I faced it when applying Getx Binding. Finally solved it on below way

passing context

endDrawer: NavMenuDrawer(mainContext: context),

hold context

class NavMenuDrawer extends StatefulWidget {

  var mainContext;
  NavMenuDrawer({this.mainContext});
  
....

finally used here

Navigator.of(widget.mainContext).pushNamedAndRemoveUntil('/', (Route<dynamic> route) => false);

Reason : It is happend due to context mismatch issue. U passed context for Navigator.pop(context) that not matching with your MainApp BuildContext.

Solution : There has 2 way

1. U can use Global key
2. pass actual context to your next page

I am showing You 2nd way, which is relevant to your problem, And hope will solve it.

pass context to your ExistingCardsPage. where u can use constructor to hold variable like

class ExistingCardsPage extends StatefulWidget {
    var mainContext;
    ExistingCardsPage({this.mainContext});

.....

then use it

Navigator.pop(widget.mainContext).
Mimu Saha Tishan
  • 2,402
  • 1
  • 21
  • 40