How Create TextField Like Credit Card Number in flutter?
Asked Answered
L

3

11

enter image description here How I give space between 4 digits ?

Want help to solve issue

Lentigo answered 27/3, 2022 at 2:0 Comment(0)
B
16

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: 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) {},
  ),

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 = 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,
      ),
    );
  }
}

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: 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,
      ),
    );
  }
}

Test Your code on Darpad

Your Result Screen-> image

Boarish answered 27/3, 2022 at 5:29 Comment(1)
this makes the input puts extra spaces and none sance selecting once I try to remove something from the inputHello
C
6

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 and remove the separators inside the card 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.

Chronaxie answered 1/8, 2022 at 16:44 Comment(1)
Very important to remember to remove the separator before any further action.Rutherfurd
C
1

In this article I found a simple solution, all what you need to do is create the class below and assign it to your TextField widget:

1- Create this class:

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- Assign the new class to inputFormatters inside TextField:

TextField(
  // ..
  inputFormatters: [
     LengthLimitingTextInputFormatter(16),
     CreditCardNumberFormatter()
  ],
  // ..
 ),
Connivent answered 26/2, 2023 at 14:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.