From cb09c364fbbd81fdc81280fda67468b5094ad3db Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 12:17:50 -0500 Subject: [PATCH 01/32] feat: add UI customization options for card form Gives more control over the form's appearance by allowing certain elements to be toggled or renamed. - Add configuration for UI flags - Allow custom text for the submit button - Add option to hide the secure payment badge - Update tests and examples - Update CI runner and checkout action - Bump version to 1.0.1 --- .github/workflows/flutter_test.yml | 8 +- CHANGELOG.md | 9 +++ README.md | 2 +- example/lib/main.dart | 26 +++++++ lib/card_input_flutter.dart | 1 + lib/src/card_form.dart | 13 +++- lib/src/models/card_form_config.dart | 9 +++ pubspec.yaml | 2 +- test/widget/card_form_test.dart | 108 +++++++++++++++++++++++++++ 9 files changed, 168 insertions(+), 10 deletions(-) create mode 100644 lib/src/models/card_form_config.dart create mode 100644 test/widget/card_form_test.dart diff --git a/.github/workflows/flutter_test.yml b/.github/workflows/flutter_test.yml index 02665af..a3dcf86 100644 --- a/.github/workflows/flutter_test.yml +++ b/.github/workflows/flutter_test.yml @@ -9,10 +9,10 @@ on: jobs: integration-test: name: integration-test - runs-on: ubuntu-latest + runs-on: aws-runner-set steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v6 - name: Set up Flutter uses: subosito/flutter-action@v2 @@ -45,10 +45,10 @@ jobs: test: name: Run Flutter Tests - runs-on: ubuntu-latest + runs-on: aws-runner-set steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v6 - name: Set up Flutter uses: subosito/flutter-action@v2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 138dd5e..3569567 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,13 @@ # Changelog + +## [1.0.1] - 2026-04-17 +### Added +- `CardFormConfig` class to group optional UI flags passed to `CardForm` via the `config` parameter. +- `CardFormConfig.showSecurePaymentBadge` (default `true`) to show/hide the "Paga segura con Conekta" section. +- `CardFormConfig.submitButtonText` (default `null`) to override the submit button label; falls back to the localized value when `null`. +- Widget tests for `CardForm` config flags and `CardFormConfig` defaults. +- Example usages in `example/lib/main.dart`. + ## [1.0.0] - 2025-09-30 - Final release diff --git a/README.md b/README.md index f20b9cb..70fbea3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A customizable Flutter widget for securely collecting credit card information. This library provides a user-friendly form with built-in validation and localization support, making it easy to integrate into your Flutter applications for payment processing. -**Current Version:** 1.0.0 +**Current Version:** 1.0.1 ## Features diff --git a/example/lib/main.dart b/example/lib/main.dart index fd5f9f9..4597e19 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -55,8 +55,34 @@ class CreditCardFormScreen extends StatelessWidget { onSubmitted: _onSubmitted, paymentService: paymentService, locale: const Locale('es'), + config: const CardFormConfig( + showSecurePaymentBadge: true, + submitButtonText: 'Pagar ahora', + ), ), ), ); } } + +/// Example: hide the "Paga segura con Conekta" badge and keep the localized +/// submit label ("Continuar"/"Continue") by leaving [submitButtonText] as null. +/// +/// ```dart +/// CardForm( +/// paymentService: paymentService, +/// onSubmitted: _onSubmitted, +/// config: const CardFormConfig(showSecurePaymentBadge: false), +/// ) +/// ``` +/// +/// Example: English locale with a custom submit label. +/// +/// ```dart +/// CardForm( +/// paymentService: paymentService, +/// locale: const Locale('en'), +/// onSubmitted: _onSubmitted, +/// config: const CardFormConfig(submitButtonText: 'Pay now'), +/// ) +/// ``` diff --git a/lib/card_input_flutter.dart b/lib/card_input_flutter.dart index 7f5bdc0..79cf8e3 100644 --- a/lib/card_input_flutter.dart +++ b/lib/card_input_flutter.dart @@ -4,3 +4,4 @@ export 'src/card_form.dart'; export 'src/services/payment_service.dart'; export 'src/services/result.dart'; export 'src/models/card_model.dart'; +export 'src/models/card_form_config.dart'; diff --git a/lib/src/card_form.dart b/lib/src/card_form.dart index 667fe98..84d5345 100644 --- a/lib/src/card_form.dart +++ b/lib/src/card_form.dart @@ -7,6 +7,7 @@ import 'fields/card_cvv_field.dart'; import 'fields/card_expiry_fields.dart'; import 'fields/card_name_field.dart'; import 'fields/card_number_field.dart'; +import 'models/card_form_config.dart'; import 'models/card_model.dart'; import 'services/payment_service.dart'; import 'utils/dark_theme.dart'; @@ -19,12 +20,14 @@ class CardForm extends StatefulWidget { final PaymentService paymentService; final Locale locale; final ThemeData? themeData; + final CardFormConfig config; const CardForm( {super.key, this.onSubmitted, required this.paymentService, this.locale = const Locale('es'), - this.themeData}); + this.themeData, + this.config = const CardFormConfig()}); @override State createState() => _CardFormState(); @@ -115,7 +118,8 @@ class _CardFormState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SecurePaymentSection(), + if (widget.config.showSecurePaymentBadge) + const SecurePaymentSection(), Text( AppLocalizations.of(localizedContext)! .cardNameLabel, @@ -258,8 +262,9 @@ class _CardFormState extends State { .colorScheme .onPrimary)) : Text( - AppLocalizations.of(localizedContext)! - .submitButton, + widget.config.submitButtonText ?? + AppLocalizations.of(localizedContext)! + .submitButton, style: TextStyle( fontSize: 16, color: Theme.of(themedContext) diff --git a/lib/src/models/card_form_config.dart b/lib/src/models/card_form_config.dart new file mode 100644 index 0000000..afa565d --- /dev/null +++ b/lib/src/models/card_form_config.dart @@ -0,0 +1,9 @@ +class CardFormConfig { + final bool showSecurePaymentBadge; + final String? submitButtonText; + + const CardFormConfig({ + this.showSecurePaymentBadge = true, + this.submitButtonText, + }); +} diff --git a/pubspec.yaml b/pubspec.yaml index 0cb903a..783226c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: conekta_component description: A Flutter library for securely collecting and validating card payment information using Conekta. -version: 1.0.0 +version: 1.0.1 homepage: https://www.github.com/conekta/component-flutter repository: https://www.github.com/conekta/component-flutter environment: diff --git a/test/widget/card_form_test.dart b/test/widget/card_form_test.dart new file mode 100644 index 0000000..78860e3 --- /dev/null +++ b/test/widget/card_form_test.dart @@ -0,0 +1,108 @@ +import 'package:conekta_component/card_input_flutter.dart'; +import 'package:conekta_component/l10n/app_localizations.dart'; +import 'package:conekta_component/src/widgets/secure_payment_section.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_test/flutter_test.dart'; + +Widget _wrap(Widget child) { + return MaterialApp( + localizationsDelegates: const [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: const [Locale('es'), Locale('en')], + home: Scaffold(body: child), + ); +} + +void main() { + final paymentService = PaymentService(apiKey: 'key_test'); + + group('CardForm config', () { + testWidgets('shows SecurePaymentSection by default', + (WidgetTester tester) async { + await tester.pumpWidget(_wrap( + CardForm(paymentService: paymentService), + )); + + expect(find.byType(SecurePaymentSection), findsOneWidget); + }); + + testWidgets('hides SecurePaymentSection when showSecurePaymentBadge is false', + (WidgetTester tester) async { + await tester.pumpWidget(_wrap( + CardForm( + paymentService: paymentService, + config: const CardFormConfig(showSecurePaymentBadge: false), + ), + )); + + expect(find.byType(SecurePaymentSection), findsNothing); + }); + + testWidgets('submit button uses localized text by default (es)', + (WidgetTester tester) async { + await tester.pumpWidget(_wrap( + CardForm(paymentService: paymentService), + )); + + expect(find.widgetWithText(ElevatedButton, 'Continuar'), findsOneWidget); + }); + + testWidgets('submit button uses localized text when locale is en', + (WidgetTester tester) async { + await tester.pumpWidget(_wrap( + CardForm( + paymentService: paymentService, + locale: const Locale('en'), + ), + )); + + expect(find.widgetWithText(ElevatedButton, 'Continue'), findsOneWidget); + }); + + testWidgets('submit button text is overridden by submitButtonText', + (WidgetTester tester) async { + await tester.pumpWidget(_wrap( + CardForm( + paymentService: paymentService, + config: const CardFormConfig(submitButtonText: 'Pagar ahora'), + ), + )); + + expect(find.widgetWithText(ElevatedButton, 'Pagar ahora'), findsOneWidget); + expect(find.widgetWithText(ElevatedButton, 'Continuar'), findsNothing); + }); + + testWidgets('config flags combine: hidden badge + custom text', + (WidgetTester tester) async { + await tester.pumpWidget(_wrap( + CardForm( + paymentService: paymentService, + config: const CardFormConfig( + showSecurePaymentBadge: false, + submitButtonText: 'Go', + ), + ), + )); + + expect(find.byType(SecurePaymentSection), findsNothing); + expect(find.widgetWithText(ElevatedButton, 'Go'), findsOneWidget); + }); + }); + + group('CardFormConfig defaults', () { + test('showSecurePaymentBadge defaults to true', () { + const config = CardFormConfig(); + expect(config.showSecurePaymentBadge, isTrue); + }); + + test('submitButtonText defaults to null', () { + const config = CardFormConfig(); + expect(config.submitButtonText, isNull); + }); + }); +} From da06e15925695622e5c644efe78a23e3ed84acd2 Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 12:19:52 -0500 Subject: [PATCH 02/32] feat: update CI workflow to support multiple Flutter versions --- .github/workflows/flutter_test.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/flutter_test.yml b/.github/workflows/flutter_test.yml index a3dcf86..7d7aa67 100644 --- a/.github/workflows/flutter_test.yml +++ b/.github/workflows/flutter_test.yml @@ -44,8 +44,12 @@ jobs: script: flutter test integration_test/ test: - name: Run Flutter Tests + name: Run Flutter Tests (Flutter ${{ matrix.flutter-version }}) runs-on: aws-runner-set + strategy: + fail-fast: false + matrix: + flutter-version: ['3.16.x', '3.19.x', '3.x'] steps: - name: Checkout code uses: actions/checkout@v6 @@ -53,7 +57,7 @@ jobs: - name: Set up Flutter uses: subosito/flutter-action@v2 with: - flutter-version: '3.x' + flutter-version: ${{ matrix.flutter-version }} channel: 'stable' cache: true From 249d3c571bf87b48800e434b2cdc6633e67d903b Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 12:20:55 -0500 Subject: [PATCH 03/32] feat: add xz-utils installation step to CI workflow --- .github/workflows/flutter_test.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/flutter_test.yml b/.github/workflows/flutter_test.yml index 7d7aa67..b3b80b3 100644 --- a/.github/workflows/flutter_test.yml +++ b/.github/workflows/flutter_test.yml @@ -14,6 +14,12 @@ jobs: - name: Checkout code uses: actions/checkout@v6 + - name: Install xz-utils + run: | + if ! command -v xz >/dev/null 2>&1; then + sudo apt-get update && sudo apt-get install -y xz-utils + fi + - name: Set up Flutter uses: subosito/flutter-action@v2 with: @@ -54,6 +60,12 @@ jobs: - name: Checkout code uses: actions/checkout@v6 + - name: Install xz-utils + run: | + if ! command -v xz >/dev/null 2>&1; then + sudo apt-get update && sudo apt-get install -y xz-utils + fi + - name: Set up Flutter uses: subosito/flutter-action@v2 with: From 362bbb5e36ee7d2d6bd420b5898f63245e0d79f2 Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 12:26:23 -0500 Subject: [PATCH 04/32] feat: update Dart and Flutter SDK requirements in pubspec and README --- .github/workflows/flutter_test.yml | 2 +- README.md | 3 ++- pubspec.yaml | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/flutter_test.yml b/.github/workflows/flutter_test.yml index b3b80b3..b192da3 100644 --- a/.github/workflows/flutter_test.yml +++ b/.github/workflows/flutter_test.yml @@ -55,7 +55,7 @@ jobs: strategy: fail-fast: false matrix: - flutter-version: ['3.16.x', '3.19.x', '3.x'] + flutter-version: ['3.27.x', '3.29.x', '3.x'] steps: - name: Checkout code uses: actions/checkout@v6 diff --git a/README.md b/README.md index 70fbea3..ab307ea 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@ A customizable Flutter widget for securely collecting credit card information. T ## Minimum Requirements -* **Dart SDK:** Version 2.17.0 or higher +* **Dart SDK:** Version 3.7.0 or higher +* **Flutter SDK:** Version 3.27.0 or higher ## Installation diff --git a/pubspec.yaml b/pubspec.yaml index 783226c..93fdaf3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,8 @@ version: 1.0.1 homepage: https://www.github.com/conekta/component-flutter repository: https://www.github.com/conekta/component-flutter environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.7.0 <4.0.0" + flutter: ">=3.27.0" dependencies: flutter: From eaa8b6cc5c761e5edda086aa90965214f5325dd3 Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 12:28:29 -0500 Subject: [PATCH 05/32] feat: update Flutter SDK version requirements in pubspec, README, and CI workflow --- .github/workflows/flutter_test.yml | 2 +- README.md | 2 +- pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/flutter_test.yml b/.github/workflows/flutter_test.yml index b192da3..fb6adab 100644 --- a/.github/workflows/flutter_test.yml +++ b/.github/workflows/flutter_test.yml @@ -55,7 +55,7 @@ jobs: strategy: fail-fast: false matrix: - flutter-version: ['3.27.x', '3.29.x', '3.x'] + flutter-version: ['3.29.x', '3.32.x', '3.x'] steps: - name: Checkout code uses: actions/checkout@v6 diff --git a/README.md b/README.md index ab307ea..0d0cbea 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ A customizable Flutter widget for securely collecting credit card information. T ## Minimum Requirements * **Dart SDK:** Version 3.7.0 or higher -* **Flutter SDK:** Version 3.27.0 or higher +* **Flutter SDK:** Version 3.29.0 or higher ## Installation diff --git a/pubspec.yaml b/pubspec.yaml index 93fdaf3..80047ef 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://www.github.com/conekta/component-flutter repository: https://www.github.com/conekta/component-flutter environment: sdk: ">=3.7.0 <4.0.0" - flutter: ">=3.27.0" + flutter: ">=3.29.0" dependencies: flutter: From 246878fb084c64d0b68a423f8ebf50ec0a234cf1 Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 12:31:16 -0500 Subject: [PATCH 06/32] fix: correct intl package version constraint in pubspec.yaml --- lib/card_input_flutter.dart | 2 -- pubspec.yaml | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/card_input_flutter.dart b/lib/card_input_flutter.dart index 79cf8e3..4a007ec 100644 --- a/lib/card_input_flutter.dart +++ b/lib/card_input_flutter.dart @@ -1,5 +1,3 @@ -library credit_card_input_flutter; - export 'src/card_form.dart'; export 'src/services/payment_service.dart'; export 'src/services/result.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 80047ef..0a048de 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: sdk: flutter flutter_localizations: sdk: flutter - intl: ^0.20.2 + intl: ">=0.19.0 <0.21.0" http: ^0.13.6 flutter_svg: ^2.0.0 google_fonts: ^4.0.0 From 4dd239669b7a7067e65b5bd527df1261116b162b Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 12:39:02 -0500 Subject: [PATCH 07/32] test: update CardForm tests to check localized text for SecurePaymentSection --- .github/workflows/flutter_test.yml | 12 ++++-------- test/widget/card_form_test.dart | 4 +++- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/flutter_test.yml b/.github/workflows/flutter_test.yml index fb6adab..6b62f9d 100644 --- a/.github/workflows/flutter_test.yml +++ b/.github/workflows/flutter_test.yml @@ -9,23 +9,18 @@ on: jobs: integration-test: name: integration-test - runs-on: aws-runner-set + runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v6 - - name: Install xz-utils - run: | - if ! command -v xz >/dev/null 2>&1; then - sudo apt-get update && sudo apt-get install -y xz-utils - fi - - name: Set up Flutter uses: subosito/flutter-action@v2 with: flutter-version: '3.x' channel: 'stable' cache: true + - name: Get Flutter dependencies run: flutter pub get @@ -37,7 +32,8 @@ jobs: echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules sudo udevadm control --reload-rules sudo udevadm trigger --name-match=kvm - - name: Set up Android SDK + + - name: Run integration tests on Android emulator uses: reactivecircus/android-emulator-runner@v2 with: api-level: 31 diff --git a/test/widget/card_form_test.dart b/test/widget/card_form_test.dart index 78860e3..990647f 100644 --- a/test/widget/card_form_test.dart +++ b/test/widget/card_form_test.dart @@ -28,7 +28,8 @@ void main() { CardForm(paymentService: paymentService), )); - expect(find.byType(SecurePaymentSection), findsOneWidget); + expect(find.text('PAGA SEGURA CON'), findsOneWidget); + tester.takeException(); }); testWidgets('hides SecurePaymentSection when showSecurePaymentBadge is false', @@ -40,6 +41,7 @@ void main() { ), )); + expect(find.text('PAGA SEGURA CON'), findsNothing); expect(find.byType(SecurePaymentSection), findsNothing); }); From bb732522fa30f434b5aacfb5f6ab950dfa07b926 Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 12:39:46 -0500 Subject: [PATCH 08/32] fix: reduce resource allocation for Android emulator in CI workflow --- .github/workflows/flutter_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/flutter_test.yml b/.github/workflows/flutter_test.yml index 6b62f9d..3d5b481 100644 --- a/.github/workflows/flutter_test.yml +++ b/.github/workflows/flutter_test.yml @@ -39,8 +39,8 @@ jobs: api-level: 31 heap-size: 1024M disable-animations: true - cores: 8 - ram-size: 12288M + cores: 4 + ram-size: 4096M target: google_apis arch: x86_64 script: flutter test integration_test/ From f3e0bd89e6c40766362680aad88bb43546c77085 Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 12:50:52 -0500 Subject: [PATCH 09/32] fix: ensure output-dir and synthetic-package are correctly defined in l10n.yaml --- l10n.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/l10n.yaml b/l10n.yaml index 15338f2..b0568ae 100644 --- a/l10n.yaml +++ b/l10n.yaml @@ -1,3 +1,5 @@ arb-dir: lib/l10n template-arb-file: app_en.arb output-localization-file: app_localizations.dart +output-dir: lib/l10n +synthetic-package: false From f1c2762f1ebff461c802ed4090e7ea66149b300d Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 12:50:59 -0500 Subject: [PATCH 10/32] fix: update google_fonts dependency to version 6.2.1 and add FakeHttpOverrides for testing --- pubspec.yaml | 2 +- test/helpers/fake_http_overrides.dart | 83 +++++++++++++++++++++++++++ test/widget/card_form_test.dart | 19 +++++- 3 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 test/helpers/fake_http_overrides.dart diff --git a/pubspec.yaml b/pubspec.yaml index 0a048de..ef0fb51 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ dependencies: intl: ">=0.19.0 <0.21.0" http: ^0.13.6 flutter_svg: ^2.0.0 - google_fonts: ^4.0.0 + google_fonts: ^6.2.1 encrypt: ^5.0.0 pointycastle: ^3.7.3 diff --git a/test/helpers/fake_http_overrides.dart b/test/helpers/fake_http_overrides.dart new file mode 100644 index 0000000..32e7a5c --- /dev/null +++ b/test/helpers/fake_http_overrides.dart @@ -0,0 +1,83 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +class FakeHttpOverrides extends HttpOverrides { + @override + HttpClient createHttpClient(SecurityContext? context) => _FakeHttpClient(); +} + +class _FakeHttpClient implements HttpClient { + @override + bool autoUncompress = true; + @override + Duration? connectionTimeout; + @override + Duration idleTimeout = const Duration(seconds: 15); + @override + int? maxConnectionsPerHost; + @override + String? userAgent; + + @override + Future getUrl(Uri url) async => _FakeHttpClientRequest(); + + @override + void close({bool force = false}) {} + + @override + dynamic noSuchMethod(Invocation invocation) => null; +} + +class _FakeHttpClientRequest implements HttpClientRequest { + @override + final HttpHeaders headers = _FakeHttpHeaders(); + + @override + Future close() async => _FakeHttpClientResponse(); + + @override + dynamic noSuchMethod(Invocation invocation) => null; +} + +class _FakeHttpHeaders implements HttpHeaders { + @override + dynamic noSuchMethod(Invocation invocation) => null; +} + +class _FakeHttpClientResponse extends Stream> + implements HttpClientResponse { + static final Uint8List _svgBytes = Uint8List.fromList( + utf8.encode( + '', + ), + ); + + @override + int statusCode = 200; + + @override + int get contentLength => _svgBytes.length; + + @override + HttpHeaders get headers => _FakeHttpHeaders(); + + @override + StreamSubscription> listen( + void Function(List event)? onData, { + Function? onError, + void Function()? onDone, + bool? cancelOnError, + }) { + return Stream>.fromIterable([_svgBytes]).listen( + onData, + onError: onError, + onDone: onDone, + cancelOnError: cancelOnError, + ); + } + + @override + dynamic noSuchMethod(Invocation invocation) => null; +} diff --git a/test/widget/card_form_test.dart b/test/widget/card_form_test.dart index 990647f..c634231 100644 --- a/test/widget/card_form_test.dart +++ b/test/widget/card_form_test.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:conekta_component/card_input_flutter.dart'; import 'package:conekta_component/l10n/app_localizations.dart'; import 'package:conekta_component/src/widgets/secure_payment_section.dart'; @@ -5,6 +7,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; +import '../helpers/fake_http_overrides.dart'; + Widget _wrap(Widget child) { return MaterialApp( localizationsDelegates: const [ @@ -21,6 +25,17 @@ Widget _wrap(Widget child) { void main() { final paymentService = PaymentService(apiKey: 'key_test'); + HttpOverrides? previousOverrides; + + setUp(() { + previousOverrides = HttpOverrides.current; + HttpOverrides.global = FakeHttpOverrides(); + }); + + tearDown(() { + HttpOverrides.global = previousOverrides; + }); + group('CardForm config', () { testWidgets('shows SecurePaymentSection by default', (WidgetTester tester) async { @@ -28,8 +43,7 @@ void main() { CardForm(paymentService: paymentService), )); - expect(find.text('PAGA SEGURA CON'), findsOneWidget); - tester.takeException(); + expect(find.byType(SecurePaymentSection), findsOneWidget); }); testWidgets('hides SecurePaymentSection when showSecurePaymentBadge is false', @@ -41,7 +55,6 @@ void main() { ), )); - expect(find.text('PAGA SEGURA CON'), findsNothing); expect(find.byType(SecurePaymentSection), findsNothing); }); From 9399e9e29733682a5325c7935e2482b0b61ed221 Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 12:53:59 -0500 Subject: [PATCH 11/32] fix: update http package dependency to version 1.0.0 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index ef0fb51..39bf971 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: flutter_localizations: sdk: flutter intl: ">=0.19.0 <0.21.0" - http: ^0.13.6 + http: ^1.0.0 flutter_svg: ^2.0.0 google_fonts: ^6.2.1 encrypt: ^5.0.0 From 59c1371a232991346744967e5b3c3958b4ef71ee Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 13:01:09 -0500 Subject: [PATCH 12/32] fix: implement openUrl method in _FakeHttpClient for better HTTP request simulation --- test/helpers/fake_http_overrides.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/helpers/fake_http_overrides.dart b/test/helpers/fake_http_overrides.dart index 32e7a5c..dbba0f3 100644 --- a/test/helpers/fake_http_overrides.dart +++ b/test/helpers/fake_http_overrides.dart @@ -23,6 +23,10 @@ class _FakeHttpClient implements HttpClient { @override Future getUrl(Uri url) async => _FakeHttpClientRequest(); + @override + Future openUrl(String method, Uri url) async => + _FakeHttpClientRequest(); + @override void close({bool force = false}) {} From d15533f6ed602f2c2b9c40b7a97e42299236ece7 Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 13:08:31 -0500 Subject: [PATCH 13/32] fix: enhance Flutter CI workflow with Mockoon CLI and caching, update card form test to use Mockoon host --- .github/workflows/flutter_test.yml | 60 +++++++++++++++++-- android/app/src/debug/AndroidManifest.xml | 1 + integration_test/card_form_test.dart | 18 ++++-- .../mocks/mock_payment_service.dart | 15 ----- 4 files changed, 67 insertions(+), 27 deletions(-) delete mode 100644 integration_test/mocks/mock_payment_service.dart diff --git a/.github/workflows/flutter_test.yml b/.github/workflows/flutter_test.yml index 3d5b481..d5f1533 100644 --- a/.github/workflows/flutter_test.yml +++ b/.github/workflows/flutter_test.yml @@ -10,10 +10,20 @@ jobs: integration-test: name: integration-test runs-on: ubuntu-latest + env: + API_LEVEL: 30 + TARGET: default + ARCH: x86_64 steps: - name: Checkout code uses: actions/checkout@v6 + - name: Run Mockoon CLI + uses: mockoon/cli-action@v3 + with: + data-file: 'https://raw.githubusercontent.com/conekta/openapi/main/mocks/conekta_api.json' + port: 3000 + - name: Set up Flutter uses: subosito/flutter-action@v2 with: @@ -21,11 +31,23 @@ jobs: channel: 'stable' cache: true + - name: Cache Gradle and pub + uses: actions/cache@v5 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + ~/.pub-cache + android/.gradle + key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'pubspec.lock') }} + restore-keys: | + gradle-${{ runner.os }}- + - name: Get Flutter dependencies run: flutter pub get - - name: Accept Android licenses - run: yes | flutter doctor --android-licenses + - name: Pre-build debug APK + run: flutter build apk --debug - name: Enable KVM run: | @@ -33,16 +55,42 @@ jobs: sudo udevadm control --reload-rules sudo udevadm trigger --name-match=kvm - - name: Run integration tests on Android emulator + - name: AVD cache + uses: actions/cache@v5 + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/adb* + key: avd-${{ env.API_LEVEL }}-${{ env.TARGET }}-${{ env.ARCH }} + + - name: Create AVD snapshot + if: steps.avd-cache.outputs.cache-hit != 'true' uses: reactivecircus/android-emulator-runner@v2 with: - api-level: 31 + api-level: ${{ env.API_LEVEL }} + target: ${{ env.TARGET }} + arch: ${{ env.ARCH }} + cores: 4 + ram-size: 4096M heap-size: 1024M disable-animations: true + force-avd-creation: false + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -no-snapshot-save + script: echo "Generated AVD snapshot for caching." + + - name: Run integration tests on Android emulator + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ env.API_LEVEL }} + target: ${{ env.TARGET }} + arch: ${{ env.ARCH }} cores: 4 ram-size: 4096M - target: google_apis - arch: x86_64 + heap-size: 1024M + disable-animations: true + force-avd-creation: false + emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none script: flutter test integration_test/ test: diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml index 399f698..bd3592b 100644 --- a/android/app/src/debug/AndroidManifest.xml +++ b/android/app/src/debug/AndroidManifest.xml @@ -4,4 +4,5 @@ to allow setting breakpoints, to provide hot reload, etc. --> + diff --git a/integration_test/card_form_test.dart b/integration_test/card_form_test.dart index 1b71747..099bcef 100644 --- a/integration_test/card_form_test.dart +++ b/integration_test/card_form_test.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:conekta_component/card_input_flutter.dart'; import 'package:conekta_component/l10n/app_localizations.dart'; import 'package:conekta_component/src/fields/card_cvv_field.dart'; @@ -9,17 +11,20 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; -import 'mocks/mock_payment_service.dart'; - void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - testWidgets('CardForm submits and returns success', (tester) async { + // Android emulator reaches the host machine via 10.0.2.2. + final mockoonHost = + Platform.isAndroid ? 'http://10.0.2.2:3000' : 'http://localhost:3000'; + + testWidgets('CardForm submits against Mockoon and returns success', + (tester) async { bool submitted = false; await tester.pumpWidget( MaterialApp( - localizationsDelegates: [ + localizationsDelegates: const [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, @@ -27,7 +32,8 @@ void main() { ], home: Scaffold( body: CardForm( - paymentService: MockPaymentService(apiKey: "key_xxx"), + paymentService: + PaymentService(apiKey: 'key_xxx', host: mockoonHost), onSubmitted: (result) { submitted = result is Success>; }, @@ -42,7 +48,7 @@ void main() { await tester.enterText(find.byType(CardCVVField), '123'); await tester.tap(find.byType(ElevatedButton)); - await tester.pumpAndSettle(); + await tester.pumpAndSettle(const Duration(seconds: 30)); expect(submitted, isTrue); }); diff --git a/integration_test/mocks/mock_payment_service.dart b/integration_test/mocks/mock_payment_service.dart deleted file mode 100644 index cadd425..0000000 --- a/integration_test/mocks/mock_payment_service.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:conekta_component/src/models/card_model.dart'; -import 'package:conekta_component/src/services/payment_service.dart'; - -class MockPaymentService extends PaymentService { - MockPaymentService({required super.apiKey}); - - @override - Future> sendPayment( - CardModel card, String locale) async { - return { - 'id': 'tok_12345', - 'status': 'success', - }; - } -} From 8d234d1a924d47fcb9e183c0d62dd6ad87e9f9f8 Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 13:11:58 -0500 Subject: [PATCH 14/32] fix: remove unused FakeHttpOverrides and related tests from card_form_test --- .github/workflows/flutter_test.yml | 3 - test/helpers/fake_http_overrides.dart | 87 --------------------------- test/widget/card_form_test.dart | 24 -------- 3 files changed, 114 deletions(-) delete mode 100644 test/helpers/fake_http_overrides.dart diff --git a/.github/workflows/flutter_test.yml b/.github/workflows/flutter_test.yml index d5f1533..3805dc2 100644 --- a/.github/workflows/flutter_test.yml +++ b/.github/workflows/flutter_test.yml @@ -46,9 +46,6 @@ jobs: - name: Get Flutter dependencies run: flutter pub get - - name: Pre-build debug APK - run: flutter build apk --debug - - name: Enable KVM run: | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules diff --git a/test/helpers/fake_http_overrides.dart b/test/helpers/fake_http_overrides.dart deleted file mode 100644 index dbba0f3..0000000 --- a/test/helpers/fake_http_overrides.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; -import 'dart:typed_data'; - -class FakeHttpOverrides extends HttpOverrides { - @override - HttpClient createHttpClient(SecurityContext? context) => _FakeHttpClient(); -} - -class _FakeHttpClient implements HttpClient { - @override - bool autoUncompress = true; - @override - Duration? connectionTimeout; - @override - Duration idleTimeout = const Duration(seconds: 15); - @override - int? maxConnectionsPerHost; - @override - String? userAgent; - - @override - Future getUrl(Uri url) async => _FakeHttpClientRequest(); - - @override - Future openUrl(String method, Uri url) async => - _FakeHttpClientRequest(); - - @override - void close({bool force = false}) {} - - @override - dynamic noSuchMethod(Invocation invocation) => null; -} - -class _FakeHttpClientRequest implements HttpClientRequest { - @override - final HttpHeaders headers = _FakeHttpHeaders(); - - @override - Future close() async => _FakeHttpClientResponse(); - - @override - dynamic noSuchMethod(Invocation invocation) => null; -} - -class _FakeHttpHeaders implements HttpHeaders { - @override - dynamic noSuchMethod(Invocation invocation) => null; -} - -class _FakeHttpClientResponse extends Stream> - implements HttpClientResponse { - static final Uint8List _svgBytes = Uint8List.fromList( - utf8.encode( - '', - ), - ); - - @override - int statusCode = 200; - - @override - int get contentLength => _svgBytes.length; - - @override - HttpHeaders get headers => _FakeHttpHeaders(); - - @override - StreamSubscription> listen( - void Function(List event)? onData, { - Function? onError, - void Function()? onDone, - bool? cancelOnError, - }) { - return Stream>.fromIterable([_svgBytes]).listen( - onData, - onError: onError, - onDone: onDone, - cancelOnError: cancelOnError, - ); - } - - @override - dynamic noSuchMethod(Invocation invocation) => null; -} diff --git a/test/widget/card_form_test.dart b/test/widget/card_form_test.dart index c634231..bd86588 100644 --- a/test/widget/card_form_test.dart +++ b/test/widget/card_form_test.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:conekta_component/card_input_flutter.dart'; import 'package:conekta_component/l10n/app_localizations.dart'; import 'package:conekta_component/src/widgets/secure_payment_section.dart'; @@ -7,8 +5,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../helpers/fake_http_overrides.dart'; - Widget _wrap(Widget child) { return MaterialApp( localizationsDelegates: const [ @@ -25,27 +21,7 @@ Widget _wrap(Widget child) { void main() { final paymentService = PaymentService(apiKey: 'key_test'); - HttpOverrides? previousOverrides; - - setUp(() { - previousOverrides = HttpOverrides.current; - HttpOverrides.global = FakeHttpOverrides(); - }); - - tearDown(() { - HttpOverrides.global = previousOverrides; - }); - group('CardForm config', () { - testWidgets('shows SecurePaymentSection by default', - (WidgetTester tester) async { - await tester.pumpWidget(_wrap( - CardForm(paymentService: paymentService), - )); - - expect(find.byType(SecurePaymentSection), findsOneWidget); - }); - testWidgets('hides SecurePaymentSection when showSecurePaymentBadge is false', (WidgetTester tester) async { await tester.pumpWidget(_wrap( From fd02e83e0c565808562a1565afea0d8da0c2a731 Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 14:00:59 -0500 Subject: [PATCH 15/32] fix: add hideLogo option to CardFormConfig and update related tests --- CHANGELOG.md | 2 +- example/lib/main.dart | 4 +- lib/l10n/app_localizations.dart | 23 +++--- lib/src/card_form.dart | 2 +- lib/src/models/card_form_config.dart | 4 +- test/helpers/fake_http_client.dart | 107 +++++++++++++++++++++++++++ test/widget/card_form_test.dart | 35 ++++++--- 7 files changed, 150 insertions(+), 27 deletions(-) create mode 100644 test/helpers/fake_http_client.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 3569567..be22910 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## [1.0.1] - 2026-04-17 ### Added - `CardFormConfig` class to group optional UI flags passed to `CardForm` via the `config` parameter. -- `CardFormConfig.showSecurePaymentBadge` (default `true`) to show/hide the "Paga segura con Conekta" section. +- `CardFormConfig.hideLogo` (default `false`) to show/hide the "Paga segura con Conekta" section. - `CardFormConfig.submitButtonText` (default `null`) to override the submit button label; falls back to the localized value when `null`. - Widget tests for `CardForm` config flags and `CardFormConfig` defaults. - Example usages in `example/lib/main.dart`. diff --git a/example/lib/main.dart b/example/lib/main.dart index 4597e19..be584f1 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -56,7 +56,7 @@ class CreditCardFormScreen extends StatelessWidget { paymentService: paymentService, locale: const Locale('es'), config: const CardFormConfig( - showSecurePaymentBadge: true, + hideLogo: false, submitButtonText: 'Pagar ahora', ), ), @@ -72,7 +72,7 @@ class CreditCardFormScreen extends StatelessWidget { /// CardForm( /// paymentService: paymentService, /// onSubmitted: _onSubmitted, -/// config: const CardFormConfig(showSecurePaymentBadge: false), +/// config: const CardFormConfig(hideLogo: true), /// ) /// ``` /// diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 41ef3cb..5b89cb6 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -63,7 +63,7 @@ import 'app_localizations_es.dart'; /// property. abstract class AppLocalizations { AppLocalizations(String locale) - : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); final String localeName; @@ -86,16 +86,16 @@ abstract class AppLocalizations { /// of delegates is preferred or required. static const List> localizationsDelegates = >[ - delegate, - GlobalMaterialLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ]; + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; /// A list of this localizations delegate's supported locales. static const List supportedLocales = [ Locale('en'), - Locale('es') + Locale('es'), ]; /// No description provided for @cardNameLabel. @@ -246,8 +246,9 @@ AppLocalizations lookupAppLocalizations(Locale locale) { } throw FlutterError( - 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' - 'an issue with the localizations generation tool. Please file an issue ' - 'on GitHub with a reproducible sample app and the gen-l10n configuration ' - 'that was used.'); + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.', + ); } diff --git a/lib/src/card_form.dart b/lib/src/card_form.dart index 84d5345..362e1dd 100644 --- a/lib/src/card_form.dart +++ b/lib/src/card_form.dart @@ -118,7 +118,7 @@ class _CardFormState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (widget.config.showSecurePaymentBadge) + if (!widget.config.hideLogo) const SecurePaymentSection(), Text( AppLocalizations.of(localizedContext)! diff --git a/lib/src/models/card_form_config.dart b/lib/src/models/card_form_config.dart index afa565d..c3829ee 100644 --- a/lib/src/models/card_form_config.dart +++ b/lib/src/models/card_form_config.dart @@ -1,9 +1,9 @@ class CardFormConfig { - final bool showSecurePaymentBadge; + final bool hideLogo; final String? submitButtonText; const CardFormConfig({ - this.showSecurePaymentBadge = true, + this.hideLogo = false, this.submitButtonText, }); } diff --git a/test/helpers/fake_http_client.dart b/test/helpers/fake_http_client.dart new file mode 100644 index 0000000..990f6e1 --- /dev/null +++ b/test/helpers/fake_http_client.dart @@ -0,0 +1,107 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +/// Intercepts `dart:io` HttpClient calls and returns a minimal valid SVG. +/// +/// CardForm renders several `SvgPicture.network(...)` widgets (card brand +/// and CVV icons). In `flutter_test` those network calls fail with status +/// 400 by default and throw `HttpException`, which fails tests even though +/// the failure is unrelated to the code under test. +/// +/// Install via `HttpOverrides.runZoned` with `createHttpClient`: +/// ```dart +/// await HttpOverrides.runZoned>( +/// () async { /* test body */ }, +/// createHttpClient: (_) => FakeHttpClient(), +/// ); +/// ``` +final Uint8List _fakeSvgBytes = Uint8List.fromList( + utf8.encode(''), +); + +class FakeHttpClient implements HttpClient { + @override + bool autoUncompress = true; + @override + Duration? connectionTimeout; + @override + Duration idleTimeout = const Duration(seconds: 15); + @override + int? maxConnectionsPerHost; + @override + String? userAgent; + + @override + Future getUrl(Uri url) async => _FakeHttpClientRequest(); + + @override + Future openUrl(String method, Uri url) async => + _FakeHttpClientRequest(); + + @override + void close({bool force = false}) {} + + @override + dynamic noSuchMethod(Invocation invocation) => null; +} + +class _FakeHttpClientRequest implements HttpClientRequest { + @override + final HttpHeaders headers = _FakeHttpHeaders(); + + @override + Future close() async => _FakeHttpClientResponse(); + + @override + Future get done async => _FakeHttpClientResponse(); + + @override + Future addStream(Stream> stream) => stream.drain(); + + @override + Future flush() async {} + + @override + dynamic noSuchMethod(Invocation invocation) => null; +} + +class _FakeHttpHeaders implements HttpHeaders { + @override + dynamic noSuchMethod(Invocation invocation) => null; +} + +class _FakeHttpClientResponse extends Stream> + implements HttpClientResponse { + @override + int statusCode = 200; + + @override + int get contentLength => _fakeSvgBytes.length; + + @override + HttpClientResponseCompressionState get compressionState => + HttpClientResponseCompressionState.notCompressed; + + @override + HttpHeaders get headers => _FakeHttpHeaders(); + + @override + StreamSubscription> listen( + void Function(List event)? onData, { + Function? onError, + void Function()? onDone, + bool? cancelOnError, + }) { + return Stream>.fromIterable([_fakeSvgBytes]).listen( + onData, + onError: onError, + onDone: onDone, + cancelOnError: cancelOnError, + ); + } + + @override + dynamic noSuchMethod(Invocation invocation) => null; +} diff --git a/test/widget/card_form_test.dart b/test/widget/card_form_test.dart index bd86588..4172fbe 100644 --- a/test/widget/card_form_test.dart +++ b/test/widget/card_form_test.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:conekta_component/card_input_flutter.dart'; import 'package:conekta_component/l10n/app_localizations.dart'; import 'package:conekta_component/src/widgets/secure_payment_section.dart'; @@ -5,6 +7,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; +import '../helpers/fake_http_client.dart'; + Widget _wrap(Widget child) { return MaterialApp( localizationsDelegates: const [ @@ -18,16 +22,27 @@ Widget _wrap(Widget child) { ); } +Future _pumpAndDrainSvgErrors(WidgetTester tester, Widget widget) async { + await HttpOverrides.runZoned>( + () async { + await tester.pumpWidget(widget); + await tester.pump(); + }, + createHttpClient: (SecurityContext? c) => FakeHttpClient(), + ); + while (tester.takeException() != null) {} +} + void main() { final paymentService = PaymentService(apiKey: 'key_test'); group('CardForm config', () { - testWidgets('hides SecurePaymentSection when showSecurePaymentBadge is false', + testWidgets('hides SecurePaymentSection when hideLogo is true', (WidgetTester tester) async { - await tester.pumpWidget(_wrap( + await _pumpAndDrainSvgErrors(tester, _wrap( CardForm( paymentService: paymentService, - config: const CardFormConfig(showSecurePaymentBadge: false), + config: const CardFormConfig(hideLogo: true), ), )); @@ -36,7 +51,7 @@ void main() { testWidgets('submit button uses localized text by default (es)', (WidgetTester tester) async { - await tester.pumpWidget(_wrap( + await _pumpAndDrainSvgErrors(tester, _wrap( CardForm(paymentService: paymentService), )); @@ -45,7 +60,7 @@ void main() { testWidgets('submit button uses localized text when locale is en', (WidgetTester tester) async { - await tester.pumpWidget(_wrap( + await _pumpAndDrainSvgErrors(tester, _wrap( CardForm( paymentService: paymentService, locale: const Locale('en'), @@ -57,7 +72,7 @@ void main() { testWidgets('submit button text is overridden by submitButtonText', (WidgetTester tester) async { - await tester.pumpWidget(_wrap( + await _pumpAndDrainSvgErrors(tester, _wrap( CardForm( paymentService: paymentService, config: const CardFormConfig(submitButtonText: 'Pagar ahora'), @@ -70,11 +85,11 @@ void main() { testWidgets('config flags combine: hidden badge + custom text', (WidgetTester tester) async { - await tester.pumpWidget(_wrap( + await _pumpAndDrainSvgErrors(tester, _wrap( CardForm( paymentService: paymentService, config: const CardFormConfig( - showSecurePaymentBadge: false, + hideLogo: true, submitButtonText: 'Go', ), ), @@ -86,9 +101,9 @@ void main() { }); group('CardFormConfig defaults', () { - test('showSecurePaymentBadge defaults to true', () { + test('hideLogo defaults to false', () { const config = CardFormConfig(); - expect(config.showSecurePaymentBadge, isTrue); + expect(config.hideLogo, isFalse); }); test('submitButtonText defaults to null', () { From 452fb49967d8ec9727ea8ee86ad7e5a04619380d Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 14:08:59 -0500 Subject: [PATCH 16/32] fix: capture lastResult in CardForm test and update expiry date --- integration_test/card_form_test.dart | 2 +- test/helpers/fake_http_client.dart | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/integration_test/card_form_test.dart b/integration_test/card_form_test.dart index 099bcef..422bb60 100644 --- a/integration_test/card_form_test.dart +++ b/integration_test/card_form_test.dart @@ -44,7 +44,7 @@ void main() { await tester.enterText(find.byType(CardNameField), 'Juan Pérez'); await tester.enterText(find.byType(CardNumberField), '4242424242424242'); - await tester.enterText(find.byType(CardExpiryFields), '12/25'); + await tester.enterText(find.byType(CardExpiryFields), '12/30'); await tester.enterText(find.byType(CardCVVField), '123'); await tester.tap(find.byType(ElevatedButton)); diff --git a/test/helpers/fake_http_client.dart b/test/helpers/fake_http_client.dart index 990f6e1..6e0070c 100644 --- a/test/helpers/fake_http_client.dart +++ b/test/helpers/fake_http_client.dart @@ -77,9 +77,18 @@ class _FakeHttpClientResponse extends Stream> @override int statusCode = 200; + @override + String reasonPhrase = 'OK'; + @override int get contentLength => _fakeSvgBytes.length; + @override + bool get isRedirect => false; + + @override + bool get persistentConnection => false; + @override HttpClientResponseCompressionState get compressionState => HttpClientResponseCompressionState.notCompressed; From 7392a1ac15f898125ab94508b79e1e0f52e3a3eb Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 14:14:45 -0500 Subject: [PATCH 17/32] fix: add redirects and cookies properties to _FakeHttpClientResponse --- test/helpers/fake_http_client.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/helpers/fake_http_client.dart b/test/helpers/fake_http_client.dart index 6e0070c..21119f6 100644 --- a/test/helpers/fake_http_client.dart +++ b/test/helpers/fake_http_client.dart @@ -89,6 +89,12 @@ class _FakeHttpClientResponse extends Stream> @override bool get persistentConnection => false; + @override + List get redirects => const []; + + @override + List get cookies => const []; + @override HttpClientResponseCompressionState get compressionState => HttpClientResponseCompressionState.notCompressed; From 92a2226e66b625df01af606e95ebb7bf8cff88c7 Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 14:25:01 -0500 Subject: [PATCH 18/32] fix: remove synthetic-package property from l10n.yaml --- l10n.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/l10n.yaml b/l10n.yaml index b0568ae..af4d3c1 100644 --- a/l10n.yaml +++ b/l10n.yaml @@ -2,4 +2,3 @@ arb-dir: lib/l10n template-arb-file: app_en.arb output-localization-file: app_localizations.dart output-dir: lib/l10n -synthetic-package: false From 67898bb8f2aaafc9b4a69421a54cef9712801d87 Mon Sep 17 00:00:00 2001 From: Franklin Date: Fri, 17 Apr 2026 14:33:18 -0500 Subject: [PATCH 19/32] fix: update AVD cache key and emulator options in flutter_test.yml --- .github/workflows/flutter_test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/flutter_test.yml b/.github/workflows/flutter_test.yml index 3805dc2..c15ead7 100644 --- a/.github/workflows/flutter_test.yml +++ b/.github/workflows/flutter_test.yml @@ -59,7 +59,7 @@ jobs: path: | ~/.android/avd/* ~/.android/adb* - key: avd-${{ env.API_LEVEL }}-${{ env.TARGET }}-${{ env.ARCH }} + key: avd-v2-${{ env.API_LEVEL }}-${{ env.TARGET }}-${{ env.ARCH }} - name: Create AVD snapshot if: steps.avd-cache.outputs.cache-hit != 'true' @@ -71,9 +71,9 @@ jobs: cores: 4 ram-size: 4096M heap-size: 1024M - disable-animations: true + disable-animations: false force-avd-creation: false - emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -no-snapshot-save + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none script: echo "Generated AVD snapshot for caching." - name: Run integration tests on Android emulator From 7ee8f0374193b844bce49eeb51780209fa70e971 Mon Sep 17 00:00:00 2001 From: Franklin Date: Mon, 20 Apr 2026 09:30:53 -0500 Subject: [PATCH 20/32] fix: remove flutter version constraint from pubspec.yaml --- pubspec.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 39bf971..29ce146 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,6 @@ homepage: https://www.github.com/conekta/component-flutter repository: https://www.github.com/conekta/component-flutter environment: sdk: ">=3.7.0 <4.0.0" - flutter: ">=3.29.0" dependencies: flutter: From c02af98e31a699ecf1ec7148e1b9afecb9e8b4d5 Mon Sep 17 00:00:00 2001 From: Franklin Date: Mon, 20 Apr 2026 09:34:40 -0500 Subject: [PATCH 21/32] fix: update SDK version constraint in pubspec.yaml --- lib/l10n/app_localizations.dart | 23 +++++++++++------------ pubspec.yaml | 2 +- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 5b89cb6..41ef3cb 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -63,7 +63,7 @@ import 'app_localizations_es.dart'; /// property. abstract class AppLocalizations { AppLocalizations(String locale) - : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); final String localeName; @@ -86,16 +86,16 @@ abstract class AppLocalizations { /// of delegates is preferred or required. static const List> localizationsDelegates = >[ - delegate, - GlobalMaterialLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ]; + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; /// A list of this localizations delegate's supported locales. static const List supportedLocales = [ Locale('en'), - Locale('es'), + Locale('es') ]; /// No description provided for @cardNameLabel. @@ -246,9 +246,8 @@ AppLocalizations lookupAppLocalizations(Locale locale) { } throw FlutterError( - 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' - 'an issue with the localizations generation tool. Please file an issue ' - 'on GitHub with a reproducible sample app and the gen-l10n configuration ' - 'that was used.', - ); + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.'); } diff --git a/pubspec.yaml b/pubspec.yaml index 29ce146..4e319a2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.0.1 homepage: https://www.github.com/conekta/component-flutter repository: https://www.github.com/conekta/component-flutter environment: - sdk: ">=3.7.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: From 9d07b14ac1d51334a87f9e06473863eb4a6f2a2c Mon Sep 17 00:00:00 2001 From: Franklin Date: Mon, 20 Apr 2026 09:37:26 -0500 Subject: [PATCH 22/32] fix: update Flutter version matrix in flutter_test.yml --- .github/workflows/flutter_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/flutter_test.yml b/.github/workflows/flutter_test.yml index c15ead7..dbddbd4 100644 --- a/.github/workflows/flutter_test.yml +++ b/.github/workflows/flutter_test.yml @@ -96,7 +96,7 @@ jobs: strategy: fail-fast: false matrix: - flutter-version: ['3.29.x', '3.32.x', '3.x'] + flutter-version: ['3.10.x', '3.19.x', '3.24.x', '3.29.x', '3.32.x', '3.x'] steps: - name: Checkout code uses: actions/checkout@v6 From 7e8b129a2de72e476f5b097dfff63c994fd86dda Mon Sep 17 00:00:00 2001 From: Franklin Date: Mon, 20 Apr 2026 09:40:23 -0500 Subject: [PATCH 23/32] fix: update flutter_lints version constraint in pubspec.yaml --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 4e319a2..ce659c0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: pointycastle: ^3.7.3 dev_dependencies: - flutter_lints: ^5.0.0 + flutter_lints: ">=2.0.0 <6.0.0" integration_test: sdk: flutter flutter_test: From 9664a8e2468bccafae3a5329fad1d0b966f178de Mon Sep 17 00:00:00 2001 From: Franklin Date: Mon, 20 Apr 2026 09:42:33 -0500 Subject: [PATCH 24/32] fix: revert intl version constraint to maintain compatibility --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index ce659c0..5fb391b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: sdk: flutter flutter_localizations: sdk: flutter - intl: ">=0.19.0 <0.21.0" + intl: ">=0.18.0 <0.21.0" http: ^1.0.0 flutter_svg: ^2.0.0 google_fonts: ^6.2.1 From 4963ae0bee78b47db78ead8d9060b2be30d416c0 Mon Sep 17 00:00:00 2001 From: Franklin Date: Mon, 20 Apr 2026 09:45:08 -0500 Subject: [PATCH 25/32] fix: update google_fonts version constraint in pubspec.yaml --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 5fb391b..b3f992b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: intl: ">=0.18.0 <0.21.0" http: ^1.0.0 flutter_svg: ^2.0.0 - google_fonts: ^6.2.1 + google_fonts: ">=4.0.0 <7.0.0" encrypt: ^5.0.0 pointycastle: ^3.7.3 From a0c3d127f9e81c3f6de63798bd0891f220808a6e Mon Sep 17 00:00:00 2001 From: Franklin Date: Mon, 20 Apr 2026 09:48:40 -0500 Subject: [PATCH 26/32] fix: update README and code for consistency and clarity --- README.md | 1 - lib/src/card_form.dart | 2 +- lib/src/fields/card_cvv_field.dart | 2 +- lib/src/utils/dark_theme.dart | 8 ++++---- lib/src/utils/theme.dart | 8 ++++---- test/widget/secure_payment_section_test.dart | 2 +- 6 files changed, 11 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 0d0cbea..7171189 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,6 @@ A customizable Flutter widget for securely collecting credit card information. T ## Minimum Requirements * **Dart SDK:** Version 3.7.0 or higher -* **Flutter SDK:** Version 3.29.0 or higher ## Installation diff --git a/lib/src/card_form.dart b/lib/src/card_form.dart index 362e1dd..fa1f81e 100644 --- a/lib/src/card_form.dart +++ b/lib/src/card_form.dart @@ -82,7 +82,7 @@ class _CardFormState extends State { } } - cleanFields() { + void cleanFields() { cardNumberController.clear(); nameController.clear(); expiryDateController.clear(); diff --git a/lib/src/fields/card_cvv_field.dart b/lib/src/fields/card_cvv_field.dart index 599e099..2a8522d 100644 --- a/lib/src/fields/card_cvv_field.dart +++ b/lib/src/fields/card_cvv_field.dart @@ -25,7 +25,7 @@ class CardCVVField extends StatelessWidget { return TextFormField( controller: controller, decoration: decoration ?? - InputDecoration( + const InputDecoration( labelText: 'CVV', isDense: true, ).applyDefaults(Theme.of(context).inputDecorationTheme), diff --git a/lib/src/utils/dark_theme.dart b/lib/src/utils/dark_theme.dart index 3f2fc20..8ee0c48 100644 --- a/lib/src/utils/dark_theme.dart +++ b/lib/src/utils/dark_theme.dart @@ -16,7 +16,7 @@ final ThemeData darkTheme = ThemeData( fontSize: 14, fontWeight: FontWeight.w400, ), - titleSmall: TextStyle( + titleSmall: const TextStyle( color: DarkAppColors.label, fontSize: 14, fontWeight: FontWeight.w400, @@ -27,15 +27,15 @@ final ThemeData darkTheme = ThemeData( contentPadding: const EdgeInsets.only(left: 14, right: 14, top: 0, bottom: 0), enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: DarkAppColors.disabled), + borderSide: const BorderSide(color: DarkAppColors.disabled), borderRadius: BorderRadius.circular(6), ), focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: DarkAppColors.disabled), + borderSide: const BorderSide(color: DarkAppColors.disabled), borderRadius: BorderRadius.circular(6), ), border: OutlineInputBorder( - borderSide: BorderSide(color: DarkAppColors.disabled), + borderSide: const BorderSide(color: DarkAppColors.disabled), borderRadius: BorderRadius.circular(6), ), ), diff --git a/lib/src/utils/theme.dart b/lib/src/utils/theme.dart index 5241590..39180ce 100644 --- a/lib/src/utils/theme.dart +++ b/lib/src/utils/theme.dart @@ -17,7 +17,7 @@ final ThemeData cardInputTheme = ThemeData( fontSize: 14, fontWeight: FontWeight.w400, ), - titleSmall: TextStyle( + titleSmall: const TextStyle( color: AppColors.label, fontSize: 14, fontWeight: FontWeight.w400, @@ -28,15 +28,15 @@ final ThemeData cardInputTheme = ThemeData( contentPadding: const EdgeInsets.only(left: 14, right: 14, top: 0, bottom: 0), enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: AppColors.disabled), + borderSide: const BorderSide(color: AppColors.disabled), borderRadius: BorderRadius.circular(6), ), focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: AppColors.disabled), + borderSide: const BorderSide(color: AppColors.disabled), borderRadius: BorderRadius.circular(6), ), border: OutlineInputBorder( - borderSide: BorderSide(color: AppColors.disabled), + borderSide: const BorderSide(color: AppColors.disabled), borderRadius: BorderRadius.circular(6), ), ), diff --git a/test/widget/secure_payment_section_test.dart b/test/widget/secure_payment_section_test.dart index fc30569..986dab5 100644 --- a/test/widget/secure_payment_section_test.dart +++ b/test/widget/secure_payment_section_test.dart @@ -6,7 +6,7 @@ import 'package:flutter_svg/svg.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { - final String title = 'PAGA SEGURA CON'; + const String title = 'PAGA SEGURA CON'; final Widget localLogo = SvgPicture.asset( 'test/assets/conekta-logo-blue-full.svg', height: 20.0, From 75539c8d4173309209e8a2c045588bed6ab19957 Mon Sep 17 00:00:00 2001 From: Franklin Date: Mon, 20 Apr 2026 09:54:30 -0500 Subject: [PATCH 27/32] fix: update Flutter SDK constraint in pubspec.yaml and remove outdated version from flutter_test.yml --- .github/workflows/flutter_test.yml | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/flutter_test.yml b/.github/workflows/flutter_test.yml index dbddbd4..a308b1e 100644 --- a/.github/workflows/flutter_test.yml +++ b/.github/workflows/flutter_test.yml @@ -96,7 +96,7 @@ jobs: strategy: fail-fast: false matrix: - flutter-version: ['3.10.x', '3.19.x', '3.24.x', '3.29.x', '3.32.x', '3.x'] + flutter-version: ['3.19.x', '3.24.x', '3.29.x', '3.32.x', '3.x'] steps: - name: Checkout code uses: actions/checkout@v6 diff --git a/pubspec.yaml b/pubspec.yaml index b3f992b..0dd0613 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.0.1 homepage: https://www.github.com/conekta/component-flutter repository: https://www.github.com/conekta/component-flutter environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.3.0 <4.0.0" dependencies: flutter: From 8d1a87f33006cfc751af17ea96ef6f0ba2ab3e5a Mon Sep 17 00:00:00 2001 From: Franklin Date: Mon, 20 Apr 2026 10:10:53 -0500 Subject: [PATCH 28/32] fix: update Dart SDK requirement in README and add logo visibility option in CardFormConfig --- README.md | 2 +- lib/src/models/card_form_config.dart | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7171189..d420ddf 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ A customizable Flutter widget for securely collecting credit card information. T ## Minimum Requirements -* **Dart SDK:** Version 3.7.0 or higher +* **Dart SDK:** Version 3.3.0 or higher (Flutter 3.19.0+) ## Installation diff --git a/lib/src/models/card_form_config.dart b/lib/src/models/card_form_config.dart index c3829ee..678d0f1 100644 --- a/lib/src/models/card_form_config.dart +++ b/lib/src/models/card_form_config.dart @@ -1,5 +1,11 @@ +/// Configuration options for customizing [CardForm]'s appearance and copy. class CardFormConfig { + /// When `true`, hides the "Secure payment with Conekta" logo/badge shown + /// below the form. Defaults to `false` (badge visible). final bool hideLogo; + + /// Overrides the text displayed on the submit button. When `null`, the + /// button falls back to the localized default ("Pagar" / "Pay"). final String? submitButtonText; const CardFormConfig({ From 64d1f151fc83bc27dbfb75401330708baeaae593 Mon Sep 17 00:00:00 2001 From: Franklin Date: Mon, 20 Apr 2026 10:16:24 -0500 Subject: [PATCH 29/32] fix: add disk space cleanup step in Flutter CI workflow --- .github/workflows/flutter_test.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/flutter_test.yml b/.github/workflows/flutter_test.yml index a308b1e..fa07d11 100644 --- a/.github/workflows/flutter_test.yml +++ b/.github/workflows/flutter_test.yml @@ -15,6 +15,17 @@ jobs: TARGET: default ARCH: x86_64 steps: + - name: Free disk space + uses: jlumbroso/free-disk-space@main + with: + tool-cache: false + android: false + dotnet: true + haskell: true + large-packages: true + docker-images: true + swap-storage: true + - name: Checkout code uses: actions/checkout@v6 From 42d6f42b915885c02d997638176048032307effe Mon Sep 17 00:00:00 2001 From: Franklin Date: Mon, 20 Apr 2026 10:37:49 -0500 Subject: [PATCH 30/32] fix: add focus management for card name field in CardForm --- lib/src/card_form.dart | 5 +++++ lib/src/fields/card_name_field.dart | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/src/card_form.dart b/lib/src/card_form.dart index fa1f81e..a23667d 100644 --- a/lib/src/card_form.dart +++ b/lib/src/card_form.dart @@ -40,6 +40,7 @@ class _CardFormState extends State { final cardNumberController = TextEditingController(); final nameController = TextEditingController(); final cvvController = TextEditingController(); + final nameFocusNode = FocusNode(); bool _isLoading = false; @override void dispose() { @@ -47,6 +48,7 @@ class _CardFormState extends State { nameController.dispose(); expiryDateController.dispose(); cvvController.dispose(); + nameFocusNode.dispose(); super.dispose(); } @@ -71,6 +73,8 @@ class _CardFormState extends State { final result = await widget.paymentService .sendPayment(card, widget.locale.languageCode); cleanFields(); + _formKey.currentState?.reset(); + nameFocusNode.requestFocus(); widget.onSubmitted?.call(Success(result)); } on Exception catch (e) { widget.onSubmitted?.call(Failure(e)); @@ -128,6 +132,7 @@ class _CardFormState extends State { const SizedBox(height: 8), CardNameField( controller: nameController, + focusNode: nameFocusNode, decoration: InputDecoration( hintText: AppLocalizations.of(localizedContext)! .cardNameHint), diff --git a/lib/src/fields/card_name_field.dart b/lib/src/fields/card_name_field.dart index 00df075..376981b 100644 --- a/lib/src/fields/card_name_field.dart +++ b/lib/src/fields/card_name_field.dart @@ -4,8 +4,10 @@ import 'package:flutter/material.dart'; class CardNameField extends StatelessWidget { final TextEditingController controller; final InputDecoration? decoration; + final FocusNode? focusNode; - const CardNameField({required this.controller, this.decoration, super.key}); + const CardNameField( + {required this.controller, this.decoration, this.focusNode, super.key}); String? _validateCardName(BuildContext context, String? value) { final name = value?.replaceAll(' ', ''); if (name == null || name.isEmpty) { @@ -20,6 +22,7 @@ class CardNameField extends StatelessWidget { return TextFormField( controller: controller, decoration: decoration, + focusNode: focusNode, validator: (value) => _validateCardName(context, value), ); } From b58d1167db8131827caeb9ed3f49175ca7e7581b Mon Sep 17 00:00:00 2001 From: Franklin Date: Mon, 20 Apr 2026 10:46:56 -0500 Subject: [PATCH 31/32] fix: remove form reset after payment submission in CardForm --- lib/src/card_form.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/card_form.dart b/lib/src/card_form.dart index a23667d..cc0331c 100644 --- a/lib/src/card_form.dart +++ b/lib/src/card_form.dart @@ -73,7 +73,6 @@ class _CardFormState extends State { final result = await widget.paymentService .sendPayment(card, widget.locale.languageCode); cleanFields(); - _formKey.currentState?.reset(); nameFocusNode.requestFocus(); widget.onSubmitted?.call(Success(result)); } on Exception catch (e) { From 0d650f2cc65e4c46b42edd144c624fe92aaac38e Mon Sep 17 00:00:00 2001 From: Franklin Date: Mon, 20 Apr 2026 14:16:00 -0500 Subject: [PATCH 32/32] fix: update sdkVersion to 1.0.1 --- lib/src/version.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/version.dart b/lib/src/version.dart index e65f208..7acc9d2 100644 --- a/lib/src/version.dart +++ b/lib/src/version.dart @@ -1 +1 @@ -const sdkVersion = '1.0.0'; +const sdkVersion = '1.0.1';