dart formatting

This commit is contained in:
Nathan Anderson 2023-12-07 15:38:08 -07:00
parent abcbf96d08
commit 713f35c692
2 changed files with 264 additions and 111 deletions

View File

@ -20,7 +20,11 @@ class CardDetails {
/// Sets every field to null, a default /// Sets every field to null, a default
/// `CardDetails` when nothing has been entered. /// `CardDetails` when nothing has been entered.
factory CardDetails.blank() { factory CardDetails.blank() {
return CardDetails(cardNumber: null, securityCode: null, expirationString: null, postalCode: null); return CardDetails(
cardNumber: null,
securityCode: null,
expirationString: null,
postalCode: null);
} }
/// Returns the CardNumber as a `String` with the spaces removed. /// Returns the CardNumber as a `String` with the spaces removed.
@ -53,8 +57,9 @@ class CardDetails {
/// Returns true if `_cardNumber` is null, or /// Returns true if `_cardNumber` is null, or
/// if the _cardNumber matches the detected `provider`'s /// if the _cardNumber matches the detected `provider`'s
/// card lenght, defaulting to 16. /// card lenght, defaulting to 16.
bool get cardNumberFilled => bool get cardNumberFilled => _cardNumber == null
_cardNumber == null ? false : (provider?.cardLength ?? 16) == _cardNumber!.replaceAll(' ', '').length; ? false
: (provider?.cardLength ?? 16) == _cardNumber!.replaceAll(' ', '').length;
/// Returns true if all details are complete and valid /// Returns true if all details are complete and valid
/// otherwise, return false. /// otherwise, return false.
@ -89,7 +94,10 @@ class CardDetails {
_complete = false; _complete = false;
_lastCheckHash = currentHash; _lastCheckHash = currentHash;
if (_cardNumber == null && expirationString == null && securityCode == null && postalCode == null) { if (_cardNumber == null &&
expirationString == null &&
securityCode == null &&
postalCode == null) {
_validState = CardDetailsValidState.blank; _validState = CardDetailsValidState.blank;
return; return;
} }
@ -117,7 +125,8 @@ class CardDetails {
_validState = CardDetailsValidState.missingDate; _validState = CardDetailsValidState.missingDate;
return; return;
} }
final month = int.parse(expSplits.first[0] == '0' ? expSplits.first[1] : expSplits.first); final month = int.parse(
expSplits.first[0] == '0' ? expSplits.first[1] : expSplits.first);
if (month < 1 || month > 12) { if (month < 1 || month > 12) {
_validState = CardDetailsValidState.invalidMonth; _validState = CardDetailsValidState.invalidMonth;
return; return;
@ -127,7 +136,8 @@ class CardDetails {
if (date.isBefore(DateTime.now())) { if (date.isBefore(DateTime.now())) {
_validState = CardDetailsValidState.dateTooEarly; _validState = CardDetailsValidState.dateTooEarly;
return; return;
} else if (date.isAfter(DateTime.now().add(const Duration(days: 365 * 50)))) { } else if (date
.isAfter(DateTime.now().add(const Duration(days: 365 * 50)))) {
_validState = CardDetailsValidState.dateTooLate; _validState = CardDetailsValidState.dateTooLate;
return; return;
} }
@ -281,7 +291,11 @@ class CardProvider {
int cvcLength; int cvcLength;
CardProvider( CardProvider(
{required this.id, required this.cardLength, required this.cvcLength, this.innValidNums, this.innValidRanges}) { {required this.id,
required this.cardLength,
required this.cvcLength,
this.innValidNums,
this.innValidRanges}) {
// Must provide one or the other // Must provide one or the other
assert(innValidNums != null || innValidRanges != null); assert(innValidNums != null || innValidRanges != null);
// Do not provide empty list of valid nums // Do not provide empty list of valid nums

View File

@ -75,7 +75,8 @@ class CardTextField extends StatefulWidget {
const msg = 'Invalid stripe key, doesn\'t start with "pk_"'; const msg = 'Invalid stripe key, doesn\'t start with "pk_"';
if (kDebugMode) assert(false, msg); if (kDebugMode) assert(false, msg);
if (kReleaseMode || kProfileMode) { if (kReleaseMode || kProfileMode) {
throw CardTextFieldError(CardTextFieldErrorType.stripeImplementation, details: msg); throw CardTextFieldError(CardTextFieldErrorType.stripeImplementation,
details: msg);
} }
} }
} }
@ -245,9 +246,12 @@ class CardTextFieldState extends State<CardTextField> {
// No way to get backspace events on soft keyboards, so add invisible character to detect delete // No way to get backspace events on soft keyboards, so add invisible character to detect delete
_cardNumberController = TextEditingController(); _cardNumberController = TextEditingController();
_expirationController = TextEditingController(text: _isMobile ? '\u200b' : ''); _expirationController =
_securityCodeController = TextEditingController(text: _isMobile ? '\u200b' : ''); TextEditingController(text: _isMobile ? '\u200b' : '');
_postalCodeController = TextEditingController(text: _isMobile ? '\u200b' : ''); _securityCodeController =
TextEditingController(text: _isMobile ? '\u200b' : '');
_postalCodeController =
TextEditingController(text: _isMobile ? '\u200b' : '');
_controllers.addAll([ _controllers.addAll([
_cardNumberController, _cardNumberController,
@ -273,7 +277,9 @@ class CardTextFieldState extends State<CardTextField> {
// Add listeners to know when card details are completed // Add listeners to know when card details are completed
_cardDetails.onCompleteController.stream.listen((card) async { _cardDetails.onCompleteController.stream.listen((card) async {
if (widget.stripePublishableKey != null && widget.onStripeResponse != null && widget.autoFetchStripektoken) { if (widget.stripePublishableKey != null &&
widget.onStripeResponse != null &&
widget.autoFetchStripektoken) {
final res = await getStripeResponse(); final res = await getStripeResponse();
widget.onStripeResponse!(res); widget.onStripeResponse!(res);
} }
@ -322,9 +328,11 @@ class CardTextFieldState extends State<CardTextField> {
// Enable scrolling on mobile and if its narrow (not all fields visible) // Enable scrolling on mobile and if its narrow (not all fields visible)
onHorizontalDragUpdate: (details) { onHorizontalDragUpdate: (details) {
const minOffset = 0.0; const minOffset = 0.0;
final maxOffset = _horizontalScrollController.position.maxScrollExtent; final maxOffset =
_horizontalScrollController.position.maxScrollExtent;
if (!_isMobile || isWideFormat) return; if (!_isMobile || isWideFormat) return;
final newOffset = _horizontalScrollController.offset - details.delta.dx; final newOffset =
_horizontalScrollController.offset - details.delta.dx;
if (newOffset < minOffset) { if (newOffset < minOffset) {
_horizontalScrollController.jumpTo(minOffset); _horizontalScrollController.jumpTo(minOffset);
@ -335,7 +343,9 @@ class CardTextFieldState extends State<CardTextField> {
} }
}, },
onHorizontalDragEnd: (details) { onHorizontalDragEnd: (details) {
if (!_isMobile || isWideFormat || details.primaryVelocity == null) { if (!_isMobile ||
isWideFormat ||
details.primaryVelocity == null) {
return; return;
} }
@ -343,13 +353,16 @@ class CardTextFieldState extends State<CardTextField> {
const cur = Curves.ease; const cur = Curves.ease;
// final max = _horizontalScrollController.position.maxScrollExtent; // final max = _horizontalScrollController.position.maxScrollExtent;
final newOffset = _horizontalScrollController.offset - details.primaryVelocity! * 0.15; final newOffset = _horizontalScrollController.offset -
_horizontalScrollController.animateTo(newOffset, curve: cur, duration: dur); details.primaryVelocity! * 0.15;
_horizontalScrollController.animateTo(newOffset,
curve: cur, duration: dur);
}, },
child: Container( child: Container(
width: widget.width, width: widget.width,
height: widget.height ?? 60.0, height: widget.height ?? 60.0,
decoration: _showBorderError ? _errorBoxDecoration : _normalBoxDecoration, decoration:
_showBorderError ? _errorBoxDecoration : _normalBoxDecoration,
child: ClipRect( child: ClipRect(
child: IgnorePointer( child: IgnorePointer(
child: SingleChildScrollView( child: SingleChildScrollView(
@ -360,23 +373,31 @@ class CardTextFieldState extends State<CardTextField> {
height: widget.height ?? 60.0, height: widget.height ?? 60.0,
child: Column( child: Column(
children: [ children: [
if (widget.loadingWidgetLocation == LoadingLocation.above) if (widget.loadingWidgetLocation ==
LoadingLocation.above)
AnimatedOpacity( AnimatedOpacity(
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
opacity: _loading && widget.showInternalLoadingWidget ? 1.0 : 0.0, opacity:
child: widget.loadingWidget ?? const LinearProgressIndicator(), _loading && widget.showInternalLoadingWidget
? 1.0
: 0.0,
child: widget.loadingWidget ??
const LinearProgressIndicator(),
), ),
Padding( Padding(
padding: switch (widget.loadingWidgetLocation) { padding: switch (widget.loadingWidgetLocation) {
LoadingLocation.above => const EdgeInsets.only(top: 0, bottom: 4.0), LoadingLocation.above =>
LoadingLocation.below => const EdgeInsets.only(top: 4.0, bottom: 0), const EdgeInsets.only(top: 0, bottom: 4.0),
LoadingLocation.below =>
const EdgeInsets.only(top: 4.0, bottom: 0),
}, },
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 6.0), padding: const EdgeInsets.symmetric(
horizontal: 6.0),
child: CardProviderIcon( child: CardProviderIcon(
cardDetails: _cardDetails, cardDetails: _cardDetails,
size: widget.iconSize, size: widget.iconSize,
@ -404,27 +425,37 @@ class CardTextFieldState extends State<CardTextField> {
} }
// setState(() => _cardDetails.cardNumber = content); // setState(() => _cardDetails.cardNumber = content);
if (_cardDetails.validState == CardDetailsValidState.invalidCard) { if (_cardDetails.validState ==
_setValidationState('Your card number is invalid.'); CardDetailsValidState.invalidCard) {
} else if (_cardDetails.validState == CardDetailsValidState.missingCard) { _setValidationState(
_setValidationState('Your card number is incomplete.'); 'Your card number is invalid.');
} else if (_cardDetails.validState ==
CardDetailsValidState.missingCard) {
_setValidationState(
'Your card number is incomplete.');
} }
return null; return null;
}, },
onChanged: (str) { onChanged: (str) {
_onTextFieldChanged(str, CardEntryStep.number); _onTextFieldChanged(
str, CardEntryStep.number);
final numbers = str.replaceAll(' ', ''); final numbers = str.replaceAll(' ', '');
if (str.length <= _cardDetails.maxINNLength) { if (str.length <=
_cardDetails.maxINNLength) {
_cardDetails.detectCardProvider(); _cardDetails.detectCardProvider();
} }
if (numbers.length == 16) { if (numbers.length == 16) {
_currentCardEntryStepController.add(CardEntryStep.exp); _currentCardEntryStepController
.add(CardEntryStep.exp);
} }
}, },
onFieldSubmitted: (_) => _currentCardEntryStepController.add(CardEntryStep.exp), onFieldSubmitted: (_) =>
_currentCardEntryStepController
.add(CardEntryStep.exp),
inputFormatters: [ inputFormatters: [
LengthLimitingTextInputFormatter(19), LengthLimitingTextInputFormatter(19),
FilteringTextInputFormatter.allow(RegExp('[0-9 ]')), FilteringTextInputFormatter.allow(
RegExp('[0-9 ]')),
CardNumberInputFormatter(), CardNumberInputFormatter(),
], ],
cursorColor: _cursorColor, cursorColor: _cursorColor,
@ -443,13 +474,16 @@ class CardTextFieldState extends State<CardTextField> {
// fit: _currentStep == CardEntryStep.number ? FlexFit.loose : FlexFit.tight, // fit: _currentStep == CardEntryStep.number ? FlexFit.loose : FlexFit.tight,
child: AnimatedContainer( child: AnimatedContainer(
curve: Curves.easeInOut, curve: Curves.easeInOut,
duration: const Duration(milliseconds: 400), duration:
constraints: _currentStep == CardEntryStep.number const Duration(milliseconds: 400),
constraints: _currentStep ==
CardEntryStep.number
? BoxConstraints.loose( ? BoxConstraints.loose(
Size(_expanderWidthExpanded, 0.0), Size(_expanderWidthExpanded, 0.0),
) )
: BoxConstraints.tight( : BoxConstraints.tight(
Size(_expanderWidthCollapsed, 0.0), Size(
_expanderWidthCollapsed, 0.0),
), ),
), ),
), ),
@ -461,7 +495,9 @@ class CardTextFieldState extends State<CardTextField> {
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
children: [ children: [
// Must manually add hint label because they wont show on mobile with backspace hack // Must manually add hint label because they wont show on mobile with backspace hack
if (_isMobile && _expirationController.text == '\u200b') if (_isMobile &&
_expirationController.text ==
'\u200b')
Text('MM/YYY', style: _hintTextSyle), Text('MM/YYY', style: _hintTextSyle),
TextFormField( TextFormField(
key: const Key('expiration_field'), key: const Key('expiration_field'),
@ -477,7 +513,10 @@ class CardTextFieldState extends State<CardTextField> {
? _errorTextStyle ? _errorTextStyle
: _normalTextStyle, : _normalTextStyle,
validator: (content) { validator: (content) {
if (content == null || content.isEmpty || _isMobile && content == '\u200b') { if (content == null ||
content.isEmpty ||
_isMobile &&
content == '\u200b') {
return null; return null;
} }
@ -488,27 +527,44 @@ class CardTextFieldState extends State<CardTextField> {
// setState(() => _cardDetails.expirationString = content); // setState(() => _cardDetails.expirationString = content);
// } // }
if (_cardDetails.validState == CardDetailsValidState.dateTooEarly) { if (_cardDetails.validState ==
_setValidationState('Your card\'s expiration date is in the past.'); CardDetailsValidState
} else if (_cardDetails.validState == CardDetailsValidState.dateTooLate) { .dateTooEarly) {
_setValidationState('Your card\'s expiration year is invalid.'); _setValidationState(
} else if (_cardDetails.validState == CardDetailsValidState.missingDate) { 'Your card\'s expiration date is in the past.');
_setValidationState('You must include your card\'s expiration date.'); } else if (_cardDetails.validState ==
} else if (_cardDetails.validState == CardDetailsValidState.invalidMonth) { CardDetailsValidState
_setValidationState('Your card\'s expiration month is invalid.'); .dateTooLate) {
_setValidationState(
'Your card\'s expiration year is invalid.');
} else if (_cardDetails.validState ==
CardDetailsValidState
.missingDate) {
_setValidationState(
'You must include your card\'s expiration date.');
} else if (_cardDetails.validState ==
CardDetailsValidState
.invalidMonth) {
_setValidationState(
'Your card\'s expiration month is invalid.');
} }
return null; return null;
}, },
onChanged: (str) { onChanged: (str) {
_onTextFieldChanged(str, CardEntryStep.exp); _onTextFieldChanged(
str, CardEntryStep.exp);
if (str.length == 5) { if (str.length == 5) {
_currentCardEntryStepController.add(CardEntryStep.cvc); _currentCardEntryStepController
.add(CardEntryStep.cvc);
} }
}, },
onFieldSubmitted: (_) => _currentCardEntryStepController.add(CardEntryStep.cvc), onFieldSubmitted: (_) =>
_currentCardEntryStepController
.add(CardEntryStep.cvc),
inputFormatters: [ inputFormatters: [
LengthLimitingTextInputFormatter(5), LengthLimitingTextInputFormatter(5),
FilteringTextInputFormatter.allow(RegExp('[0-9/]')), FilteringTextInputFormatter.allow(
RegExp('[0-9/]')),
CardExpirationFormatter(), CardExpirationFormatter(),
], ],
cursorColor: _cursorColor, cursorColor: _cursorColor,
@ -528,7 +584,9 @@ class CardTextFieldState extends State<CardTextField> {
child: Stack( child: Stack(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
children: [ children: [
if (_isMobile && _securityCodeController.text == '\u200b') if (_isMobile &&
_securityCodeController.text ==
'\u200b')
Text( Text(
'CVC', 'CVC',
style: _hintTextSyle, style: _hintTextSyle,
@ -538,12 +596,17 @@ class CardTextFieldState extends State<CardTextField> {
focusNode: securityCodeFocusNode, focusNode: securityCodeFocusNode,
controller: _securityCodeController, controller: _securityCodeController,
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
style: _isRedText( style: _isRedText([
[CardDetailsValidState.invalidCVC, CardDetailsValidState.missingCVC]) CardDetailsValidState.invalidCVC,
CardDetailsValidState.missingCVC
])
? _errorTextStyle ? _errorTextStyle
: _normalTextStyle, : _normalTextStyle,
validator: (content) { validator: (content) {
if (content == null || content.isEmpty || _isMobile && content == '\u200b') { if (content == null ||
content.isEmpty ||
_isMobile &&
content == '\u200b') {
return null; return null;
} }
@ -554,26 +617,41 @@ class CardTextFieldState extends State<CardTextField> {
// setState(() => _cardDetails.securityCode = content); // setState(() => _cardDetails.securityCode = content);
// } // }
if (_cardDetails.validState == CardDetailsValidState.invalidCVC) { if (_cardDetails.validState ==
_setValidationState('Your card\'s security code is invalid.'); CardDetailsValidState
} else if (_cardDetails.validState == CardDetailsValidState.missingCVC) { .invalidCVC) {
_setValidationState('Your card\'s security code is incomplete.'); _setValidationState(
'Your card\'s security code is invalid.');
} else if (_cardDetails.validState ==
CardDetailsValidState
.missingCVC) {
_setValidationState(
'Your card\'s security code is incomplete.');
} }
return null; return null;
}, },
onFieldSubmitted: (_) => onFieldSubmitted: (_) =>
_currentCardEntryStepController.add(CardEntryStep.postal), _currentCardEntryStepController
.add(CardEntryStep.postal),
onChanged: (str) { onChanged: (str) {
_onTextFieldChanged(str, CardEntryStep.cvc); _onTextFieldChanged(
str, CardEntryStep.cvc);
if (str.length == _cardDetails.provider?.cvcLength) { if (str.length ==
_currentCardEntryStepController.add(CardEntryStep.postal); _cardDetails
.provider?.cvcLength) {
_currentCardEntryStepController
.add(CardEntryStep.postal);
} }
}, },
inputFormatters: [ inputFormatters: [
LengthLimitingTextInputFormatter( LengthLimitingTextInputFormatter(
_cardDetails.provider == null ? 4 : _cardDetails.provider!.cvcLength), _cardDetails.provider == null
FilteringTextInputFormatter.allow(RegExp('[0-9]')), ? 4
: _cardDetails
.provider!.cvcLength),
FilteringTextInputFormatter.allow(
RegExp('[0-9]')),
], ],
cursorColor: _cursorColor, cursorColor: _cursorColor,
decoration: InputDecoration( decoration: InputDecoration(
@ -592,7 +670,9 @@ class CardTextFieldState extends State<CardTextField> {
child: Stack( child: Stack(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
children: [ children: [
if (_isMobile && _postalCodeController.text == '\u200b') if (_isMobile &&
_postalCodeController.text ==
'\u200b')
Text( Text(
'Postal Code', 'Postal Code',
style: _hintTextSyle, style: _hintTextSyle,
@ -602,12 +682,17 @@ class CardTextFieldState extends State<CardTextField> {
focusNode: postalCodeFocusNode, focusNode: postalCodeFocusNode,
controller: _postalCodeController, controller: _postalCodeController,
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
style: _isRedText( style: _isRedText([
[CardDetailsValidState.invalidZip, CardDetailsValidState.missingZip]) CardDetailsValidState.invalidZip,
CardDetailsValidState.missingZip
])
? _errorTextStyle ? _errorTextStyle
: _normalTextStyle, : _normalTextStyle,
validator: (content) { validator: (content) {
if (content == null || content.isEmpty || _isMobile && content == '\u200b') { if (content == null ||
content.isEmpty ||
_isMobile &&
content == '\u200b') {
return null; return null;
} }
@ -617,15 +702,22 @@ class CardTextFieldState extends State<CardTextField> {
// setState(() => _cardDetails.postalCode = content); // setState(() => _cardDetails.postalCode = content);
// } // }
if (_cardDetails.validState == CardDetailsValidState.invalidZip) { if (_cardDetails.validState ==
_setValidationState('The postal code you entered is not correct.'); CardDetailsValidState
} else if (_cardDetails.validState == CardDetailsValidState.missingZip) { .invalidZip) {
_setValidationState('You must enter your card\'s postal code.'); _setValidationState(
'The postal code you entered is not correct.');
} else if (_cardDetails.validState ==
CardDetailsValidState
.missingZip) {
_setValidationState(
'You must enter your card\'s postal code.');
} }
return null; return null;
}, },
onChanged: (str) { onChanged: (str) {
_onTextFieldChanged(str, CardEntryStep.postal); _onTextFieldChanged(
str, CardEntryStep.postal);
}, },
textInputAction: TextInputAction.done, textInputAction: TextInputAction.done,
onFieldSubmitted: (_) { onFieldSubmitted: (_) {
@ -634,7 +726,8 @@ class CardTextFieldState extends State<CardTextField> {
cursorColor: _cursorColor, cursorColor: _cursorColor,
decoration: InputDecoration( decoration: InputDecoration(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
hintText: _isMobile ? '' : 'Postal Code', hintText:
_isMobile ? '' : 'Postal Code',
hintStyle: _hintTextSyle, hintStyle: _hintTextSyle,
fillColor: Colors.transparent, fillColor: Colors.transparent,
border: InputBorder.none, border: InputBorder.none,
@ -646,11 +739,16 @@ class CardTextFieldState extends State<CardTextField> {
], ],
), ),
), ),
if (widget.loadingWidgetLocation == LoadingLocation.below) if (widget.loadingWidgetLocation ==
LoadingLocation.below)
AnimatedOpacity( AnimatedOpacity(
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
opacity: _loading && widget.showInternalLoadingWidget ? 1.0 : 0.0, opacity:
child: widget.loadingWidget ?? const LinearProgressIndicator(), _loading && widget.showInternalLoadingWidget
? 1.0
: 0.0,
child: widget.loadingWidget ??
const LinearProgressIndicator(),
), ),
], ],
), ),
@ -687,7 +785,8 @@ class CardTextFieldState extends State<CardTextField> {
switch (step) { switch (step) {
case CardEntryStep.number: case CardEntryStep.number:
setState(() => _cardDetails.cardNumber = cleanedStr.replaceAll(' ', '')); setState(
() => _cardDetails.cardNumber = cleanedStr.replaceAll(' ', ''));
break; break;
case CardEntryStep.exp: case CardEntryStep.exp:
setState(() => _cardDetails.expirationString = cleanedStr); setState(() => _cardDetails.expirationString = cleanedStr);
@ -715,15 +814,31 @@ class CardTextFieldState extends State<CardTextField> {
_expirationFieldWidth = widget.expFieldWidth ?? 70.0; _expirationFieldWidth = widget.expFieldWidth ?? 70.0;
_securityFieldWidth = widget.securityFieldWidth ?? 40.0; _securityFieldWidth = widget.securityFieldWidth ?? 40.0;
_postalFieldWidth = widget.postalFieldWidth ?? 95.0; _postalFieldWidth = widget.postalFieldWidth ?? 95.0;
isWideFormat = isWideFormat = widget.width >=
widget.width >= _cardFieldWidth + _expirationFieldWidth + _securityFieldWidth + _postalFieldWidth + 60.0; _cardFieldWidth +
_expirationFieldWidth +
_securityFieldWidth +
_postalFieldWidth +
60.0;
if (isWideFormat) { if (isWideFormat) {
_internalFieldWidth = widget.width + _postalFieldWidth + 35; _internalFieldWidth = widget.width + _postalFieldWidth + 35;
_expanderWidthExpanded = widget.width - _cardFieldWidth - _expirationFieldWidth - _securityFieldWidth - 35; _expanderWidthExpanded = widget.width -
_expanderWidthCollapsed = _cardFieldWidth -
widget.width - _cardFieldWidth - _expirationFieldWidth - _securityFieldWidth - _postalFieldWidth - 70; _expirationFieldWidth -
_securityFieldWidth -
35;
_expanderWidthCollapsed = widget.width -
_cardFieldWidth -
_expirationFieldWidth -
_securityFieldWidth -
_postalFieldWidth -
70;
} else { } else {
_internalFieldWidth = _cardFieldWidth + _expirationFieldWidth + _securityFieldWidth + _postalFieldWidth + 80; _internalFieldWidth = _cardFieldWidth +
_expirationFieldWidth +
_securityFieldWidth +
_postalFieldWidth +
80;
} }
_isMobile = kIsWeb ? !isWideFormat : Platform.isAndroid || Platform.isIOS; _isMobile = kIsWeb ? !isWideFormat : Platform.isAndroid || Platform.isIOS;
@ -738,11 +853,15 @@ class CardTextFieldState extends State<CardTextField> {
/// Called every `build()` invocation, combines passed in styles with the defaults /// Called every `build()` invocation, combines passed in styles with the defaults
void _initStyles() { void _initStyles() {
_errorTextStyle = const TextStyle(color: Colors.red, fontSize: 14, inherit: true) _errorTextStyle =
.merge(widget.errorTextStyle ?? widget.textStyle); const TextStyle(color: Colors.red, fontSize: 14, inherit: true)
_normalTextStyle = const TextStyle(color: Colors.black87, fontSize: 14, inherit: true).merge(widget.textStyle); .merge(widget.errorTextStyle ?? widget.textStyle);
_hintTextSyle = const TextStyle(color: Colors.black54, fontSize: 14, inherit: true) _normalTextStyle =
.merge(widget.hintTextStyle ?? widget.textStyle); const TextStyle(color: Colors.black87, fontSize: 14, inherit: true)
.merge(widget.textStyle);
_hintTextSyle =
const TextStyle(color: Colors.black54, fontSize: 14, inherit: true)
.merge(widget.hintTextStyle ?? widget.textStyle);
_normalBoxDecoration = BoxDecoration( _normalBoxDecoration = BoxDecoration(
color: const Color(0xfff6f9fc), color: const Color(0xfff6f9fc),
@ -784,8 +903,10 @@ class CardTextFieldState extends State<CardTextField> {
void _checkErrorOverride() { void _checkErrorOverride() {
if ((widget.errorText != null || widget.overrideValidState != null) && if ((widget.errorText != null || widget.overrideValidState != null) &&
Object.hashAll([widget.errorText, widget.overrideValidState]) != _prevErrorOverrideHash) { Object.hashAll([widget.errorText, widget.overrideValidState]) !=
_prevErrorOverrideHash = Object.hashAll([widget.errorText, widget.overrideValidState]); _prevErrorOverrideHash) {
_prevErrorOverrideHash =
Object.hashAll([widget.errorText, widget.overrideValidState]);
_validateFields(); _validateFields();
} }
} }
@ -793,7 +914,9 @@ class CardTextFieldState extends State<CardTextField> {
// Makes an http call to stripe API with provided card credentials and returns the result // Makes an http call to stripe API with provided card credentials and returns the result
Future<Map<String, dynamic>?> getStripeResponse() async { Future<Map<String, dynamic>?> getStripeResponse() async {
if (widget.stripePublishableKey == null) { if (widget.stripePublishableKey == null) {
if (kDebugMode) print('***ERROR tried calling `getStripeResponse()` but no stripe key provided'); if (kDebugMode)
print(
'***ERROR tried calling `getStripeResponse()` but no stripe key provided');
return null; return null;
} }
@ -801,7 +924,8 @@ class CardTextFieldState extends State<CardTextField> {
if (!_cardDetails.isComplete) { if (!_cardDetails.isComplete) {
if (kDebugMode) { if (kDebugMode) {
print('***ERROR Could not get stripe response, card details not complete: ${_cardDetails.validState}'); print(
'***ERROR Could not get stripe response, card details not complete: ${_cardDetails.validState}');
} }
return null; return null;
} }
@ -842,7 +966,8 @@ class CardTextFieldState extends State<CardTextField> {
if (_cardDetails.isComplete) { if (_cardDetails.isComplete) {
if (widget.onValidCardDetails != null) { if (widget.onValidCardDetails != null) {
widget.onValidCardDetails!(_cardDetails); widget.onValidCardDetails!(_cardDetails);
} else if (widget.onStripeResponse != null && !widget.autoFetchStripektoken) { } else if (widget.onStripeResponse != null &&
!widget.autoFetchStripektoken) {
// Callback that stripe call is being made // Callback that stripe call is being made
if (widget.onCallToStripe != null) widget.onCallToStripe!(); if (widget.onCallToStripe != null) widget.onCallToStripe!();
final jsonBody = await getStripeResponse(); final jsonBody = await getStripeResponse();
@ -896,14 +1021,20 @@ class CardTextFieldState extends State<CardTextField> {
_horizontalScrollController.animateTo(-20.0, duration: dur, curve: cur); _horizontalScrollController.animateTo(-20.0, duration: dur, curve: cur);
break; break;
case CardEntryStep.exp: case CardEntryStep.exp:
_horizontalScrollController.animateTo(_cardFieldWidth / 2, duration: dur, curve: cur); _horizontalScrollController.animateTo(_cardFieldWidth / 2,
duration: dur, curve: cur);
break; break;
case CardEntryStep.cvc: case CardEntryStep.cvc:
_horizontalScrollController.animateTo(_cardFieldWidth / 2 + _expirationFieldWidth, duration: dur, curve: cur); _horizontalScrollController.animateTo(
_cardFieldWidth / 2 + _expirationFieldWidth,
duration: dur,
curve: cur);
break; break;
case CardEntryStep.postal: case CardEntryStep.postal:
_horizontalScrollController.animateTo(_cardFieldWidth / 2 + _expirationFieldWidth + _securityFieldWidth, _horizontalScrollController.animateTo(
duration: dur, curve: cur); _cardFieldWidth / 2 + _expirationFieldWidth + _securityFieldWidth,
duration: dur,
curve: cur);
break; break;
} }
} }
@ -941,8 +1072,10 @@ class CardTextFieldState extends State<CardTextField> {
postalCodeFocusNode.requestFocus(); postalCodeFocusNode.requestFocus();
break; break;
} }
/// Make the selection adjustment after first frame builds /// Make the selection adjustment after first frame builds
if (kIsWeb) WidgetsBinding.instance.addPostFrameCallback((_) => _adjustSelection()); if (kIsWeb)
WidgetsBinding.instance.addPostFrameCallback((_) => _adjustSelection());
if (!isWideFormat) { if (!isWideFormat) {
_scrollRow(step); _scrollRow(step);
@ -963,26 +1096,26 @@ class CardTextFieldState extends State<CardTextField> {
case CardEntryStep.number: case CardEntryStep.number:
final len = _cardNumberController.text.length; final len = _cardNumberController.text.length;
final offset = len == 0 ? 1 : len; final offset = len == 0 ? 1 : len;
_cardNumberController.value = _cardNumberController.value = _cardNumberController.value.copyWith(
_cardNumberController.value.copyWith(selection: TextSelection(baseOffset: offset, extentOffset: offset)); selection: TextSelection(baseOffset: offset, extentOffset: offset));
break; break;
case CardEntryStep.exp: case CardEntryStep.exp:
final len = _expirationController.text.length; final len = _expirationController.text.length;
final offset = len == 0 ? 0 : len; final offset = len == 0 ? 0 : len;
_expirationController.value = _expirationController.value = _expirationController.value.copyWith(
_expirationController.value.copyWith(selection: TextSelection(baseOffset: offset, extentOffset: offset)); selection: TextSelection(baseOffset: offset, extentOffset: offset));
break; break;
case CardEntryStep.cvc: case CardEntryStep.cvc:
final len = _securityCodeController.text.length; final len = _securityCodeController.text.length;
final offset = len == 0 ? 0 : len; final offset = len == 0 ? 0 : len;
_securityCodeController.value = _securityCodeController.value = _securityCodeController.value.copyWith(
_securityCodeController.value.copyWith(selection: TextSelection(baseOffset: offset, extentOffset: offset)); selection: TextSelection(baseOffset: offset, extentOffset: offset));
break; break;
case CardEntryStep.postal: case CardEntryStep.postal:
final len = _postalCodeController.text.length; final len = _postalCodeController.text.length;
final offset = len == 0 ? 0 : len; final offset = len == 0 ? 0 : len;
_postalCodeController.value = _postalCodeController.value = _postalCodeController.value.copyWith(
_postalCodeController.value.copyWith(selection: TextSelection(baseOffset: offset, extentOffset: offset)); selection: TextSelection(baseOffset: offset, extentOffset: offset));
break; break;
} }
} }
@ -1056,7 +1189,8 @@ class CardTextFieldState extends State<CardTextField> {
/// to make the card number display cleanly. /// to make the card number display cleanly.
class CardNumberInputFormatter implements TextInputFormatter { class CardNumberInputFormatter implements TextInputFormatter {
@override @override
TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
String cardNum = newValue.text; String cardNum = newValue.text;
if (cardNum.length <= 4) return newValue; if (cardNum.length <= 4) return newValue;
@ -1071,7 +1205,9 @@ class CardNumberInputFormatter implements TextInputFormatter {
} }
} }
return newValue.copyWith(text: buffer.toString(), selection: TextSelection.collapsed(offset: buffer.length)); return newValue.copyWith(
text: buffer.toString(),
selection: TextSelection.collapsed(offset: buffer.length));
} }
} }
@ -1079,7 +1215,8 @@ class CardNumberInputFormatter implements TextInputFormatter {
/// the month and the year for the expiration date. /// the month and the year for the expiration date.
class CardExpirationFormatter implements TextInputFormatter { class CardExpirationFormatter implements TextInputFormatter {
@override @override
TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
String cardExp = newValue.text; String cardExp = newValue.text;
if (cardExp.length == 1) { if (cardExp.length == 1) {
if (cardExp[0] == '0' || cardExp[0] == '1') { if (cardExp[0] == '0' || cardExp[0] == '1') {
@ -1100,6 +1237,8 @@ class CardExpirationFormatter implements TextInputFormatter {
buffer.write('/'); buffer.write('/');
} }
} }
return newValue.copyWith(text: buffer.toString(), selection: TextSelection.collapsed(offset: buffer.length)); return newValue.copyWith(
text: buffer.toString(),
selection: TextSelection.collapsed(offset: buffer.length));
} }
} }