如何在 flutter 中创建类似于信用卡号的 TextField?

问题描述 投票:0回答:3

enter image description here 我如何在 4 位数字之间留出空格?

需要帮助解决问题

flutter text payment space credit-card
3个回答
14
投票

试试下面的代码希望对你有帮助。

您的小部件:

TextFormField(
    inputFormatters: [
      FilteringTextInputFormatter.digitsOnly,
      CardNumberFormatter(),
    ],
    textInputAction: TextInputAction.done,
    keyboardType: TextInputType.number,
    decoration: InputDecoration(
      prefixIcon: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Image.network(
          'https://upload.wikimedia.org/wikipedia/commons/thumb/2/2a/Mastercard-logo.svg/800px-Mastercard-logo.svg.png',
          height: 30,
          width: 30,
        ),
      ),
      suffixIcon: const Padding(
        padding: EdgeInsets.all(8.0),
        child: Text(
          'Change',
          style: TextStyle(color: Colors.green),
        ),
      ),
      border: const OutlineInputBorder(),
      hintText: 'XXXX XXXX XXXX XXXX',
      labelText: 'Card Number',
    ),
    maxLength: 19,
    onChanged: (value) {},
  ),

创建用于分隔数字的类:

class CardNumberFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(
    TextEditingValue previousValue,
    TextEditingValue nextValue,
  ) {
    var inputText = nextValue.text;

    if (nextValue.selection.baseOffset == 0) {
      return nextValue;
    }

    var bufferString = StringBuffer();
    for (int i = 0; i < inputText.length; i++) {
      bufferString.write(inputText[i]);
      var nonZeroIndexValue = i + 1;
      if (nonZeroIndexValue % 4 == 0 && nonZeroIndexValue != inputText.length) {
        bufferString.write(' ');
      }
    }

    var string = bufferString.toString();
    return nextValue.copyWith(
      text: string,
      selection: TextSelection.collapsed(
        offset: string.length,
      ),
    );
  }
}

完整示例:

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

void main() {
  runApp(
    MyApp(),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.symmetric(
        horizontal: 20,
      ),
      child: TextFormField(
        inputFormatters: [
          FilteringTextInputFormatter.digitsOnly,
          CardNumberFormatter(),
        ],
        textInputAction: TextInputAction.done,
        keyboardType: TextInputType.number,
        decoration: InputDecoration(
          prefixIcon: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Image.network(
              'https://upload.wikimedia.org/wikipedia/commons/thumb/2/2a/Mastercard-logo.svg/800px-Mastercard-logo.svg.png',
              height: 30,
              width: 30,
            ),
          ),
          suffixIcon: const Padding(
            padding: EdgeInsets.all(8.0),
            child: Text(
              'Change',
              style: TextStyle(color: Colors.green),
            ),
          ),
          border: const OutlineInputBorder(),
          hintText: 'XXXX XXXX XXXX XXXX',
          labelText: 'Card Number',
        ),
        maxLength: 19,
        onChanged: (value) {},
      ),
    );
  }
}

class CardNumberFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(
    TextEditingValue previousValue,
    TextEditingValue nextValue,
  ) {
    var inputText = nextValue.text;

    if (nextValue.selection.baseOffset == 0) {
      return nextValue;
    }

    var bufferString = StringBuffer();
    for (int i = 0; i < inputText.length; i++) {
      bufferString.write(inputText[i]);
      var nonZeroIndexValue = i + 1;
      if (nonZeroIndexValue % 4 == 0 && nonZeroIndexValue != inputText.length) {
        bufferString.write(' ');
      }
    }

    var string = bufferString.toString();
    return nextValue.copyWith(
      text: string,
      selection: TextSelection.collapsed(
        offset: string.length,
      ),
    );
  }
}

Darpad

上测试你的代码

您的结果屏幕->


3
投票

所选答案不适合我。但这是我自己的问题版本,它也有一个自定义分隔符希望它能帮助那里的人。

非常重要

确保您清洁卡号并移除TextField卡号验证器内的分隔符

