Compare commits

...

13 Commits

Author SHA1 Message Date
Nathan Anderson 7ffeb2d199 dart analyzer 2023-12-07 16:24:17 -07:00
Nathan Anderson 3e140d6310 0.0.10 2023-12-07 16:23:11 -07:00
Nathan Anderson dcc5cf010f 0.0.10, remove that pesky Y 2023-12-07 16:03:02 -07:00
Nathan Anderson 713f35c692 dart formatting 2023-12-07 15:38:08 -07:00
Nathan Anderson abcbf96d08 0.0.9 2023-12-07 15:37:29 -07:00
Nathan Anderson f4b05dbccf Fix versioning 2023-12-05 17:45:59 -07:00
Nathan Anderson ef189517c1 Changed lower sdk constraint 2023-12-05 11:08:39 -07:00
Nathan Anderson 462e40308f 0.0.6 release 2023-12-01 16:12:42 -07:00
Nathan Anderson d7d27a1cf5 Update pubspec to 0.0.5 2023-11-22 10:55:29 -07:00
Nathan Anderson 1c918f72f9 Update changelog to 0.0.5 2023-11-22 10:54:15 -07:00
Nathan Anderson 4b0e4a98de Added web fix for platform call and static analysis fixes 2023-11-22 10:52:52 -07:00
n8r 645d0749da Merge pull request 'Updateto 0.0.4' (#2) from 0.0.4 into main
Reviewed-on: #2
2023-11-21 13:39:47 -07:00
Nathan Anderson bd91c1f814 Updateto 0.0.4 2023-11-21 13:38:02 -07:00
16 changed files with 1160 additions and 737 deletions
+42
View File
@@ -1,3 +1,45 @@
## 0.0.10
- One little 'Y' I missed
- Fixed out of range error with `_adjustSelection`
## 0.0.9
- Drastically improved usability and performance with flutter web and canvaskit renderer, especially on mobile
- Using streams to more accurately call `widget.onValidCardDetails` when the card details are valid and completed
- Added `cursorColor` customization
- Reworked widget life cycle so that hot reloads work as expected (resizing, focus, etc.).
## 0.0.8
- Updated dart sdk constraints again... oops (>=3.0.0)
## 0.0.7
- Changed pubspec versioning to allow lower SDK constraints (>=2.12.0)
## 0.0.6
- Improved assertion and error messaging when missing stripe implements
- Added better doc comments
- Fixed `CardTextField.delayToShowLoading`, now it uses it
- Fixed bad assertion logic when providing stripe keys
- Added ability to make Stripe call with `GlobalKey`
- Refactored method `onTokenReceived` to `onStripeResponse` to be clearer
- Refactored method `onCardDetailsComplete` to `onValidCardDetails` to be clearer
## 0.0.5
- Fix Web, invalid call to `Platform.isAndroid`
- Analysis issues fixed for pub points
## 0.0.4
- Fix for focus and soft keyboard on mobile devices
- Added README gif to show `CardTextField` in action
- Added Icon color customization to `CardProviderIcon` widget
- Fleshed out a dark mode styling example
## 0.0.3
Lots of improvements!
+30 -20
View File
@@ -9,11 +9,11 @@ elements they provide in flutter was inconvenient for me, so I made this package
Got to use emojis and taglines for attention grabbing and algorithm hacking:
- 󱐋 Blazingly fast ( its as fast as the rest of flutter )
- 󰃢 Cleaner ( fewer dependencies than the official stripe elements )
- Blazingly fast ( its as fast as the rest of flutter )
- 🧹 Cleaner & Easier to Use ( fewer dependencies than the official stripe elements )
- 🛡 Safe and Supports all Flutter Targets ( its native flutter with minimal dependencies )
- ☑ Seemless UI/UX ( hard to match stripe quality, but I think I got close )
- Built-in Stripe Integration ( guess that one is obvious )
- ☑ Seemless UI/UX ( hard to match stripe quality, but I think I got pretty close )
- 💳 Built-in Stripe Integration ( guess that one is obvious )
- ☯ Chi Energy Boost ( alright I'm fishing... )
## Why StripeNativeCardField?
@@ -23,7 +23,7 @@ Got to use emojis and taglines for attention grabbing and algorithm hacking:
- Native Implementation: compiles and loads like the rest of your app, unlike embeded html
- Automatic validation: no `inputFormatters` or `RegExp` needed on your side
The card data can either be retrieved with the `onCardDetailsComplete` callback, or
The card data can either be retrieved with the `onValidCardDetails` callback, or
you can have the element automatically create a Stripe card token when the fields
are filled out, and return the token with the `onTokenReceived` callback.
@@ -31,19 +31,23 @@ are filled out, and return the token with the `onTokenReceived` callback.
![Card Provider Detection](https://git.fosscat.com/n8r/stripe_native_card_field/raw/branch/main/readme_assets/card_provider_detection.gif)
[Documentation to supported card providers](https://pub.dev/documentation/stripe_native_card_field/latest/card_details/CardProviderID.html)
Currently support American Express, Diners Club, Discover Card, Mastercard, Jcb, Visa
[Supported Card Providers in Docs](https://pub.dev/documentation/stripe_native_card_field/latest/card_details/CardProviderID.html)
### Customizable Styles
![Customizable Style 1]()
![Customizable Style 1](https://git.fosscat.com/n8r/stripe_native_card_field/raw/branch/main/readme_assets/customizable_style.gif)
![Customizable Style 2]()
This dark mode style example provided [here](https://git.fosscat.com/n8r/stripe_native_card_field/raw/branch/main/example/lib/dark_customization.dart)
### Cross Platform
For documentation on all of the available customizable aspects of the `CardTextField`, go
to the [API docs here](https://pub.dev/documentation/stripe_native_card_field/latest/stripe_native_card_field/CardTextField-class.html).
![desktop showcase](./example/loading.gif)
### Smooth UX
![Smooth UX](https://git.fosscat.com/n8r/stripe_native_card_field/raw/branch/main/readme_assets/smooth_ux.gif)
Mimics the Stripe html elements behavior wherever possible. Auto focusing / transitioning text fields, backspacing focuses to last field,
automatically validating user input, etc.
# Getting started
@@ -58,12 +62,13 @@ Include the package in a file:
import 'package:stripe_native_card_field/stripe_native_card_field.dart';
```
### For just Card Data
### For Raw Card Data
Provide a callback for the `CardTextField` to return you the data when its complete.
```dart
CardTextField(
width: 500,
onCardDetailsComplete: (details) {
onValidCardDetails: (details) {
// Save the card details to use with your call to Stripe, or whoever
setState(() => _cardDetails = details);
},
@@ -72,22 +77,27 @@ CardTextField(
### For Stripe Token
Simply provide a function for the `onStripeResponse` callback!
```dart
CardTextField(
width: 500,
stripePublishableKey: 'pk_test_abc123', // Your stripe key here
onTokenReceived: (token) {
onStripeResponse: (Map<String, dynamic> data) {
// Save the stripe token to send to your backend
setState(() => _token = token);
setState(() => _tokenData = data);
},
);
```
### Cumstomization
For documentation on all of the available customizable aspects of the `CardTextField`, go
to the [API docs here](https://pub.dev/documentation/stripe_native_card_field/latest/stripe_native_card_field/CardTextField-class.html).
If you want more fine-grained control of when the stripe call is made, you
can create a `GlobalKey` and access the `CardTextFieldState`, calling the
`getStripeResponse()` function yourself. See the provided [example](https://pub.dev/packages/stripe_native_card_field/example)
for details. If you choose this route, do not provide an `onStripeResponse` callback, or you will end up
making two calls to stripe!
# Additional information
Repository located [here](https://git.fosscat.com/n8r/stripe_native_card_field)
Please email me at n8r@fosscat.com for any issues or PRs.
Binary file not shown.
+101
View File
@@ -0,0 +1,101 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:stripe_native_card_field/card_details.dart';
import 'package:stripe_native_card_field/stripe_native_card_field.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Native Stripe Field Demo',
theme: ThemeData(
brightness: Brightness.dark,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple, brightness: Brightness.dark),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
CardDetailsValidState? state;
String? errorText;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black45,
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Padding(
padding: EdgeInsets.all(8.0),
child: Text(
'Enter your card details below:',
style: TextStyle(color: Colors.white70),
),
),
CardTextField(
width: 300,
onValidCardDetails: (details) {
if (kDebugMode) {
print(details);
}
},
textStyle:
const TextStyle(fontFamily: 'Lato', color: Colors.tealAccent),
hintTextStyle:
const TextStyle(fontFamily: 'Lato', color: Colors.teal),
errorTextStyle: const TextStyle(color: Colors.purpleAccent),
boxDecoration: BoxDecoration(
color: Colors.black54,
border: Border.all(
color: Colors.teal.withAlpha(255),
),
),
errorBoxDecoration: BoxDecoration(
color: Colors.black54,
border: Border.all(width: 3.0, color: Colors.purple),
),
cardIconColor: 'teal',
cardIconErrorColor: '#b65cc2',
overrideValidState: state,
errorText: errorText,
),
ElevatedButton(
child: const Text('Set manual error'),
onPressed: () => setState(() {
errorText = 'There is a problem';
state = CardDetailsValidState.invalidCard;
}),
)
],
),
),
);
}
}
+29 -20
View File
@@ -38,13 +38,27 @@ class _MyHomePageState extends State<MyHomePage> {
CardDetailsValidState? state;
String? errorText;
// Creating a global key here allows us to call the `getStripeResponse()`
// inside the CardTextFieldState widget in our build method. See below
final _key = GlobalKey<CardTextFieldState>();
@override
Widget build(BuildContext context) {
final cardField = CardTextField(
key: _key,
loadingWidgetLocation: LoadingLocation.above,
stripePublishableKey: 'pk_test_abc123testmykey',
width: 600,
onValidCardDetails: (details) {
if (kDebugMode) {
print(details);
}
},
overrideValidState: state,
errorText: errorText,
);
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
@@ -55,28 +69,23 @@ class _MyHomePageState extends State<MyHomePage> {
'Enter your card details below:',
),
),
CardTextField(
width: 300,
onCardDetailsComplete: (details) {
if (kDebugMode) {
print(details);
}
},
// textStyle: TextStyle(fontSize: 24.0),
// cardFieldWidth: 260,
// expFieldWidth: 100.0,
// securityFieldWidth: 60.0,
// postalFieldWidth: 130.0,
// iconSize: Size(50.0, 35.0),
overrideValidState: state,
errorText: errorText,
),
cardField,
ElevatedButton(
child: const Text('Set manual error'),
onPressed: () => setState(() {
errorText = 'There is a problem';
state = CardDetailsValidState.invalidCard;
}),
),
const SizedBox(height: 12),
ElevatedButton(
child: const Text('Get Stripe token'),
onPressed: () async {
// Here we use the global key to get the stripe data, rather than
// using the `onStripeResponse` callback in the widget
final tok = await _key.currentState?.getStripeResponse();
if (kDebugMode) print(tok);
},
)
],
),
+14 -14
View File
@@ -45,10 +45,10 @@ packages:
dependency: transitive
description:
name: collection
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev"
source: hosted
version: "1.17.2"
version: "1.18.0"
cupertino_icons:
dependency: "direct main"
description:
@@ -135,10 +135,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
url: "https://pub.dev"
source: hosted
version: "1.9.1"
version: "1.10.0"
path:
dependency: transitive
description:
@@ -180,18 +180,18 @@ packages:
dependency: transitive
description:
name: stack_trace
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev"
source: hosted
version: "1.11.0"
version: "1.11.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.1.2"
string_scanner:
dependency: transitive
description:
@@ -206,7 +206,7 @@ packages:
path: ".."
relative: true
source: path
version: "0.0.2"
version: "0.0.9"
term_glyph:
dependency: transitive
description:
@@ -219,10 +219,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
url: "https://pub.dev"
source: hosted
version: "0.6.0"
version: "0.6.1"
typed_data:
dependency: transitive
description:
@@ -267,10 +267,10 @@ packages:
dependency: transitive
description:
name: web
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
url: "https://pub.dev"
source: hosted
version: "0.1.4-beta"
version: "0.3.0"
xml:
dependency: transitive
description:
@@ -280,5 +280,5 @@ packages:
source: hosted
version: "6.3.0"
sdks:
dart: ">=3.1.3 <4.0.0"
dart: ">=3.2.0-194.0.dev <4.0.0"
flutter: ">=3.7.0-0"
+8 -1
View File
@@ -4,7 +4,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: '>=3.1.3 <4.0.0'
sdk: '>=3.0.0 <4.0.0'
dependencies:
flutter:
@@ -25,3 +25,10 @@ flutter:
uses-material-design: true
fonts:
- family: Lato
fonts:
- asset: assets/Lato-Medium.ttf
weight: 400
+33 -20
View File
@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
/// Class encapsulating the card's data
@@ -18,7 +20,11 @@ class CardDetails {
/// Sets every field to null, a default
/// `CardDetails` when nothing has been entered.
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.
@@ -35,6 +41,7 @@ class CardDetails {
CardDetailsValidState _validState = CardDetailsValidState.blank;
int _lastCheckHash = 0;
CardProvider? provider;
StreamController<CardDetails> onCompleteController = StreamController();
set overrideValidState(CardDetailsValidState state) => _validState = state;
@@ -47,12 +54,12 @@ class CardDetails {
String get expMonth => isComplete ? expirationString!.split('/').first : '';
String get expYear => isComplete ? expirationString!.split('/').last : '';
// TODO rename to be more clear
/// Returns true if `_cardNumber` is null, or
/// if the _cardNumber matches the detected `provider`'s
/// card lenght, defaulting to 16.
bool get cardNumberFilled =>
_cardNumber == null ? false : (provider?.cardLength ?? 16) == _cardNumber!.replaceAll(' ', '').length;
bool get cardNumberFilled => _cardNumber == null
? false
: (provider?.cardLength ?? 16) == _cardNumber!.replaceAll(' ', '').length;
/// Returns true if all details are complete and valid
/// otherwise, return false.
@@ -61,6 +68,14 @@ class CardDetails {
return _complete;
}
/// Detects if the card is complete, then broadcasts
/// card details to `onCompleteController`
void broadcastStatus() {
if (isComplete) {
onCompleteController.add(this);
}
}
/// The maximum length of the INN (identifier)
/// of a card provider.
int get maxINNLength => 4;
@@ -77,9 +92,12 @@ class CardDetails {
return;
}
_complete = false;
_lastCheckHash = currentHash;
if (_cardNumber == null && expirationString == null && securityCode == null && postalCode == null) {
_complete = false;
if (_cardNumber == null &&
expirationString == null &&
securityCode == null &&
postalCode == null) {
_validState = CardDetailsValidState.blank;
return;
}
@@ -91,61 +109,52 @@ class CardDetails {
)
.toList();
if (!_luhnAlgorithmCheck(nums)) {
_complete = false;
_validState = CardDetailsValidState.invalidCard;
return;
}
if (_cardNumber == null || !cardNumberFilled) {
_complete = false;
_validState = CardDetailsValidState.missingCard;
return;
}
if (expirationString == null) {
_complete = false;
_validState = CardDetailsValidState.missingDate;
return;
}
final expSplits = expirationString!.split('/');
if (expSplits.length != 2 || expSplits.last == '') {
_complete = false;
_validState = CardDetailsValidState.missingDate;
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) {
_complete = false;
_validState = CardDetailsValidState.invalidMonth;
return;
}
final year = 2000 + int.parse(expSplits.last);
final date = DateTime(year, month);
if (date.isBefore(DateTime.now())) {
_complete = false;
_validState = CardDetailsValidState.dateTooEarly;
return;
} else if (date.isAfter(DateTime.now().add(const Duration(days: 365 * 50)))) {
_complete = false;
} else if (date
.isAfter(DateTime.now().add(const Duration(days: 365 * 50)))) {
_validState = CardDetailsValidState.dateTooLate;
return;
}
expirationDate = date;
if (securityCode == null) {
_complete = false;
_validState = CardDetailsValidState.missingCVC;
return;
}
if (provider != null && securityCode!.length != provider!.cvcLength) {
_complete = false;
_validState = CardDetailsValidState.invalidCVC;
return;
}
if (postalCode == null) {
_complete = false;
_validState = CardDetailsValidState.missingZip;
return;
}
if (!RegExp(r'^\d{5}(-\d{4})?$').hasMatch(postalCode!)) {
_complete = false;
_validState = CardDetailsValidState.invalidZip;
return;
}
@@ -282,7 +291,11 @@ class CardProvider {
int cvcLength;
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
assert(innValidNums != null || innValidRanges != null);
// Do not provide empty list of valid nums
File diff suppressed because one or more lines are too long
+19
View File
@@ -0,0 +1,19 @@
/// Error class that `CardTextField` throws if any errors are encountered
class CardTextFieldError extends Error {
/// Details provided for the error
String? details;
CardTextFieldErrorType type;
CardTextFieldError(this.type, {this.details});
@override
String toString() {
return 'CardTextFieldError-${type.name}: $details';
}
}
/// Enum to add typing to the `CardTextFieldErrorType`
enum CardTextFieldErrorType {
stripeImplementation,
unknown,
}
File diff suppressed because it is too large Load Diff
+2 -3
View File
@@ -1,11 +1,10 @@
name: stripe_native_card_field
description: A native flutter implementation of the elegant Stripe Card Field.
version: 0.0.3
version: 0.0.10
repository: https://git.fosscat.com/n8r/stripe_native_card_field
environment:
sdk: '>=3.1.3 <4.0.0'
flutter: ">=1.17.0"
sdk: '>=3.0.0 <4.0.0'
dependencies:
flutter:
Binary file not shown.

After

Width:  |  Height:  |  Size: 924 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 MiB

-111
View File
@@ -1,111 +0,0 @@
---
author: Nathan Anderson
date: MMMM dd, YYYY
paging: Slide %d / %d
---
# Steps to Publish
1. Initialize Library
2. Write Library
4. Write some tests
5. Publish!
---
# 1. Initialize Your Library!
---
## Publishing Requirements
- You must include a LICENSE file.
- Your package must be smaller than 100 MB after gzip compression.
- Your package should depend only on hosted dependencies (from the default pub
package server) and SDK dependencies (sdk: flutter).
- You must have a Google Account,
---
## Publishing is Forever
To make sure projects don't break after using a dependency, you are unable to
take down a published project.
If you will have regrets publishing, turn back now.
---
## Initialize Project
Creating a flutter library is straightforward, simply run the command
```sh
flutter create -template=package <package_name>
```
Creates a new flutter project with a simple example library inside.
---
## Select License
This is important. The dart team recommends the BSD-3 clause license.
---
## pubspec.yaml Considerations
In the `pubspec.yaml` file, it is recommended to include a "homepage" or "repository"
field. This gets popultated into the package page on [pub.dev](https://pub.dev).
```yaml
# Top of pubspec.yaml file
name: stripe_native_card_field
description: A native flutter implementation of Stripes Card Field.
version: 0.0.1
repository: https://git.fosscat.com/nathananderson98/stripe_native_card_field
```
---
# 2. Write Your Library
---
## Important Bits
Be sure to include a `README.md` file at the root of your library as this is
what determines the content of your packages page on [pub.dev](https://pub.dev)
A `CHANGELOG.md` file can also be included and will fill out a tab on the
package's page
### Verified Publisher
You can publish under a verified domain as a "Verified Publisher". Its a bit of
a process. But it adds a cool checkmark on your package and you can hide your email.
---
## Tests
Its a good idea to include some Unit Tests and Widget Tests for your library.
---
# 3. Publishing!
---
## Dry Run
To see the results of what publishing will look like without going through with it run
```sh
dart pub publish --dry-run
```
## Helpful Links
[Dart Publishing Guide](https://dart.dev/tools/pub/publishing)
+26 -12
View File
@@ -18,13 +18,14 @@ void main() {
CardDetails? details;
final cardField = CardTextField(
width: width,
onCardDetailsComplete: (cd) => details = cd,
onValidCardDetails: (cd) => details = cd,
);
await tester.pumpWidget(baseCardFieldWidget(cardField));
final input = TestTextInput();
final cardState = tester.state(find.byType(CardTextField)) as CardTextFieldState;
final cardState =
tester.state(find.byType(CardTextField)) as CardTextFieldState;
assertEmptyTextFields(tester, cardState.isWideFormat);
@@ -47,7 +48,8 @@ void main() {
await tester.pump();
expect(getTextFormField(expirationFieldKey).controller?.text, '');
expect(getTextFormField(cardFieldKey).controller?.text, '4242 4242 4242 424');
expect(getTextFormField(cardFieldKey).controller?.text,
'4242 4242 4242 424');
expect(cardState.cardNumberFocusNode.hasFocus, true);
expect(cardState.expirationFocusNode.hasFocus, false);
// Postal code should now be gone
@@ -58,7 +60,8 @@ void main() {
input.enterText("4242424242424242");
await tester.pump();
expect(getTextFormField(cardFieldKey).controller?.text, '4242 4242 4242 4242');
expect(getTextFormField(cardFieldKey).controller?.text,
'4242 4242 4242 4242');
expect(cardState.cardNumberFocusNode.hasFocus, false);
expect(cardState.expirationFocusNode.hasFocus, true);
// Postal code should move back into view
@@ -90,7 +93,10 @@ void main() {
await tester.pump();
final expectedCardDetails = CardDetails(
cardNumber: '4242 4242 4242 4242', securityCode: '333', expirationString: '10/28', postalCode: '91555');
cardNumber: '4242 4242 4242 4242',
securityCode: '333',
expirationString: '10/28',
postalCode: '91555');
// print('${expectedCardDetails.toString()}\n${details?.toString()}');
expect(details?.hash, expectedCardDetails.hash);
},
@@ -104,13 +110,14 @@ void main() {
final cardField = CardTextField(
width: width,
onCardDetailsComplete: (cd) => details = cd,
onValidCardDetails: (cd) => details = cd,
);
await tester.pumpWidget(baseCardFieldWidget(cardField));
final input = TestTextInput();
final cardState = tester.state(find.byType(CardTextField)) as CardTextFieldState;
final cardState =
tester.state(find.byType(CardTextField)) as CardTextFieldState;
assertEmptyTextFields(tester, cardState.isWideFormat);
@@ -135,7 +142,8 @@ void main() {
input.enterText('0055');
await tester.pump();
expect(find.text("Your card's expiration month is invalid."), findsOneWidget);
expect(
find.text("Your card's expiration month is invalid."), findsOneWidget);
await tester.sendKeyDownEvent(LogicalKeyboardKey.backspace);
await tester.pump();
@@ -145,7 +153,8 @@ void main() {
input.enterText('1099');
await tester.pump();
expect(find.text("Your card's expiration year is invalid."), findsOneWidget);
expect(
find.text("Your card's expiration year is invalid."), findsOneWidget);
await tester.sendKeyDownEvent(LogicalKeyboardKey.backspace);
await tester.pump();
@@ -169,7 +178,8 @@ void main() {
await input.receiveAction(TextInputAction.done);
await tester.pump();
expect(find.text("The postal code you entered is not correct."), findsOneWidget);
expect(find.text("The postal code you entered is not correct."),
findsOneWidget);
await tester.tap(find.byType(CardTextField));
@@ -182,7 +192,10 @@ void main() {
await tester.pump();
final expectedCardDetails = CardDetails(
cardNumber: '4242 4242 4242 4242', expirationString: '02/28', securityCode: '123', postalCode: '12345');
cardNumber: '4242 4242 4242 4242',
expirationString: '02/28',
securityCode: '123',
postalCode: '12345');
expect(details?.hash, expectedCardDetails.hash);
});
@@ -209,7 +222,8 @@ void assertEmptyTextFields(WidgetTester tester, bool isWideFormat) {
// expect(find.text("Postal Code"), findsNothing);
}
Future<void> enterTextByKey(WidgetTester tester, {required String key, required String text}) async {
Future<void> enterTextByKey(WidgetTester tester,
{required String key, required String text}) async {
await tester.enterText(find.byKey(ValueKey(key)), text);
}