Build a Flawless SMS Authentication Flow in Flutter with Riverpod in 5 Minutes (Zero Paperwork)
2026-06-01T01:02:17.307Z
![]()
Build a Flawless SMS Authentication Flow in Flutter with Riverpod in 5 Minutes (Zero Paperwork)
Are you building a toy project or a startup MVP in Flutter? When you finally sit down to build the user registration flow, you will inevitably hit a massive roadblock: SMS Verification (OTP).
If you've ever tried integrating legacy SMS gateway APIs, you know the pain:
- Submitting business registration certificates.
- Verifying telecom documents just to register a sender ID.
- Waiting days for approval.
For solo developers, freelancers, or early-stage startups without a registered business entity, this is a showstopper. In this tutorial, we will build a flawless SMS authentication flow using Flutter, Riverpod, and EasyAuth—a developer-first SMS API that requires zero paperwork and takes 5 minutes to set up.
Why Choose EasyAuth?
EasyAuth is designed purely with developers in mind:
- Zero Paperwork: No business certificates needed. Sign up with an email and start instantly.
- Auto Sender ID: We provide default sender numbers out of the box so you don't have to register one.
- Cost-Effective: At just 15~25 KRW per message, it's nearly half the price of legacy providers.
- Dead Simple API: Only two endpoints needed:
/sendand/verify.
1. Defining the Authentication State with Riverpod
A standard OTP flow goes through several stages: Idle, Sending SMS, Code Sent (Timer running), Verifying, Success, and Error. Let's use Riverpod's Notifier to manage this state cleanly.
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
enum AuthStatus { initial, sending, codeSent, verifying, success, error }
class AuthState {
final AuthStatus status;
final String? errorMessage;
AuthState({required this.status, this.errorMessage});
}
class AuthNotifier extends Notifier {
@override
AuthState build() => AuthState(status: AuthStatus.initial);
// 1. Send OTP (POST /send)
Future sendSms(String phone) async {
state = AuthState(status: AuthStatus.sending);
try {
final response = await http.post(
Uri.parse('https://api.easyauth.kr/send'),
headers: {'Authorization': 'Bearer YOUR_API_KEY'},
body: jsonEncode({'phone': phone}),
);
if (response.statusCode == 200) {
state = AuthState(status: AuthStatus.codeSent);
} else {
state = AuthState(status: AuthStatus.error, errorMessage: 'Failed to send SMS');
}
} catch (e) {
state = AuthState(status: AuthStatus.error, errorMessage: 'Network Error');
}
}
// 2. Verify OTP (POST /verify)
Future verifyCode(String phone, String code) async {
state = AuthState(status: AuthStatus.verifying);
try {
final response = await http.post(
Uri.parse('https://api.easyauth.kr/verify'),
headers: {'Authorization': 'Bearer YOUR_API_KEY'},
body: jsonEncode({'phone': phone, 'code': code}),
);
if (response.statusCode == 200) {
state = AuthState(status: AuthStatus.success);
} else {
state = AuthState(status: AuthStatus.error, errorMessage: 'Invalid verification code');
}
} catch (e) {
state = AuthState(status: AuthStatus.error, errorMessage: 'Network Error');
}
}
}
final authProvider = NotifierProvider(() => AuthNotifier());
2. Building the Perfect UI/UX
Now, let's wire our state up to the UI. We'll show the phone input first, and then reveal the OTP input field once the SMS is sent.
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class SmsAuthScreen extends ConsumerWidget {
final phoneController = TextEditingController();
final codeController = TextEditingController();
@override
Widget build(BuildContext context, WidgetRef ref) {
final authState = ref.watch(authProvider);
final authNotifier = ref.read(authProvider.notifier);
return Scaffold(
appBar: AppBar(title: Text('SMS Verification')),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(
controller: phoneController,
keyboardType: TextInputType.phone,
decoration: InputDecoration(labelText: 'Phone Number (Numbers only)'),
enabled: authState.status == AuthStatus.initial || authState.status == AuthStatus.error,
),
SizedBox(height: 16),
if (authState.status == AuthStatus.initial || authState.status == AuthStatus.error)
ElevatedButton(
onPressed: () => authNotifier.sendSms(phoneController.text),
child: Text('Get Verification Code'),
),
if (authState.status == AuthStatus.codeSent || authState.status == AuthStatus.verifying) ...[
TextField(
controller: codeController,
keyboardType: TextInputType.number,
decoration: InputDecoration(labelText: '6-digit OTP'),
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () => authNotifier.verifyCode(phoneController.text, codeController.text),
child: authState.status == AuthStatus.verifying
? CircularProgressIndicator()
: Text('Verify'),
),
],
if (authState.status == AuthStatus.success)
Padding(
padding: const EdgeInsets.only(top: 20),
child: Text('✅ Verification Successful!', style: TextStyle(color: Colors.green, fontSize: 18)),
),
if (authState.errorMessage != null)
Padding(
padding: const EdgeInsets.only(top: 20),
child: Text(authState.errorMessage!, style: TextStyle(color: Colors.red)),
),
],
),
),
);
}
}
3. Best Practices for Production
- Autofill OTP: Add
autofillHints: [AutofillHints.oneTimeCode]to your OTPTextField. Both iOS and Android will automatically parse incoming SMS and prompt the user to paste the code. - Implement a Timer: It is highly recommended to implement a 3-minute (180s) timer after sending the code. You can use Dart's
Timer.periodicto decrement a value in your Riverpod state. - Prevent Double Clicks: Disable the submit buttons while the state is
sendingorverifyingto prevent users from spamming API requests.
Conclusion
By combining the reactive power of Flutter & Riverpod with the simplicity of EasyAuth, you can build a secure, production-ready SMS authentication flow in under 5 minutes.
If you've been putting off user verification because of red tape and expensive legacy APIs, it's time to make the switch. EasyAuth gives you 10 free credits upon sign-up—no credit card or business license required. Try it out on your next project today!
비트베이크에서 광고를 시작해보세요
광고 문의하기