// Automatic FlutterFlow imports
import '/backend/backend.dart';
import '/backend/schema/structs/index.dart';
import '/actions/actions.dart' as action_blocks;
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/widgets/index.dart'; // Imports other custom widgets
import '/custom_code/actions/index.dart'; // Imports custom actions
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom widget code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!
// Import necessary packages
import 'package:flutter/services.dart';
// Begin widget code
class CreditCardForm extends StatefulWidget {
const CreditCardForm({
Key? key,
this.width,
this.height,
}) : super(key: key);
final double? width;
final double? height;
@override
State<CreditCardForm> createState() => _CreditCardFormState();
}
class _CreditCardFormState extends State<CreditCardForm> {
final _formKey = GlobalKey<FormState>();
final _cardNumberController = TextEditingController();
final _expiryDateController = TextEditingController();
final _securityCodeController = TextEditingController();
String? _cardNumberError;
String? _expiryDateError;
String? _cvvError;
void _validateField(String fieldName) {
setState(() {
switch (fieldName) {
case 'cardNumber':
_cardNumberError = _validateCardNumber(_cardNumberController.text);
break;
case 'expiryDate':
_expiryDateError = _validateExpiryDate(_expiryDateController.text);
break;
case 'cvv':
_cvvError = _validateCVV(_securityCodeController.text);
break;
}
});
}
String? _validateCardNumber(String? value) {
if (value == null || value.isEmpty) {
return 'Please enter a card number';
}
if (value.replaceAll(' ', '').length != 16) {
return 'Please enter a valid 16-digit card number';
}
FFAppState().update(() {
FFAppState().CardNumber = value;
});
return null;
}
String? _validateExpiryDate(String? value) {
if (value == null || value.isEmpty) {
return 'Please enter an expiry date';
}
if (!RegExp(r'^(0[1-9]|1[0-2])\/([0-9]{2})$').hasMatch(value)) {
return 'Please enter a valid expiry date (MM/YY)';
}
final now = DateTime.now();
final int currentYear = int.parse(now.year.toString().substring(2));
final int currentMonth = now.month;
final int inputMonth = int.parse(value.substring(0, 2));
final int inputYear = int.parse(value.substring(3));
if (inputYear < currentYear ||
(inputYear == currentYear && inputMonth < currentMonth)) {
return 'Expiry date cannot be in the past';
}
FFAppState().update(() {
FFAppState().ExpiryDate = value;
});
return null;
}
String? _validateCVV(String? value) {
if (value == null || value.isEmpty) {
return 'Please enter a CVV';
}
if (value.length != 3) {
return 'Please enter a valid 3-digit CVV';
}
FFAppState().update(() {
FFAppState().CVC = value;
});
return null;
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Card Number',
style: TextStyle(
color: FlutterFlowTheme.of(context).primaryText,
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
TextFormField(
controller: _cardNumberController,
cursorColor: FlutterFlowTheme.of(context).primaryText,
decoration: InputDecoration(
contentPadding:
EdgeInsets.symmetric(vertical: 17, horizontal: 17),
hintText: '0000 0000 0000 0000',
hintStyle: TextStyle(
color: FlutterFlowTheme.of(context).secondaryText,
fontSize: 14),
errorText: _cardNumberError,
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: FlutterFlowTheme.of(context).primary, width: 2.0),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: Color(0xFFD3D3D3),
width: 1.0,
),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: FlutterFlowTheme.of(context)
.primary, // Light gray border when typing
width: 1.0,
),
),
),
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
CardNumberInputFormatter(),
],
onChanged: (value) => _validateField('cardNumber'),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Expiry Date',
style: TextStyle(
color: FlutterFlowTheme.of(context).primaryText,
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
TextFormField(
controller: _expiryDateController,
cursorColor: FlutterFlowTheme.of(context).primaryText,
decoration: InputDecoration(
contentPadding:
EdgeInsets.symmetric(vertical: 17, horizontal: 17),
hintText: 'MM/YY',
hintStyle: TextStyle(
color: FlutterFlowTheme.of(context).secondaryText,
fontSize: 14),
errorText: _expiryDateError,
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: FlutterFlowTheme.of(context).primary,
width: 2.0),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: Color(0xFFD3D3D3),
width: 1.0,
),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: FlutterFlowTheme.of(context)
.primary, // Light gray border when typing
width: 1.0,
),
),
),
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
ExpiryDateInputFormatter(),
],
keyboardType: TextInputType.datetime,
onChanged: (value) => _validateField('expiryDate'),
),
],
),
),
const SizedBox(width: 20),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'CVC',
style: TextStyle(
color: FlutterFlowTheme.of(context).primaryText,
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
TextFormField(
controller: _securityCodeController,
cursorColor: const Color(0xFFBCD241),
decoration: InputDecoration(
contentPadding:
EdgeInsets.symmetric(vertical: 17, horizontal: 17),
hintText: '123',
hintStyle: const TextStyle(
color: Color(0xFF7C8BA0), fontSize: 14),
errorText: _cvvError,
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: FlutterFlowTheme.of(context).primary,
width: 2.0),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: Color(0xFFD3D3D3),
width: 1.0,
),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: FlutterFlowTheme.of(context)
.primary, // Light gray border when typing
width: 1.0,
),
),
),
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(3),
],
keyboardType: TextInputType.number,
onChanged: (value) => _validateField('cvv'),
),
],
),
),
],
),
],
),
);
}
@override
void dispose() {
_cardNumberController.dispose();
_expiryDateController.dispose();
_securityCodeController.dispose();
super.dispose();
}
}
// Formatter for card numbers
class CardNumberInputFormatter extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
final newText = newValue.text.replaceAll(' ', '');
if (newText.length > 16) return oldValue;
final buffer = StringBuffer();
for (int i = 0; i < newText.length; i++) {
if (i % 4 == 0 && i != 0) buffer.write(' ');
buffer.write(newText[i]);
}
return newValue.copyWith(
text: buffer.toString(),
selection: TextSelection.collapsed(offset: buffer.length),
);
}
}
// Formatter for expiry dates
class ExpiryDateInputFormatter extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
String newText = newValue.text.replaceAll(RegExp(r'[^0-9]'), '');
if (newText.length > 2) {
newText = '${newText.substring(0, 2)}/${newText.substring(2)}';
}
if (newText.length > 5) {
newText = newText.substring(0, 5);
}
return TextEditingValue(
text: newText,
selection: TextSelection.collapsed(offset: newText.length),
);
}
}
๐ Custom Credit Card Form for FlutterFlow! ๐
The existing FlutterFlow credit card widget wasnโt working, so I built my own! ๐ This custom credit card form is fully functional, user-friendly, and integrates seamlessly with FlutterFlow projects.
โจ Super easy to use! You just need to create three App State values to store the card details:
๐น CardNumber
๐น CVC
๐น ExpiryDate
๐น When passing data in the API, make sure to:
โ
Remove spaces: cardNumber.replaceAll(' ', '')
(using code expression)
โ
Extract Expiry Month: int.parse(expiryDate.split('/')[0])
โ
Extract Expiry Year: int.parse(expiryDate.split('/')[1])
I'm uploading it to the FlutterFlow community to help fellow developers. If you find it useful, like, comment and share! ๐
Let me know what you think! โจ #FlutterFlow #CreditCardForm #FlutterflowDevelopment