Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get OTP from SMS - autofill

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

like image 225
khaled mohammad Avatar asked Jan 04 '21 07:01

khaled mohammad


2 Answers

I hope I am not too 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

like image 74
Md. Kamrul Amin Avatar answered Sep 22 '22 08:09

Md. Kamrul Amin


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

like image 30
AVEbrahimi Avatar answered Sep 21 '22 08:09

AVEbrahimi