how to solve "The method 'call' was called on null. Receiver: null Tried calling: call()" error on flutter?
729
The issue is CreditCardWidget
has a required parameter onCreditCardWidgetChange
which expects a function, it's missing in your code. You should change your code to pass that:
CreditCardWidget(
cardNumber: card['cardNumber'],
expiryDate: card['expiryDate'],
cardHolderName: card['cardHolderName'],
cvvCode: card['cvvCode'],
showBackView: false,
onCreditCardWidgetChange: (brand) {
print(brand);
},
),
Author by
ryan chandra
Updated on January 02, 2023Comments
-
ryan chandra over 1 year
i am trying to integrate stripe payment method in flutter. i am following the tutorial from https://www.youtube.com/watch?v=VOtmM9i25R4.
i got error that says The method 'call' was called on null. Receiver: null Tried calling: call()
this is the code where the error is
import 'package:flutter/material.dart'; import 'package:flutter_credit_card/credit_card_widget.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:izzilly_customer/API_service/payment_service/stripe- payment-service.dart'; import 'package:stripe_payment/stripe_payment.dart'; class ExistingCardsPage extends StatefulWidget { static const String id = 'existing-card'; ExistingCardsPage({Key key}) : super(key: key); @override ExistingCardsPageState createState() => ExistingCardsPageState(); } class ExistingCardsPageState extends State<ExistingCardsPage> { List cards = [{ 'cardNumber': '4242424242424242', 'expiryDate': '04/24', 'cardHolderName': 'Muhammad Ahsan Ayaz', 'cvvCode': '424', 'showBackView': false, }, { 'cardNumber': '5555555566554444', 'expiryDate': '04/23', 'cardHolderName': 'Tracer', 'cvvCode': '123', 'showBackView': false, }]; payViaExistingCard(BuildContext context, card) 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: '2500', currency: 'USD', card: stripeCard ); await EasyLoading.dismiss(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(response.message), duration: new Duration(milliseconds: 1200), ) ).closed.then((_) { Navigator.pop(context); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Choose existing card'), ), body: Container( padding: EdgeInsets.all(20), child: ListView.builder( itemCount: cards.length, itemBuilder: (BuildContext context, int index) { var card = cards[index]; return InkWell( onTap: () { payViaExistingCard(context, card); }, child: CreditCardWidget( cardNumber: card['cardNumber'], expiryDate: card['expiryDate'], cardHolderName: card['cardHolderName'], cvvCode: card['cvvCode'], showBackView: false, ) ); } }, ), ), ); } }
and this is the error
this is the code for CreditCardWidget
const Map<CardType, String> CardTypeIconAsset = <CardType, String>{ CardType.visa: 'icons/visa.png', CardType.americanExpress: 'icons/amex.png', CardType.mastercard: 'icons/mastercard.png', CardType.discover: 'icons/discover.png', }; class CreditCardWidget extends StatefulWidget { const CreditCardWidget( {Key? key, required this.cardNumber, required this.expiryDate, required this.cardHolderName, required this.cvvCode, required this.showBackView, this.animationDuration = const Duration(milliseconds: 500), this.height, this.width, this.textStyle, this.cardBgColor = const Color(0xff1b447b), this.obscureCardNumber = true, this.obscureCardCvv = true, this.labelCardHolder = 'CARD HOLDER', this.labelExpiredDate = 'MM/YY', this.cardType, this.isHolderNameVisible = false, this.backgroundImage, this.glassmorphismConfig, this.isChipVisible = true, this.isSwipeGestureEnabled = true, this.customCardTypeIcons = const <CustomCardTypeIcon>[], required this.onCreditCardWidgetChange}) : super(key: key); final String cardNumber; final String expiryDate; final String cardHolderName; final String cvvCode; final TextStyle? textStyle; final Color cardBgColor; final bool showBackView; final Duration animationDuration; final double? height; final double? width; final bool obscureCardNumber; final bool obscureCardCvv; final void Function(CreditCardBrand) onCreditCardWidgetChange; final bool isHolderNameVisible; final String? backgroundImage; final bool isChipVisible; final Glassmorphism? glassmorphismConfig; final bool isSwipeGestureEnabled; final String labelCardHolder; final String labelExpiredDate; final CardType? cardType; final List<CustomCardTypeIcon> customCardTypeIcons; @override _CreditCardWidgetState createState() => _CreditCardWidgetState(); } class _CreditCardWidgetState extends State<CreditCardWidget> with SingleTickerProviderStateMixin { late AnimationController controller; late Animation<double> _frontRotation; late Animation<double> _backRotation; late Gradient backgroundGradientColor; late bool isFrontVisible = true; late bool isGestureUpdate = false; bool isAmex = false; @override void initState() { super.initState(); ///initialize the animation controller controller = AnimationController( duration: widget.animationDuration, vsync: this, ); _gradientSetup(); _updateRotations(false); } void _gradientSetup() { backgroundGradientColor = LinearGradient( // Where the linear gradient begins and ends begin: Alignment.topRight, end: Alignment.bottomLeft, // Add one stop for each color. Stops should increase from 0 to 1 stops: const <double>[0.1, 0.4, 0.7, 0.9], colors: <Color>[ widget.cardBgColor.withOpacity(1), widget.cardBgColor.withOpacity(0.97), widget.cardBgColor.withOpacity(0.90), widget.cardBgColor.withOpacity(0.86), ], ); } @override void dispose() { controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { /// /// If uer adds CVV then toggle the card from front to back.. /// controller forward starts animation and shows back layout. /// controller reverse starts animation and shows front layout. /// if (!isGestureUpdate) { _updateRotations(false); if (widget.showBackView) { controller.forward(); } else { controller.reverse(); } } else { isGestureUpdate = false; } final CardType? cardType = widget.cardType != null ? widget.cardType : detectCCType(widget.cardNumber); widget.onCreditCardWidgetChange(CreditCardBrand(cardType)); return Stack( children: <Widget>[ _cardGesture( child: AnimationCard( animation: _frontRotation, child: _buildFrontContainer(), ), ), _cardGesture( child: AnimationCard( animation: _backRotation, child: _buildBackContainer(), ), ), ], ); } void _leftRotation() { _toggleSide(false); } void _rightRotation() { _toggleSide(true); } void _toggleSide(bool isRightTap) { _updateRotations(!isRightTap); if (isFrontVisible) { controller.forward(); isFrontVisible = false; } else { controller.reverse(); isFrontVisible = true; } } void _updateRotations(bool isRightSwipe) { setState(() { final bool rotateToLeft = (isFrontVisible && !isRightSwipe) || !isFrontVisible && isRightSwipe; ///Initialize the Front to back rotation tween sequence. _frontRotation = TweenSequence<double>( <TweenSequenceItem<double>>[ TweenSequenceItem<double>( tween: Tween<double>( begin: 0.0, end: rotateToLeft ? (pi / 2) : (-pi / 2)) .chain(CurveTween(curve: Curves.linear)), weight: 50.0, ), TweenSequenceItem<double>( tween: ConstantTween<double>(rotateToLeft ? (-pi / 2) : (pi / 2)), weight: 50.0, ), ], ).animate(controller); ///Initialize the Back to Front rotation tween sequence. _backRotation = TweenSequence<double>( <TweenSequenceItem<double>>[ TweenSequenceItem<double>( tween: ConstantTween<double>(rotateToLeft ? (pi / 2) : (-pi / 2)), weight: 50.0, ), TweenSequenceItem<double>( tween: Tween<double>( begin: rotateToLeft ? (-pi / 2) : (pi / 2), end: 0.0) .chain( CurveTween(curve: Curves.linear), ), weight: 50.0, ), ], ).animate(controller); }); } /// /// Builds a front container containing /// Card number, Exp. year and Card holder name /// Widget _buildFrontContainer() { final TextStyle defaultTextStyle = Theme.of(context).textTheme.headline6!.merge( const TextStyle( color: Colors.white, fontFamily: 'halter', fontSize: 16, package: 'flutter_credit_card', ), ); final String number = widget.obscureCardNumber ? widget.cardNumber.replaceAll(RegExp(r'(?<=.{4})\d(?=.{4})'), '*') : widget.cardNumber; return CardBackground( backgroundImage: widget.backgroundImage, backgroundGradientColor: backgroundGradientColor, glassmorphismConfig: widget.glassmorphismConfig, height: widget.height, width: widget.width, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Expanded( flex: widget.isChipVisible ? 2 : 0, child: Row( crossAxisAlignment: CrossAxisAlignment.end, children: <Widget>[ if (widget.isChipVisible) Padding( padding: const EdgeInsets.only(left: 16), child: Image.asset( 'icons/chip.png', package: 'flutter_credit_card', scale: 1, ), ), const Spacer(), Align( alignment: Alignment.topRight, child: Padding( padding: const EdgeInsets.only(left: 16, right: 16, top: 8), child: widget.cardType != null ? getCardTypeImage(widget.cardType) : getCardTypeIcon(widget.cardNumber), ), ), ], ), ), const SizedBox( height: 10, ), Expanded( child: Padding( padding: const EdgeInsets.only(left: 16), child: Text( widget.cardNumber.isEmpty ? 'XXXX XXXX XXXX XXXX' : number, style: widget.textStyle ?? defaultTextStyle, ), ), ), Expanded( flex: 1, child: Padding( padding: const EdgeInsets.only(left: 16), child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text( 'VALID\nTHRU', style: widget.textStyle ?? defaultTextStyle.copyWith(fontSize: 7), textAlign: TextAlign.center, ), const SizedBox(width: 5), Text( widget.expiryDate.isEmpty ? widget.labelExpiredDate : widget.expiryDate, style: widget.textStyle ?? defaultTextStyle, ), ], ), ), ), Visibility( visible: widget.isHolderNameVisible, child: Expanded( child: Padding( padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), child: Text( widget.cardHolderName.isEmpty ? widget.labelCardHolder : widget.cardHolderName, maxLines: 1, overflow: TextOverflow.ellipsis, style: widget.textStyle ?? defaultTextStyle, ), ), ), ), ], ), ); } /// /// Builds a back container containing cvv /// Widget _buildBackContainer() { final TextStyle defaultTextStyle = Theme.of(context).textTheme.headline6!.merge( const TextStyle( color: Colors.black, fontFamily: 'halter', fontSize: 16, package: 'flutter_credit_card', ), ); final String cvv = widget.obscureCardCvv ? widget.cvvCode.replaceAll(RegExp(r'\d'), '*') : widget.cvvCode; return CardBackground( backgroundImage: widget.backgroundImage, backgroundGradientColor: backgroundGradientColor, glassmorphismConfig: widget.glassmorphismConfig, height: widget.height, width: widget.width, child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Expanded( flex: 2, child: Container( margin: const EdgeInsets.only(top: 16), height: 48, color: Colors.black, ), ), Expanded( flex: 2, child: Container( margin: const EdgeInsets.only(top: 16), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ Expanded( flex: 9, child: Container( height: 48, color: Colors.white70, ), ), Expanded( flex: 3, child: Container( color: Colors.white, child: Padding( padding: const EdgeInsets.all(5), child: Text( widget.cvvCode.isEmpty ? isAmex ? 'XXXX' : 'XXX' : cvv, maxLines: 1, style: widget.textStyle ?? defaultTextStyle, ), ), ), ) ], ), ), ), Expanded( flex: 2, child: Align( alignment: Alignment.bottomRight, child: Padding( padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), child: widget.cardType != null ? getCardTypeImage(widget.cardType) : getCardTypeIcon(widget.cardNumber), ), ), ), ], ), ); } Widget _cardGesture({required Widget child}) { bool isRightSwipe = true; return widget.isSwipeGestureEnabled ? GestureDetector( onPanEnd: (_) { isGestureUpdate = true; if (isRightSwipe) { _leftRotation(); } else { _rightRotation(); } }, onPanUpdate: (DragUpdateDetails details) { // Swiping in right direction. if (details.delta.dx > 0) { isRightSwipe = true; } // Swiping in left direction. if (details.delta.dx < 0) { isRightSwipe = false; } }, child: child, ) : child; } /// Credit Card prefix patterns as of March 2019 /// A [List<String>] represents a range. /// i.e. ['51', '55'] represents the range of cards starting with '51' to those starting with '55' Map<CardType, Set<List<String>>> cardNumPatterns = <CardType, Set<List<String>>>{ CardType.visa: <List<String>>{ <String>['4'], }, CardType.americanExpress: <List<String>>{ <String>['34'], <String>['37'], }, CardType.discover: <List<String>>{ <String>['6011'], <String>['622126', '622925'], <String>['644', '649'], <String>['65'] }, CardType.mastercard: <List<String>>{ <String>['51', '55'], <String>['2221', '2229'], <String>['223', '229'], <String>['23', '26'], <String>['270', '271'], <String>['2720'], }, }; /// This function determines the Credit Card type based on the cardPatterns /// and returns it. CardType detectCCType(String cardNumber) { //Default card type is other CardType cardType = CardType.otherBrand; if (cardNumber.isEmpty) { return cardType; } cardNumPatterns.forEach( (CardType type, Set<List<String>> patterns) { for (List<String> patternRange in patterns) { // Remove any spaces String ccPatternStr = cardNumber.replaceAll(RegExp(r'\s+\b|\b\s'), ''); final int rangeLen = patternRange[0].length; // Trim the Credit Card number string to match the pattern prefix length if (rangeLen < cardNumber.length) { ccPatternStr = ccPatternStr.substring(0, rangeLen); } if (patternRange.length > 1) { // Convert the prefix range into numbers then make sure the // Credit Card num is in the pattern range. // Because Strings don't have '>=' type operators final int ccPrefixAsInt = int.parse(ccPatternStr); final int startPatternPrefixAsInt = int.parse(patternRange[0]); final int endPatternPrefixAsInt = int.parse(patternRange[1]); if (ccPrefixAsInt >= startPatternPrefixAsInt && ccPrefixAsInt <= endPatternPrefixAsInt) { // Found a match cardType = type; break; } } else { // Just compare the single pattern prefix with the Credit Card prefix if (ccPatternStr == patternRange[0]) { // Found a match cardType = type; break; } } } }, ); return cardType; } Widget getCardTypeImage(CardType? cardType) { final List<CustomCardTypeIcon> customCardTypeIcon = getCustomCardTypeIcon(cardType!); if(customCardTypeIcon.isNotEmpty){ return customCardTypeIcon.first.cardImage; } else { return Image.asset( CardTypeIconAsset[cardType]!, height: 48, width: 48, package: 'flutter_credit_card', ); } } // This method returns the icon for the visa card type if found // else will return the empty container Widget getCardTypeIcon(String cardNumber) { Widget icon; final CardType ccType = detectCCType(cardNumber); final List<CustomCardTypeIcon> customCardTypeIcon = getCustomCardTypeIcon(ccType); if (customCardTypeIcon.isNotEmpty) { icon = customCardTypeIcon.first.cardImage; isAmex = ccType == CardType.americanExpress; } else { switch (ccType) { case CardType.visa: icon = Image.asset( CardTypeIconAsset[ccType]!, height: 48, width: 48, package: 'flutter_credit_card', ); isAmex = false; break; case CardType.americanExpress: icon = Image.asset( CardTypeIconAsset[ccType]!, height: 48, width: 48, package: 'flutter_credit_card', ); isAmex = true; break; case CardType.mastercard: icon = Image.asset( CardTypeIconAsset[ccType]!, height: 48, width: 48, package: 'flutter_credit_card', ); isAmex = false; break; case CardType.discover: icon = Image.asset( CardTypeIconAsset[ccType]!, height: 48, width: 48, package: 'flutter_credit_card', ); isAmex = false; break; default: icon = Container( height: 48, width: 48, ); isAmex = false; break; } } return icon; } List<CustomCardTypeIcon> getCustomCardTypeIcon(CardType currentCardType) => widget.customCardTypeIcons .where((CustomCardTypeIcon element) => element.cardType == currentCardType) .toList(); } class MaskedTextController extends TextEditingController { MaskedTextController( {String? text, required this.mask, Map<String, RegExp>? translator}) : super(text: text) { this.translator = translator ?? MaskedTextController.getDefaultTranslator(); addListener(() { final String previous = _lastUpdatedText; if (beforeChange(previous, this.text)) { updateText(this.text); afterChange(previous, this.text); } else { updateText(_lastUpdatedText); } }); updateText(this.text); } String mask; late Map<String, RegExp> translator; Function afterChange = (String previous, String next) {}; Function beforeChange = (String previous, String next) { return true; }; String _lastUpdatedText = ''; void updateText(String text) { if (text.isNotEmpty) { this.text = _applyMask(mask, text); } else { this.text = ''; } _lastUpdatedText = this.text; } void updateMask(String mask, {bool moveCursorToEnd = true}) { this.mask = mask; updateText(text); if (moveCursorToEnd) { this.moveCursorToEnd(); } } void moveCursorToEnd() { final String text = _lastUpdatedText; selection = TextSelection.fromPosition(TextPosition(offset: text.length)); } @override set text(String newText) { if (super.text != newText) { super.text = newText; moveCursorToEnd(); } } static Map<String, RegExp> getDefaultTranslator() { return <String, RegExp>{ 'A': RegExp(r'[A-Za-z]'), '0': RegExp(r'[0-9]'), '@': RegExp(r'[A-Za-z0-9]'), '*': RegExp(r'.*') }; } String _applyMask(String? mask, String value) { String result = ''; int maskCharIndex = 0; int valueCharIndex = 0; while (true) { // if mask is ended, break. if (maskCharIndex == mask!.length) { break; } // if value is ended, break. if (valueCharIndex == value.length) { break; } final String maskChar = mask[maskCharIndex]; final String valueChar = value[valueCharIndex]; // value equals mask, just set if (maskChar == valueChar) { result += maskChar; valueCharIndex += 1; maskCharIndex += 1; continue; } // apply translator if match if (translator.containsKey(maskChar)) { if (translator[maskChar]!.hasMatch(valueChar)) { result += valueChar; maskCharIndex += 1; } valueCharIndex += 1; continue; } // not masked value, fixed char on mask result += maskChar; maskCharIndex += 1; continue; } return result; } } enum CardType { otherBrand, mastercard, visa, americanExpress, discover, }
can someone help me to fix this??
thank you very much
-
Midhun MP over 2 yearsCould you please add code of your
CreditCardWidget
as well? -
ryan chandra over 2 yearshi, thank you for the response. I am using flutter credit card library that provide the CreditCardWidget widget. i am going to add the code in the question
-
Yeasin Sheikh over 2 yearsCan you try removing ` if(card!=null)` and
return null;
-
Midhun MP over 2 yearsSeems like the code you've posted and the code in screenshot is different. Some conditional check is missing. Could you paste the code which actually causes the error ?
-
ryan chandra over 2 yearsi have edit it the code so its my current code that get error message i mentioned in the question. thank you for the response
-
Midhun MP over 2 years@ryanchandra What is your SDK version and version of
flutter_credit_card
? I'm trying to reproduce it in my machine -
ryan chandra over 2 yearsi have minSdkVersion 21, targetSdkVersion 31, compileSdkVersion 31 and flutter_credit_card: ^3.0.1
-
-
ryan chandra over 2 yearsThis answer solved my issue. Thank you very much for your help
-
Midhun MP over 2 yearsYou are welcome. Happy Coding!!!