dart formatting
This commit is contained in:
parent
abcbf96d08
commit
713f35c692
|
@ -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
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user