You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter中实现同款信用卡样式的FormField输入效果

示例图

实现代码

以下是封装完成的信用卡输入FormField组件,完全适配Flutter原生Form体系,所有字段的样式、装饰都支持自定义:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

// 信用卡数据存储类
class CreditCardInfo {
  final String cardNumber;
  final String expireDate;
  final String cvv;
  final String cardHolder;

  CreditCardInfo({
    required this.cardNumber,
    required this.expireDate,
    required this.cvv,
    required this.cardHolder,
  });
}

// 卡号格式化器:每4位自动插入空格
class CardNumberInputFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
    if (newValue.selection.baseOffset == 0) return newValue;
    String inputText = newValue.text.replaceAll(' ', '');
    StringBuffer buffer = StringBuffer();
    for (int i = 0; i < inputText.length; i++) {
      buffer.write(inputText[i]);
      int index = i + 1;
      if (index % 4 == 0 && index != inputText.length) {
        buffer.write(' ');
      }
    }
    String formatted = buffer.toString();
    return newValue.copyWith(
      text: formatted,
      selection: TextSelection.collapsed(offset: formatted.length),
    );
  }
}

// 有效期格式化器:自动添加/分隔,格式MM/YY
class ExpireDateInputFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
    String input = newValue.text.replaceAll('/', '');
    if (input.length > 4) input = input.substring(0,4);
    StringBuffer buffer = StringBuffer();
    for(int i=0; i<input.length; i++) {
      buffer.write(input[i]);
      if(i == 1 && input.length > 2) {
        buffer.write('/');
      }
    }
    String formatted = buffer.toString();
    return newValue.copyWith(
      text: formatted,
      selection: TextSelection.collapsed(offset: formatted.length),
    );
  }
}

// 封装好的信用卡FormField组件
class CreditCardFormField extends FormField<CreditCardInfo> {
  CreditCardFormField({
    super.key,
    super.autovalidateMode,
    super.enabled,
    super.validator,
    super.onSaved,
    // 可自定义的样式参数
    InputDecoration? cardNumberDecoration,
    InputDecoration? expireDateDecoration,
    InputDecoration? cvvDecoration,
    InputDecoration? cardHolderDecoration,
    TextStyle? textStyle,
  }) : super(
    initialValue: CreditCardInfo(cardNumber: '', expireDate: '', cvv: '', cardHolder: ''),
    builder: (FormFieldState<CreditCardInfo> state) {
      return Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          // 卡号输入框
          TextFormField(
            style: textStyle,
            decoration: cardNumberDecoration ?? const InputDecoration(
              labelText: '卡号',
              hintText: '**** **** **** ****',
              border: OutlineInputBorder(),
            ),
            keyboardType: TextInputType.number,
            inputFormatters: [
              FilteringTextInputFormatter.digitsOnly,
              CardNumberInputFormatter(),
              LengthLimitingTextInputFormatter(19), // 16位数字+3个空格
            ],
            onChanged: (val) {
              state.didChange(state.value?.copyWith(cardNumber: val));
            },
            validator: (val) {
              if(val == null || val.replaceAll(' ', '').length != 16) {
                return '请输入16位有效卡号';
              }
              return null;
            },
          ),
          const SizedBox(height: 16),
          Row(
            children: [
              // 有效期输入框
              Expanded(
                child: TextFormField(
                  style: textStyle,
                  decoration: expireDateDecoration ?? const InputDecoration(
                    labelText: '有效期',
                    hintText: 'MM/YY',
                    border: OutlineInputBorder(),
                  ),
                  keyboardType: TextInputType.number,
                  inputFormatters: [
                    FilteringTextInputFormatter.digitsOnly,
                    ExpireDateInputFormatter(),
                    LengthLimitingTextInputFormatter(5),
                  ],
                  onChanged: (val) {
                    state.didChange(state.value?.copyWith(expireDate: val));
                  },
                  validator: (val) {
                    if(val == null || val.length !=5) {
                      return '请输入有效有效期';
                    }
                    return null;
                  },
                ),
              ),
              const SizedBox(width: 16),
              // CVV输入框
              Expanded(
                child: TextFormField(
                  style: textStyle,
                  decoration: cvvDecoration ?? const InputDecoration(
                    labelText: 'CVV',
                    hintText: '***',
                    border: OutlineInputBorder(),
                  ),
                  keyboardType: TextInputType.number,
                  inputFormatters: [
                    FilteringTextInputFormatter.digitsOnly,
                    LengthLimitingTextInputFormatter(3),
                  ],
                  onChanged: (val) {
                    state.didChange(state.value?.copyWith(cvv: val));
                  },
                  validator: (val) {
                    if(val == null || val.length !=3) {
                      return '请输入3位CVV';
                    }
                    return null;
                  },
                ),
              ),
            ],
          ),
          const SizedBox(height: 16),
          // 持卡人姓名输入框
          TextFormField(
            style: textStyle,
            decoration: cardHolderDecoration ?? const InputDecoration(
              labelText: '持卡人姓名',
              hintText: '姓名拼音',
              border: OutlineInputBorder(),
            ),
            textCapitalization: TextCapitalization.characters,
            onChanged: (val) {
              state.didChange(state.value?.copyWith(cardHolder: val));
            },
            validator: (val) {
              if(val == null || val.isEmpty) {
                return '请输入持卡人姓名';
              }
              return null;
            },
          ),
          if(state.hasError)
            Padding(
              padding: const EdgeInsets.only(top: 8),
              child: Text(
                state.errorText!,
                style: const TextStyle(color: Colors.red, fontSize: 12),
              ),
            )
        ],
      );
    }
  );
}

// 给CreditCardInfo加copyWith方法方便更新
extension CreditCardInfoCopy on CreditCardInfo {
  CreditCardInfo copyWith({
    String? cardNumber,
    String? expireDate,
    String? cvv,
    String? cardHolder,
  }) {
    return CreditCardInfo(
      cardNumber: cardNumber ?? this.cardNumber,
      expireDate: expireDate ?? this.expireDate,
      cvv: cvv ?? this.cvv,
      cardHolder: cardHolder ?? this.cardHolder,
    );
  }
}

使用方式

直接将组件放入Flutter原生Form组件中使用即可,提交表单时可以直接拿到完整的信用卡信息:

Form(
  child: Column(
    children: [
      CreditCardFormField(
        // 可在这里自定义各个输入框的样式,比如修改label样式、边框等
        // cardNumberDecoration: InputDecoration(...),
        // textStyle: TextStyle(...),
      ),
      ElevatedButton(
        onPressed: () {
          if(Form.of(context).validate()) {
            Form.of(context).save();
            // 这里获取到完整的信用卡信息
          }
        },
        child: const Text('提交'),
      )
    ],
  ),
)

内容的提问来源于stack exchange,提问作者Fsd Ramjan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 09:24:00