4

I am trying to determine which credit card is it? and render Image Widget accordingly. but somehow its not working, I don't know If I miss something or is my approach is wrong

Here's the code snippet

Its always render the last else part.

Input image

Input Suffix Icon part where I am rendering container based on number

VxState.watch(context, on: [HandleInputChanges]);
TextField(
     maxLength: 16,
     decoration: InputDecoration(
          counter: const Offstage(),
          suffixIcon: getCreditCardType(store.moniPay.cardNo) // Here I am rendering image,
     hintText: "Input card number",
     onChanged: (String? value) {
          HandleInputChanges(value.toString(), 'cardno');
      })

And here's the getCreditCardType method

Widget getCreditCardType(String creditCardNumber) {
  if (RegExp(r"^4[0-9]{12}(?:[0-9]{3})?$").hasMatch(creditCardNumber)) {
    // visa card
    return Container(
        decoration: const BoxDecoration(
      image: DecorationImage(
        image: AssetImage("assets/images/visa.png"),
        fit: BoxFit.contain,
      ),
    ));
  } else if (RegExp(r"^5[1-5][0-9]{14}$").hasMatch(creditCardNumber)) {
    // master card
    return Container(
        decoration: const BoxDecoration(
      image: DecorationImage(
        image: AssetImage("assets/images/master.png"),
        fit: BoxFit.contain,
      ),
    ));
  } else if (RegExp(r"^3[47][0-9]{13}$").hasMatch(creditCardNumber)) {
    // AExpress card
    return Container(
        decoration: const BoxDecoration(
      image: DecorationImage(
        image: AssetImage("assets/images/american-express.png"),
        fit: BoxFit.contain,
      ),
    ));
  } else if (RegExp(r"^3(?:0[0-5]|[68][0-9])[0-9]{11}$")
      .hasMatch(creditCardNumber)) {
    // diner card
    return Container(
        decoration: const BoxDecoration(
      image: DecorationImage(
        image: AssetImage("assets/images/dinners-club.png"),
        fit: BoxFit.contain,
      ),
    ));
  } else if (RegExp(r"^6(?:011|5[0-9]{2})[0-9]{12}$")
      .hasMatch(creditCardNumber)) {
    // discover card
    return Container(
        decoration: const BoxDecoration(
      image: DecorationImage(
        image: AssetImage("assets/images/discover.png"),
        fit: BoxFit.contain,
      ),
    ));
  } else if (RegExp(r"^(?:2131|1800|35\\d{3})\\d{11}$")
      .hasMatch(creditCardNumber)) {
    // JCB card
    return Container(
        decoration: const BoxDecoration(
      image: DecorationImage(
        image: AssetImage("assets/images/jcb.png"),
        fit: BoxFit.contain,
      ),
    ));
  } else {
    return const Icon(Icons.credit_card);
  }
}
Sagar Chavada
  • 5,169
  • 7
  • 40
  • 67
  • 1
    Do you need "live input validation"? Try replacing the first regex (`RegExp(r"^4[0-9]{12}(?:[0-9]{3})?$")`) with `^4[0-9]{0,15}$`. – Wiktor Stribiżew May 24 '22 at 07:52
  • @WiktorStribiżew yes, I need live input validation, and thank you that work for visa card, any idea about master card and JCB card. – Sagar Chavada May 25 '22 at 04:06

2 Answers2

2

I've tried the function you wrote down, and it appears that the regex pattern only recognize the full/complete format of the credit card. So it is a "validation regex" instead of "classification regex".

Complete Visa number recognized Complete AE number recognized

It doesn't recognize incomplete credit card numbers, so it will always return the last else part.

Incomplete Visa number not recognized

Ahmad Rulim
  • 49
  • 2
  • 7
  • Thanks, now I realise what is the issue, any idea how we can achive live validation here instead of writing full number – Sagar Chavada May 24 '22 at 08:32
2

For live validation, you need to make sure that each subsequent digit can only be entered if the current substring is valid.

You can use

^5(?:[1-5][0-9]{0,14})?$                   # VISA
^5(?:[1-5][0-9]{0,14})?$                   # MASTER CARD
^3(?:[47][0-9]{0,13})?$                    # AEXPRESS CARD
^3(?:(?:0[0-5]?|[68][0-9]?)[0-9]{0,11})?$  # DINER CARD
^6(?:(?:01{0,2}|5[0-9]{0,2})[0-9]{0,12})?$ # DISCOVER CARD

The updated code will look like:

Widget getCreditCardType(String creditCardNumber) {
  if (RegExp(r"^4[0-9]{0,15}$").hasMatch(creditCardNumber)) {
    // visa card
    return Container(
        decoration: const BoxDecoration(
      image: DecorationImage(
        image: AssetImage("assets/images/visa.png"),
        fit: BoxFit.contain,
      ),
    ));
  } else if (RegExp(r"^5(?:[1-5][0-9]{0,14})?$").hasMatch(creditCardNumber)) {
    // master card
    return Container(
        decoration: const BoxDecoration(
      image: DecorationImage(
        image: AssetImage("assets/images/master.png"),
        fit: BoxFit.contain,
      ),
    ));
  } else if (RegExp(r"^3(?:[47][0-9]{0,13})?$").hasMatch(creditCardNumber)) {
    // AExpress card
    return Container(
        decoration: const BoxDecoration(
      image: DecorationImage(
        image: AssetImage("assets/images/american-express.png"),
        fit: BoxFit.contain,
      ),
    ));
  } else if (RegExp(r"^3(?:(?:0[0-5]?|[68][0-9]?)[0-9]{0,11})?$")
      .hasMatch(creditCardNumber)) {
    // diner card
    return Container(
        decoration: const BoxDecoration(
      image: DecorationImage(
        image: AssetImage("assets/images/dinners-club.png"),
        fit: BoxFit.contain,
      ),
    ));
  } else if (RegExp(r"^6(?:(?:01{0,2}|5[0-9]{0,2})[0-9]{0,12})?$")
      .hasMatch(creditCardNumber)) {
    // discover card
    return Container(
        decoration: const BoxDecoration(
      image: DecorationImage(
        image: AssetImage("assets/images/discover.png"),
        fit: BoxFit.contain,
      ),
    ));
  } else if (RegExp(r"^(?:2(?:1(?:31?)?)?|1(?:80{0,2})?|3(?:5\d{0,3})?)\d{0,11}$")
      .hasMatch(creditCardNumber)) {
    // JCB card
    return Container(
        decoration: const BoxDecoration(
      image: DecorationImage(
        image: AssetImage("assets/images/jcb.png"),
        fit: BoxFit.contain,
      ),
    ));
  } else {
    return const Icon(Icons.credit_card);
  }
}
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563