Effortless User Verification: Step-by-Step SMS Verification Version 1: Using Telephony Library
Suppose you’re building a Flutter app that requires users to enter a one-time password (OTP) sent via SMS. In that case, you can simplify the process in several ways and libraries and one of them is implementing SMS auto-fill using the telephony package. This package provides access to Android's telephony services, which allow you to listen to incoming SMS messages and extract the OTP from the message body. By using this package your message does not need to contain an App signature on the SMS body which is preferred by most people. Here is the full application’s Github link.
Here’s a step-by-step guide to implementing SMS auto-fill in Flutter using the telephony
package. Let’s start!
Let’s see what the result will look like
SMS body
Your OTP code is: 123456. Use it to complete the verification. Thank you!
Step 1: Import the telephony package
The first step is to import the telephony
package into your Flutter project’s pubspec.yaml
file. This package provides access to Android’s telephony services required for SMS auto-fill.
dependencies:
telephony: ^0.2.0
Step 2: Add the required code to the Android Manifest
The next step is to add the required code to your app’s Android manifest file (which is situated in android/app/src/main/AndroidManifest.xml). This code requests permission to receive SMS messages and sets up a broadcast receiver to listen for incoming SMS messages.
<manifest>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<application>
...
...
<receiver android:name="com.shounakmulay.telephony.sms.IncomingSmsReceiver"
android:permission="android.permission.BROADCAST_SMS" android:exported="true">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
</application>
</manifest>
Step 3: Update the build.gradle file
Update the build.gradle
file to set the minimum SDK version to 23. This is required because the telephony package relies on newer Android APIs unavailable in older versions. A file path is android/app/build.gradle
minSdkVersion 23
Step 4: Create a validation function to identify the OTP code
In this step, you create a validation use case to check the OTP from the message body. This use case uses a regular expression to extract the OTP from the message body. If the OTP is found, it is returned. Otherwise, null is returned.
String? validateOtpCode(String body) {
final otpCodeRegex = RegExp(r'\b\d{6}\b');
Match? match = otpCodeRegex.firstMatch(body);
if (match != null) {
String otpCode = match.group(0)!;
return otpCode;
} else {
return null;
}
}
Step 5: Write code to listen for incoming SMS messages
Here, you write code in the manager to get the SMS and parse it using the validator created in the previous step. This code listens for incoming SMS messages using the telephony
package and extracts the OTP from the message body using the validator. If the OTP is found, it is updated in the state.
Future<void> startMessageListener() async {
final bool? result = Platform.isAndroid
? await telephony.requestPhoneAndSmsPermissions
: null;
if (result != null && result) {
telephony.listenIncomingSms(
onBackgroundMessage: onBackgroundMessage,
onNewMessage: _messageHandler,
);
}
if (!mounted) return;
}
void messageHandler(SmsMessage message) {
final body = message.body;
if (body != null) {
String? receivedOtpCode = _validateOtpCode(body);
if (receivedOtpCode != null) {
// TODO: when OTP code received logic will be implemented
}
}
}
Last Step: Use it on the OTP page
Finally, you can use the telephony package to autofill OTP in your Flutter app. On the OTP page, you can listen for incoming SMS messages and extract the OTP from the message body using a validation use case. Once the OTP is extracted, it can be updated in the app state and auto-filled in the OTP text field.
To make OTP screen implementation fast and easy I have used Pinput package. But feel free to make your own implemented input field. Let’s see what the OTP screen page’s code looks like. First, otpEditingController in the type ofTextEditingController()
will be created and when receivedOtpCode is not null in _messageHandler() method, the otpEditingController’s text will be updated with receivedOtpCode. Pinput uses that otpEditingController as the controller to it.
class TelephonyOtpScreen extends StatefulWidget {
const TelephonyOtpScreen({super.key});
@override
State<TelephonyOtpScreen> createState() => _TelephonyOtpScreenState();
}
class _TelephonyOtpScreenState extends State<TelephonyOtpScreen> {
final otpEditingController = TextEditingController();
@override
void initState() {
super.initState();
_startMessageListener();
}
@override
void dispose() {
// do not forget dispose controller
otpEditingController.dispose();
super.dispose();
}
String? _validateOtpCode(String body) {
// method body written in Step 4 section
}
Future<void> _startMessageListener() async {
// method body written in Step 5 section
}
void _messageHandler(SmsMessage message) {
// method body written in Step 5 section but we will add logic when code received
if (receivedOtpCode != null) {
setState(() {
otpEditingController.text = receivedOtpCode;
});
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('OTP Screen (Telephony)'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Pinput(
// setting otpEditingController as controller
controller: otpEditingController,
length: 6,
defaultPinTheme: defaultPinTheme,
focusedPinTheme: focusedPinTheme,
submittedPinTheme: submittedPinTheme,
pinputAutovalidateMode: PinputAutovalidateMode.onSubmit,
showCursor: true,
),
],
),
);
}
}
The code given above is the short form of the OTP page’s code. To see all parts of it you can visit to GitHub link.
Bonus section: Show how to test on the emulator to receive SMS
For the SMS body, I have used the text given below, but feel free to customize it according to your preference.
Your OTP code is: 123456. Use it to complete the verification. Thank you!
To test autofill, you do not need to send real SMS, just you need is Android emulator. First, you will run your emulator and click from the menu option More (which is three dots) then choose the Phone section, enter your SMS in the field SMS message, and click to Send Message button to see magic!
Congratulations 🥳🥳🥳 you have reached the end of the article and successfully implemented the SMS autofill feature!
Conclusion
Now you can implement SMS autofill for your wonderful application. Maybe you have encountered some OTP messages that will contain some fancy numbers and letters at the end of the message body. By this version of implementing SMS autofill your SMS body does not need to contain an App signature. Yet, the important part is when you run an application it will ask permission to read SMS from the device. If this does not affect your app requirement feel free to use this method. But if you need an application without permission to read SMS, there will be one in my next blog post to show that method. Thank you for your time and patience! Here is the full code on GitHub.