diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c0cef3..ab867ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.1.4 +* Feature : Added number and CVV obfuscation +* Feature : Added input decorations +* Feature : Added simple card input validations + ## 0.1.3 * Feature : Applied Default ThemeData. diff --git a/README.md b/README.md index c0cf30e..ab51be8 100644 --- a/README.md +++ b/README.md @@ -46,57 +46,47 @@ import 'package:flutter_credit_card/flutter_credit_card.dart'; cvvCode: cvvCode, showBackView: isCvvFocused, cardbgColor: Colors.black, + obscureCardNumber: true, + obscureCardCvv: true, height: 175, textStyle: TextStyle(color: Colors.yellowAccent), width: MediaQuery.of(context).size.width, animationDuration: Duration(milliseconds: 1000), - ), + ), ``` 3. Adding CreditCardForm ```dart CreditCardForm( + formKey: formKey, // Required + onCreditCardModelChange: (CreditCardModel data) {}, // Required themeColor: Colors.red, - onCreditCardModelChange: (CreditCardModel data) {}, + obscureCvv: true, + obscureNumber: true, + cardNumberDecoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: 'Number', + hintText: 'XXXX XXXX XXXX XXXX', + ), + expiryDateDecoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: 'Expired Date', + hintText: 'XX/XX', + ), + cvvCodeDecoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: 'CVV', + hintText: 'XXX', + ), + cardHolderDecoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: 'Card Holder', + ), ), ``` -## Localization -To localize text field hints and labels, use `LocalizedText` model. -```dart -const LocalizedText localizedText = LocalizedText( - cardNumberLabel: 'Kreditkartennummer', - cardNumberHint: 'XXXX-XXXX-XXXX-XXXX', - expiryDateLabel: 'Ablaufdatum', - expiryDateHint: 'MM/JJ', - cvvLabel: 'Kartenprüfnummer', - cvvHint: 'XXX', - cardHolderLabel: 'Karteninhaber', - cardHolderHint: 'Max Mustermann', -); - -return Column( - children: [ - CreditCardWidget( - cardNumber: cardNumber, - expiryDate: expiryDate, - cardHolderName: cardHolderName, - cvvCode: cvvCode, - showBackView: isCvvFocused, - localizedText: localizedText, - ), - Expanded( - child: SingleChildScrollView( - child: CreditCardForm( - onCreditCardModelChange: onCreditCardModelChange, - localizedText: localizedText, - ), - ), - ], -); -``` ## How to use Check out the **example** app in the [example](example) directory or the 'Example' tab on pub.dartlang.org for a more complete example. diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 8bd86f6..7be3d8b 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/example/ios/Flutter/flutter_export_environment.sh b/example/ios/Flutter/flutter_export_environment.sh new file mode 100644 index 0000000..bb0b264 --- /dev/null +++ b/example/ios/Flutter/flutter_export_environment.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=C:\src\flutter" +export "FLUTTER_APPLICATION_PATH=C:\Users\Deana\Flutter\flutter_libraries\flutter_credit_card\example" +export "FLUTTER_TARGET=lib\main.dart" +export "FLUTTER_BUILD_DIR=build" +export "SYMROOT=${SOURCE_ROOT}/../build\ios" +export "OTHER_LDFLAGS=$(inherited) -framework Flutter" +export "FLUTTER_FRAMEWORK_DIR=C:\src\flutter\bin\cache\artifacts\engine\ios" +export "FLUTTER_BUILD_NAME=1.0.0" +export "FLUTTER_BUILD_NUMBER=1" diff --git a/example/lib/main.dart b/example/lib/main.dart index 27ec84d..0ad4fcf 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -18,6 +18,7 @@ class MySampleState extends State { String cardHolderName = ''; String cvvCode = ''; bool isCvvFocused = false; + final GlobalKey formKey = GlobalKey(); @override Widget build(BuildContext context) { @@ -38,14 +39,67 @@ class MySampleState extends State { cardHolderName: cardHolderName, cvvCode: cvvCode, showBackView: isCvvFocused, + obscureCardNumber: true, + obscureCardCvv: true, ), Expanded( child: SingleChildScrollView( - child: CreditCardForm( - onCreditCardModelChange: onCreditCardModelChange, + child: Column( + children: [ + CreditCardForm( + formKey: formKey, + obscureCvv: true, + obscureNumber: true, + cardNumberDecoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: 'Number', + hintText: 'XXXX XXXX XXXX XXXX', + ), + expiryDateDecoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: 'Expired Date', + hintText: 'XX/XX', + ), + cvvCodeDecoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: 'CVV', + hintText: 'XXX', + ), + cardHolderDecoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: 'Card Holder', + ), + onCreditCardModelChange: onCreditCardModelChange, + ), + RaisedButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + child: Container( + margin: const EdgeInsets.all(8), + child: const Text( + 'Validate', + style: TextStyle( + color: Colors.white, + fontFamily: 'halter', + fontSize: 14, + package: 'flutter_credit_card', + ), + ), + ), + color: const Color(0xff1b447b), + onPressed: () { + if (formKey.currentState.validate()) { + print('valid!'); + } else { + print('invalid!'); + } + }, + ) + ], ), ), - ) + ), ], ), ), diff --git a/example/pubspec.lock b/example/pubspec.lock index be1eab8..d80f680 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -1,34 +1,62 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + archive: + dependency: transitive + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.13" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.0" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.2.0" + version: "2.4.1" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "2.0.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.1.2" + version: "1.1.3" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.11" + version: "1.14.12" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.4" cupertino_icons: dependency: "direct main" description: @@ -47,47 +75,54 @@ packages: path: ".." relative: true source: path - version: "0.1.1" + version: "0.1.4" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + image: + dependency: transitive + description: + name: image + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.12" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.5" + version: "0.12.6" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.1.6" + version: "1.1.8" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.6.2" - pedantic: + version: "1.6.4" + petitparser: dependency: transitive description: - name: pedantic + name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "2.4.0" quiver: dependency: transitive description: name: quiver url: "https://pub.dartlang.org" source: hosted - version: "2.0.3" + version: "2.1.3" sky_engine: dependency: transitive description: flutter @@ -99,7 +134,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.5.5" + version: "1.7.0" stack_trace: dependency: transitive description: @@ -120,7 +155,7 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "1.0.5" term_glyph: dependency: transitive description: @@ -134,7 +169,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.5" + version: "0.2.15" typed_data: dependency: transitive description: @@ -149,5 +184,12 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.8" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "3.6.1" sdks: - dart: ">=2.2.2 <3.0.0" + dart: ">=2.6.0 <3.0.0" diff --git a/lib/credit_card_form.dart b/lib/credit_card_form.dart index b3b8bb7..ca6135c 100644 --- a/lib/credit_card_form.dart +++ b/lib/credit_card_form.dart @@ -12,23 +12,52 @@ class CreditCardForm extends StatefulWidget { this.expiryDate, this.cardHolderName, this.cvvCode, + this.obscureCvv = false, + this.obscureNumber = false, @required this.onCreditCardModelChange, this.themeColor, this.textColor = Colors.black, this.cursorColor, - this.localizedText = const LocalizedText(), - }) : assert(localizedText != null), - super(key: key); + this.cardHolderDecoration = const InputDecoration( + labelText: 'Card holder', + ), + this.cardNumberDecoration = const InputDecoration( + labelText: 'Card number', + hintText: 'XXXX XXXX XXXX XXXX', + ), + this.expiryDateDecoration = const InputDecoration( + labelText: 'Expired Date', + hintText: 'MM/YY', + ), + this.cvvCodeDecoration = const InputDecoration( + labelText: 'CVV', + hintText: 'XXX', + ), + @required this.formKey, + this.cvvValidationMessage = 'Please input a valid CVV', + this.dateValidationMessage = 'Please input a valid date', + this.numberValidationMessage = 'Please input a valid number', + }) : super(key: key); final String cardNumber; final String expiryDate; final String cardHolderName; final String cvvCode; + final String cvvValidationMessage; + final String dateValidationMessage; + final String numberValidationMessage; final void Function(CreditCardModel) onCreditCardModelChange; final Color themeColor; final Color textColor; final Color cursorColor; - final LocalizedText localizedText; + final bool obscureCvv; + final bool obscureNumber; + final GlobalKey formKey; + + final InputDecoration cardNumberDecoration; + final InputDecoration cardHolderDecoration; + final InputDecoration expiryDateDecoration; + final InputDecoration cvvCodeDecoration; @override _CreditCardFormState createState() => _CreditCardFormState(); @@ -55,6 +84,9 @@ class _CreditCardFormState extends State { MaskedTextController(mask: '0000'); FocusNode cvvFocusNode = FocusNode(); + FocusNode cardNumberNode = FocusNode(); + FocusNode expiryDateNode = FocusNode(); + FocusNode cardHolderNode = FocusNode(); void textFieldFocusDidChange() { creditCardModel.isCvvFocused = cvvFocusNode.hasFocus; @@ -114,6 +146,14 @@ class _CreditCardFormState extends State { }); } + @override + void dispose() { + cardHolderNode.dispose(); + cvvFocusNode.dispose(); + expiryDateNode.dispose(); + super.dispose(); + } + @override void didChangeDependencies() { themeColor = widget.themeColor ?? Theme.of(context).primaryColor; @@ -128,67 +168,105 @@ class _CreditCardFormState extends State { primaryColorDark: themeColor, ), child: Form( + key: widget.formKey, child: Column( children: [ Container( padding: const EdgeInsets.symmetric(vertical: 8.0), margin: const EdgeInsets.only(left: 16, top: 16, right: 16), child: TextFormField( + obscureText: widget.obscureNumber, controller: _cardNumberController, cursorColor: widget.cursorColor ?? themeColor, + onEditingComplete: () { + FocusScope.of(context).requestFocus(expiryDateNode); + }, style: TextStyle( color: widget.textColor, ), - decoration: InputDecoration( - border: const OutlineInputBorder(), - labelText: widget.localizedText.cardNumberLabel, - hintText: widget.localizedText.cardNumberHint, - ), - keyboardType: TextInputType.number, - textInputAction: TextInputAction.next, - ), - ), - Container( - padding: const EdgeInsets.symmetric(vertical: 8.0), - margin: const EdgeInsets.only(left: 16, top: 8, right: 16), - child: TextFormField( - controller: _expiryDateController, - cursorColor: widget.cursorColor ?? themeColor, - style: TextStyle( - color: widget.textColor, - ), - decoration: InputDecoration( - border: const OutlineInputBorder(), - labelText: widget.localizedText.expiryDateLabel, - hintText: widget.localizedText.expiryDateHint, - ), + decoration: widget.cardNumberDecoration, keyboardType: TextInputType.number, textInputAction: TextInputAction.next, + validator: (String value) { + // Validate less that 13 digits +3 white spaces + if (value.isEmpty || value.length < 16) { + return widget.numberValidationMessage; + } + return null; + }, ), ), - Container( - padding: const EdgeInsets.symmetric(vertical: 8.0), - margin: const EdgeInsets.only(left: 16, top: 8, right: 16), - child: TextField( - focusNode: cvvFocusNode, - controller: _cvvCodeController, - cursorColor: widget.cursorColor ?? themeColor, - style: TextStyle( - color: widget.textColor, + Row( + children: [ + Expanded( + child: Container( + padding: const EdgeInsets.symmetric(vertical: 8.0), + margin: const EdgeInsets.only(left: 16, top: 8, right: 16), + child: TextFormField( + controller: _expiryDateController, + cursorColor: widget.cursorColor ?? themeColor, + focusNode: expiryDateNode, + onEditingComplete: () { + FocusScope.of(context).requestFocus(cvvFocusNode); + }, + style: TextStyle( + color: widget.textColor, + ), + decoration: widget.expiryDateDecoration, + keyboardType: TextInputType.number, + textInputAction: TextInputAction.next, + validator: (String value) { + if (value.isEmpty) { + return widget.dateValidationMessage; + } + + final DateTime now = DateTime.now(); + final List date = value.split(RegExp(r'/')); + final int month = int.parse(date.first); + final int year = int.parse('20${date.last}'); + final DateTime cardDate = DateTime(year, month); + + if (cardDate.isBefore(now) || month > 12 || month == 0) { + return widget.dateValidationMessage; + } + return null; + }, + ), + ), ), - decoration: InputDecoration( - border: const OutlineInputBorder(), - labelText: widget.localizedText.cvvLabel, - hintText: widget.localizedText.cvvHint, + Expanded( + child: Container( + padding: const EdgeInsets.symmetric(vertical: 8.0), + margin: const EdgeInsets.only(left: 16, top: 8, right: 16), + child: TextFormField( + obscureText: widget.obscureCvv, + focusNode: cvvFocusNode, + controller: _cvvCodeController, + cursorColor: widget.cursorColor ?? themeColor, + onEditingComplete: () { + FocusScope.of(context).requestFocus(cardHolderNode); + }, + style: TextStyle( + color: widget.textColor, + ), + decoration: widget.cvvCodeDecoration, + keyboardType: TextInputType.number, + textInputAction: TextInputAction.next, + onChanged: (String text) { + setState(() { + cvvCode = text; + }); + }, + validator: (value) { + if (value.isEmpty || value.length < 3) { + return widget.cvvValidationMessage; + } + return null; + }, + ), + ), ), - keyboardType: TextInputType.number, - textInputAction: TextInputAction.done, - onChanged: (String text) { - setState(() { - cvvCode = text; - }); - }, - ), + ], ), Container( padding: const EdgeInsets.symmetric(vertical: 8.0), @@ -196,16 +274,16 @@ class _CreditCardFormState extends State { child: TextFormField( controller: _cardHolderNameController, cursorColor: widget.cursorColor ?? themeColor, + focusNode: cardHolderNode, style: TextStyle( color: widget.textColor, ), - decoration: InputDecoration( - border: const OutlineInputBorder(), - labelText: widget.localizedText.cardHolderLabel, - hintText: widget.localizedText.cardHolderHint, - ), + decoration: widget.cardHolderDecoration, keyboardType: TextInputType.text, - textInputAction: TextInputAction.next, + textInputAction: TextInputAction.done, + onEditingComplete: () { + onCreditCardModelChange(creditCardModel); + }, ), ), ], diff --git a/lib/credit_card_model.dart b/lib/credit_card_model.dart index 4a93ea6..9d8146a 100644 --- a/lib/credit_card_model.dart +++ b/lib/credit_card_model.dart @@ -1,10 +1,10 @@ class CreditCardModel { - CreditCardModel(this.cardNumber, this.expiryDate, this.cardHolderName, this.cvvCode, this.isCvvFocused); String cardNumber = ''; String expiryDate = ''; String cardHolderName = ''; String cvvCode = ''; + String brand = ''; bool isCvvFocused = false; } diff --git a/lib/credit_card_widget.dart b/lib/credit_card_widget.dart index 78961ea..cf0898a 100644 --- a/lib/credit_card_widget.dart +++ b/lib/credit_card_widget.dart @@ -2,7 +2,12 @@ import 'dart:math'; import 'package:flutter/material.dart'; -import 'localized_text_model.dart'; +const Map CardTypeIconAsset = { + 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({ @@ -17,10 +22,13 @@ class CreditCardWidget extends StatefulWidget { this.width, this.textStyle, this.cardBgColor = const Color(0xff1b447b), - this.localizedText = const LocalizedText(), + this.obscureCardNumber = true, + this.obscureCardCvv = true, + this.labelCardHolder = 'CARD HOLDER', + this.labelExpiredDate = 'MM/YY', + this.cardType, }) : assert(cardNumber != null), assert(showBackView != null), - assert(localizedText != null), super(key: key); final String cardNumber; @@ -33,14 +41,19 @@ class CreditCardWidget extends StatefulWidget { final Duration animationDuration; final double height; final double width; - final LocalizedText localizedText; + final bool obscureCardNumber; + final bool obscureCardCvv; + + final String labelCardHolder; + final String labelExpiredDate; + + final CardType cardType; @override _CreditCardWidgetState createState() => _CreditCardWidgetState(); } -class _CreditCardWidgetState extends State - with SingleTickerProviderStateMixin { +class _CreditCardWidgetState extends State with SingleTickerProviderStateMixin { AnimationController controller; Animation _frontRotation; Animation _backRotation; @@ -76,8 +89,7 @@ class _CreditCardWidgetState extends State _frontRotation = TweenSequence( >[ TweenSequenceItem( - tween: Tween(begin: 0.0, end: pi / 2) - .chain(CurveTween(curve: Curves.easeIn)), + tween: Tween(begin: 0.0, end: pi / 2).chain(CurveTween(curve: Curves.easeIn)), weight: 50.0, ), TweenSequenceItem( @@ -94,8 +106,7 @@ class _CreditCardWidgetState extends State weight: 50.0, ), TweenSequenceItem( - tween: Tween(begin: -pi / 2, end: 0.0) - .chain(CurveTween(curve: Curves.easeOut)), + tween: Tween(begin: -pi / 2, end: 0.0).chain(CurveTween(curve: Curves.easeOut)), weight: 50.0, ), ], @@ -148,31 +159,28 @@ class _CreditCardWidgetState extends State BuildContext context, Orientation orientation, ) { - final TextStyle defaultTextStyle = Theme.of(context).textTheme.title.merge( - TextStyle( - color: Colors.black, - fontFamily: 'halter', - fontSize: 16, - package: 'flutter_credit_card', - ), - ); + final TextStyle defaultTextStyle = + Theme.of(context).textTheme.headline6.merge( + 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 Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), - boxShadow: const [ - BoxShadow( - color: Colors.black26, - offset: Offset(0, 0), - blurRadius: 24, - ), - ], gradient: backgroundGradientColor, ), margin: const EdgeInsets.all(16), width: widget.width ?? width, - height: widget.height ?? - (orientation == Orientation.portrait ? height / 4 : height / 2), + height: widget.height ?? (orientation == Orientation.portrait ? height / 4 : height / 2), child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.start, @@ -207,8 +215,8 @@ class _CreditCardWidgetState extends State padding: const EdgeInsets.all(5), child: Text( widget.cvvCode.isEmpty - ? isAmex ? 'XXXX' : widget.localizedText.cvvHint - : widget.cvvCode, + ? isAmex ? 'XXXX' : 'XXX' + : cvv, maxLines: 1, style: widget.textStyle ?? defaultTextStyle, ), @@ -225,7 +233,7 @@ class _CreditCardWidgetState extends State alignment: Alignment.bottomRight, child: Padding( padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), - child: getCardTypeIcon(widget.cardNumber), + child: widget.cardType != null ? getCardTypeImage(widget.cardType) : getCardTypeIcon(widget.cardNumber), ), ), ), @@ -244,31 +252,34 @@ class _CreditCardWidgetState extends State BuildContext context, Orientation orientation, ) { - final TextStyle defaultTextStyle = Theme.of(context).textTheme.title.merge( - TextStyle( - color: Colors.white, - fontFamily: 'halter', - fontSize: 16, - package: 'flutter_credit_card', - ), - ); + final TextStyle defaultTextStyle = + Theme.of(context).textTheme.headline6.merge( + 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 Container( margin: const EdgeInsets.all(16), decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), - boxShadow: const [ + gradient: backgroundGradientColor, + boxShadow: [ BoxShadow( - color: Colors.black26, - offset: Offset(0, 0), - blurRadius: 24, - ) + color: Colors.grey, + blurRadius: 5, + ), ], - gradient: backgroundGradientColor, ), width: widget.width ?? width, - height: widget.height ?? - (orientation == Orientation.portrait ? height / 4 : height / 2), + height: widget.height ?? (orientation == Orientation.portrait ? height / 4 : height / 2), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -276,7 +287,7 @@ class _CreditCardWidgetState extends State alignment: Alignment.topRight, child: Padding( padding: const EdgeInsets.only(left: 16, right: 16, top: 8), - child: getCardTypeIcon(widget.cardNumber), + child: widget.cardType != null ? getCardTypeImage(widget.cardType) : getCardTypeIcon(widget.cardNumber), ), ), Expanded( @@ -284,8 +295,8 @@ class _CreditCardWidgetState extends State padding: const EdgeInsets.only(left: 16), child: Text( widget.cardNumber.isEmpty || widget.cardNumber == null - ? widget.localizedText.cardNumberHint - : widget.cardNumber, + ? 'XXXX XXXX XXXX XXXX' + : number, style: widget.textStyle ?? defaultTextStyle, ), ), @@ -295,9 +306,7 @@ class _CreditCardWidgetState extends State child: Padding( padding: const EdgeInsets.only(left: 16), child: Text( - widget.expiryDate.isEmpty || widget.expiryDate == null - ? widget.localizedText.expiryDateHint - : widget.expiryDate, + widget.expiryDate.isEmpty || widget.expiryDate == null ? widget.labelExpiredDate : widget.expiryDate, style: widget.textStyle ?? defaultTextStyle, ), ), @@ -306,9 +315,7 @@ class _CreditCardWidgetState extends State child: Padding( padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), child: Text( - widget.cardHolderName.isEmpty || widget.cardHolderName == null - ? widget.localizedText.cardHolderLabel.toUpperCase() - : widget.cardHolderName, + widget.cardHolderName.isEmpty || widget.cardHolderName == null ? widget.labelCardHolder : widget.cardHolderName, maxLines: 1, overflow: TextOverflow.ellipsis, style: widget.textStyle ?? defaultTextStyle, @@ -362,8 +369,7 @@ class _CreditCardWidgetState extends State (CardType type, Set> patterns) { for (List patternRange in patterns) { // Remove any spaces - String ccPatternStr = - cardNumber.replaceAll(RegExp(r'\s+\b|\b\s'), ''); + 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) { @@ -377,8 +383,7 @@ class _CreditCardWidgetState extends State 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) { + if (ccPrefixAsInt >= startPatternPrefixAsInt && ccPrefixAsInt <= endPatternPrefixAsInt) { // Found a match cardType = type; break; @@ -398,6 +403,13 @@ class _CreditCardWidgetState extends State return cardType; } + Widget getCardTypeImage(CardType cardType) => 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) { @@ -485,15 +497,14 @@ class AnimationCard extends StatelessWidget { } class MaskedTextController extends TextEditingController { - MaskedTextController({String text, this.mask, Map translator}) - : super(text: text) { + MaskedTextController({String text, this.mask, Map translator}) : super(text: text) { this.translator = translator ?? MaskedTextController.getDefaultTranslator(); addListener(() { final String previous = _lastUpdatedText; - if (this.beforeChange(previous, this.text)) { + if (beforeChange(previous, this.text)) { updateText(this.text); - this.afterChange(previous, this.text); + afterChange(previous, this.text); } else { updateText(_lastUpdatedText); } @@ -534,8 +545,7 @@ class MaskedTextController extends TextEditingController { void moveCursorToEnd() { final String text = _lastUpdatedText; - selection = - TextSelection.fromPosition(TextPosition(offset: (text ?? '').length)); + selection = TextSelection.fromPosition(TextPosition(offset: (text ?? '').length)); } @override @@ -547,12 +557,7 @@ class MaskedTextController extends TextEditingController { } static Map getDefaultTranslator() { - return { - 'A': RegExp(r'[A-Za-z]'), - '0': RegExp(r'[0-9]'), - '@': RegExp(r'[A-Za-z0-9]'), - '*': RegExp(r'.*') - }; + return {'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) { diff --git a/lib/flutter_credit_card.dart b/lib/flutter_credit_card.dart index a988e31..b9879f4 100644 --- a/lib/flutter_credit_card.dart +++ b/lib/flutter_credit_card.dart @@ -2,5 +2,4 @@ library flutter_credit_card; export 'credit_card_form.dart'; export 'credit_card_model.dart'; -export 'credit_card_widget.dart'; -export 'localized_text_model.dart'; +export 'credit_card_widget.dart'; \ No newline at end of file diff --git a/preview/preview.gif b/preview/preview.gif index eb2e89e..8d541fd 100644 Binary files a/preview/preview.gif and b/preview/preview.gif differ diff --git a/pubspec.lock b/pubspec.lock index 7642229..97283e8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,34 +1,62 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + archive: + dependency: transitive + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.13" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.0" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.2.0" + version: "2.4.1" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "2.0.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.1.2" + version: "1.1.3" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.11" + version: "1.14.12" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.4" flutter: dependency: "direct main" description: flutter @@ -39,41 +67,48 @@ packages: description: flutter source: sdk version: "0.0.0" + image: + dependency: transitive + description: + name: image + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.12" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.5" + version: "0.12.6" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.1.6" + version: "1.1.8" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.6.2" - pedantic: + version: "1.6.4" + petitparser: dependency: transitive description: - name: pedantic + name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "2.4.0" quiver: dependency: transitive description: name: quiver url: "https://pub.dartlang.org" source: hosted - version: "2.0.3" + version: "2.1.3" sky_engine: dependency: transitive description: flutter @@ -85,7 +120,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.5.5" + version: "1.7.0" stack_trace: dependency: transitive description: @@ -106,7 +141,7 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "1.0.5" term_glyph: dependency: transitive description: @@ -120,7 +155,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.5" + version: "0.2.15" typed_data: dependency: transitive description: @@ -135,5 +170,12 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.8" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "3.6.1" sdks: - dart: ">=2.2.2 <3.0.0" + dart: ">=2.6.0 <3.0.0"