How to get OTP from SMS - autofill

11,536

Solution 1

I hope I am not to late for people who are still looking for an answer to this question. I have used two packages https://pub.dev/packages/alt_sms_autofill and https://pub.dev/packages/pin_code_fields. Add these two packages to your pubspec.yaml file. Run 'flutter pub get' to download the packages.

In your otp screen import both packages:

import 'package:alt_sms_autofill/alt_sms_autofill.dart';
import 'package:pin_code_fields/pin_code_fields.dart';

After your AppState extends State put the following function to grab incoming SMS:

  TextEditingController textEditingController1;

  String _comingSms = 'Unknown';

  Future<void> initSmsListener() async {

    String comingSms;
    try {
      comingSms = await AltSmsAutofill().listenForSms;
    } on PlatformException {
      comingSms = 'Failed to get Sms.';
    }
    if (!mounted) return;
    setState(() {
      _comingSms = comingSms;
      print("====>Message: ${_comingSms}");
      print("${_comingSms[32]}");
      textEditingController1.text = _comingSms[32] + _comingSms[33] + _comingSms[34] + _comingSms[35]
          + _comingSms[36] + _comingSms[37]; //used to set the code in the message to a string and setting it to a textcontroller. message length is 38. so my code is in string index 32-37.
    });
  }

My incoming OTP Message Format looks like this: Your phone verification code is 625742. In the above function, it is listening to incoming sms and saving it to a string. After the sms has been received, I am setting the '625742' code to my textEditing controller by giving the index position of the code in the string which then sets the value to my PinFields which you will see later.

Call functions in your initState:

  @override
  void initState() {
    super.initState();
    textEditingController1 = TextEditingController();
    initSmsListener();
  }

You should dispose anything you are not using inside your dispose function:

  @override
  void dispose() {
    textEditingController1.dispose();
    AltSmsAutofill().unregisterListener();
    super.dispose();
  }

Then you need to put the pinfields in your build function or inside a column like this:

PinCodeTextField(
          appContext: context,
          pastedTextStyle: TextStyle(
            color: Colors.green.shade600,
            fontWeight: FontWeight.bold,
          ),
          length: 6,
          obscureText: false,
          animationType: AnimationType.fade,
          pinTheme: PinTheme(
            shape: PinCodeFieldShape.box,
            borderRadius: BorderRadius.circular(10),
            fieldHeight: 50,
            fieldWidth: 40,
            inactiveFillColor: Colors.white,
            inactiveColor: ColorUtils.greyBorderColor,
            selectedColor: ColorUtils.greyBorderColor,
            selectedFillColor: Colors.white,
            activeFillColor: Colors.white,
            activeColor: ColorUtils.greyBorderColor
          ),
          cursorColor: Colors.black,
          animationDuration: Duration(milliseconds: 300),
          enableActiveFill: true,
          controller: textEditingController1,
          keyboardType: TextInputType.number,
          boxShadows: [
            BoxShadow(
              offset: Offset(0, 1),
              color: Colors.black12,
              blurRadius: 10,
            )
          ],
          onCompleted: (v) {
            //do something or move to next screen when code complete
          },
          onChanged: (value) {
            print(value);
            setState(() {
              print('$value');
            });
          },
        ),

Make sure to set controller to the pinfield widget and after you receive the sms use string indexs to set the code to your textfield. See the below image for example. enter image description here

Solution 2

You may use this package: https://pub.dev/packages/sms_autofill

But consider the following limits:

Android SMS constraint For the code to be receive, it need to follow some rules as describe here: https://developers.google.com/identity/sms-retriever/verify

Be no longer than 140 bytes Begin with the prefix <#> Contain a one-time code that the client sends back to your server to complete the verification flow End with an 11-character hash string that identifies your app One example of SMS would be:

<#> ExampleApp: Your code is 123456 FA+9qCX9VSu

Solution 3

I have used this package for Recieveing SMS Check it

What it does was that it Listens for the SMS through it's listner, and When the SMS Arrives it print the SMS.

This was the Code, I wrote for this a while ago (I am not sure if the package have made some changes or updates, Coz I haven't been using it for a while, but it was working that time pefectly.),

SmsReceiver receiver = new SmsReceiver();
await receiver.onSmsReceived.listen((SmsMessage msg) => checkSMS(msg));

Method for printing the SMS body,

  checkSMS(SmsMessage msg) async {
    print(msg.body);
  }

Now you can Autofill the SMS and fetch out the OTP using some regex from the msg.body and set it to a TextFieldController text for autofill.

NOTE: It will fetch every SMS, So to fetch the only one you need you have to check the keyword or set some Regex at your side to show only the OTP message, or your company name in the message.

Share:
11,536
khaled mohammad
Author by

khaled mohammad

Updated on July 19, 2022

Comments

  • khaled mohammad
    khaled mohammad almost 2 years

    I want to catch or read OTP of SMS messages automatically. I did some tests like this code :

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'Demo Auto OTP'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      TextEditingController _textController = TextEditingController();
      String _error;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
              title: Text("Multi-Factor-Authentication"),
            ),
            body: Form(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                mainAxisSize: MainAxisSize.min,
                children: [
                  TextField(
                    controller: _textController,
                    autofillHints: [ AutofillHints.oneTimeCode ],
                    keyboardType: TextInputType.visiblePassword,
                    maxLength: 6,
                    maxLengthEnforced: true,
                    style: TextStyle(fontSize: 32),
                  ),
    
                  RaisedButton(
                    child: Text("Verify"),
                    onPressed: () => Navigator.of(context).pop(_textController.value.text),
                  ),
                ],
              ),
            )
        );
      }
    }
    

    And this is the test SMS message : 12345 is your code to log in.

    Flutter documentation for oneTimeCode: https://api.flutter.dev/flutter/services/AutofillHints/oneTimeCode-constant.html

    Flutter Autofill : https://github.com/flutter/flutter/blob/7891006299/packages/flutter/lib/src/services/autofill.dart#L362

    IOS : https://developer.apple.com/documentation/uikit/uitextcontenttype

    Android : https://developer.android.com/reference/androidx/autofill/HintConstants#AUTOFILL_HINT_SMS_OTP