简体   繁体   中英

How Create TextField Like Credit Card Number in flutter?

在此处输入图像描述 How I give space between 4 digits?

Want help to solve issue

Try below code hope its helpful to you.

Your Widget:

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: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Text(
          'Change',
          style: TextStyle(color: Colors.green),
        ),
      ),
      border: OutlineInputBorder(),
      hintText: 'XXXX XXXX XXXX XXXX',
      labelText: 'Card Number',
    ),
    maxLength: 19,
    onChanged: (value) {},
  ),

Create class for seprate the digits

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

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

    var bufferString = new 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: new TextSelection.collapsed(
        offset: string.length,
      ),
    );
  }
}

Full Example:

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: 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: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Text(
              'Change',
              style: TextStyle(color: Colors.green),
            ),
          ),
          border: 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 = new 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: new TextSelection.collapsed(
        offset: string.length,
      ),
    );
  }
}

Test Your code on Darpad

Your Result Screen-> 图片

The selected answer didn't work for me. But here is my own version of the problem, which also has a custom separator hope it helps someone out there.

VERY IMPORTANT

Make sure you 'clean' the card number inside the car number validator in your TextField . So Instead of passing the basic value with all the separators inside it, make sure you use something like

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";
                  }
                },

This is my CardFormatter version

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;
  }
}

With this keyboardType

keyboardType: TextInputType.number,

and with these inputFormatters inside the TextFormField Widget

// 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),
                ],

you should get the desired result

Hope this helps and works for you as well, feel free to change the text input limit to whatever you need. But if you do, also take into account the number of separators you would like to have.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM