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

Muh*_*eem 11 payment text space credit-card flutter

在此输入图像描述 如何在 4 位数字之间留出空格?

希望帮助解决问题

Rav*_*til 17

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

您的小部件:

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) {},
  ),
Run Code Online (Sandbox Code Playgroud)

创建用于分隔数字的类:

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,
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

完整示例:

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,
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

测试您的代码Darpad

您的结果屏幕->图像


Mad*_*rld 6

所选答案对我不起作用。但这是我自己的问题版本,它还有一个自定义分隔符希望它可以帮助那里的人。

很重要

确保清洁卡号并移除.txt 中卡号验证器内的分隔符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";
                  }
                },
Run Code Online (Sandbox Code Playgroud)

这是我的 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

有了这个keyboardType

keyboardType: TextInputType.number,
Run Code Online (Sandbox Code Playgroud)

并在小部件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),
                ],
Run Code Online (Sandbox Code Playgroud)

你应该得到想要的结果

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