。因此,不要传递包含所有分隔符的基本值,而是确保使用类似

的东西
validator: (val) {
                  /// check if it is null empty or whitespace
                  if (val == null || val.isEmpty || val.trim().isEmpty) {
                    return "Please input card number";
                  }
                  
                  var cleanCardNumber = val.replaceAll(separator, '');
                  if (_validateCard(cleanCardNumber)) {
                    return "invalid card number";
                  }
                },

这是我的 CardFormatter 版本

class CardFormatter extends TextInputFormatter {
  final String separator;

  CardFormatter({required this.separator});

  @override
  TextEditingValue formatEditUpdate(
      TextEditingValue oldValue, TextEditingValue newValue) {
    var oldS = oldValue.text;
    var newS = newValue.text;
    var endsWithSeparator = false;

    // if you add text
    if (newS.length > oldS.length) {
      for (var char in separator.characters) {
        if (newS.substring(0, newS.length - 1).endsWith(char)) {
          endsWithSeparator = true;
        }
      }
      print(
          'Ends with separator: $endsWithSeparator, so we will add it with next digit.');

      var clean = newS.replaceAll(separator, '');
      print('CLEAN add: $clean');
      if (!endsWithSeparator && clean.length > 1 && clean.length % 4 == 1) {
        return newValue.copyWith(
          text: newS.substring(0, newS.length - 1) +
              separator +
              newS.characters.last,
          selection: TextSelection.collapsed(
            offset: newValue.selection.end + separator.length,
          ),
        );
      }
    }

    // if you delete text
    if (newS.length < oldS.length) {
      for (var char in separator.characters) {
        if (oldS.substring(0, oldS.length - 1).endsWith(char)) {
          endsWithSeparator = true;
        }
      }
      print('Ends with separator: $endsWithSeparator, so we removed it');

      var clean = oldS.substring(0, oldS.length - 1).replaceAll(separator, '');
      print('CLEAN remove: $clean');
      if (endsWithSeparator && clean.isNotEmpty && clean.length % 4 == 0) {
        return newValue.copyWith(
          text: newS.substring(0, newS.length - separator.length),
          selection: TextSelection.collapsed(
            offset: newValue.selection.end - separator.length,
          ),
        );
      }
    }

    return newValue;
  }
}

有了这个
keyboardType

keyboardType: TextInputType.number,

和这些
inputFormatters
TextFormField
小部件

// somewhere inside the code define the separator
var separator = ' - ';

[...]

// and add this to your `TextFormField` Widget
inputFormatters: [
                  /// allows card number length of 18 and 4 separators
                  LengthLimitingTextInputFormatter(18 + separator.length * 4),
                  CardFormatter(separator: separator),
                ],

你应该得到想要的结果

希望这对您有所帮助,也对您有用,请随意将文本输入限制更改为您需要的任何内容。但如果你这样做,也要考虑你想要的分隔符的数量。


0
投票

这篇文章中,我找到了一个简单的解决方案,您需要做的就是创建下面的类并将其分配给您的

TextField
小部件:

1-创建这个类:

class CreditCardNumberFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
    if(newValue.selection.baseOffset == 0) {
      return newValue;
    }
    String enteredData = newValue.text;   // get data enter by used in textField
    StringBuffer buffer = StringBuffer();
    for (int i = 0;i < enteredData.length;i++) {
      // add each character into String buffer
      buffer.write(enteredData[i]);
      int index = i + 1;
      if(index % 4 == 0 && enteredData.length != index) {
        // add space after 4th digit
        buffer.write(' ');
      }
    }

    return  TextEditingValue(
        text: buffer.toString(),   // final generated credit card number
        selection: TextSelection.collapsed(offset: buffer.toString().length) // keep the cursor at end
    );
  }
}

2- 将新类分配给 TextField 中的 inputFormatters:

TextField(
  // ..
  inputFormatters: [
     LengthLimitingTextInputFormatter(16),
     CreditCardNumberFormatter()
  ],
  // ..
 ),
© www.soinside.com 2019 - 2024. All rights reserved.