otp_autofill_field 1.0.0
otp_autofill_field: ^1.0.0 copied to clipboard
Self-contained Flutter OTP field: PIN UI with automatic Android SMS Retriever and iOS one-time-code keyboard autofill, plus a resend timer. No external OTP/PIN dependency.
example/lib/main.dart
import 'package:otp_autofill_field/otp_autofill_field.dart';
import 'package:flutter/material.dart';
void main() => runApp(const ExampleApp());
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'auto_otp example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
useMaterial3: true,
),
home: const OtpPage(),
);
}
}
class OtpPage extends StatefulWidget {
const OtpPage({super.key});
@override
State<OtpPage> createState() => _OtpPageState();
}
class _OtpPageState extends State<OtpPage> {
final _controller = AutoOtpController();
String _status = 'Waiting for the code…';
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _verify(String code) {
// Diagnostic: prove onCompleted fires for BOTH manual entry and autofill.
// (Don't hardcode a "correct" code — a real OTP is never 123456, so a
// hardcoded check makes autofill look broken when it actually worked.)
debugPrint('onCompleted fired with: $code');
setState(() => _status = '✅ onCompleted: $code');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('auto_otp')),
body: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text('Enter the 6-digit code',
style: TextStyle(fontSize: 18)),
const SizedBox(height: 24),
AutoOtpField(
length: 6,
controller: _controller,
onChanged: (code) => debugPrint('onChanged: "$code"'),
onCompleted: _verify,
// Log only — don't overwrite _status, or it would mask
// onCompleted's result (onCompleted is the source of truth).
onAutoFill: (code) => debugPrint('onAutoFill: "$code"'),
onTimeout: () =>
setState(() => _status = '⌛ SMS wait timed out'),
),
const SizedBox(height: 12),
// 1-minute resend cooldown (default duration).
OtpResendTimer(
onResend: () async {
debugPrint('Resending OTP…');
setState(() => _status = '🔄 New code requested');
},
),
const SizedBox(height: 12),
Text(_status),
const SizedBox(height: 24),
FilledButton(
onPressed: () async {
final sig = await AutoOtp.getAppSignature();
if (!context.mounted) return;
setState(() => _status = 'App signature: ${sig ?? "n/a"}');
},
child: const Text('Show app signature'),
),
],
),
),
),
);
}
}