firebase_phone_number_verification 0.0.1
firebase_phone_number_verification: ^0.0.1 copied to clipboard
A Flutter plugin for Firebase Phone Number Verification (PNV) on Android. Verify the device's phone number directly from the carrier with one tap, without SMS OTPs.
import 'package:firebase_phone_number_verification/firebase_phone_number_verification.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Firebase PNV Example',
theme: ThemeData(
colorSchemeSeed: Colors.deepOrange,
useMaterial3: true,
),
home: const PnvDemoPage(),
);
}
}
class PnvDemoPage extends StatefulWidget {
const PnvDemoPage({super.key});
@override
State<PnvDemoPage> createState() => _PnvDemoPageState();
}
class _PnvDemoPageState extends State<PnvDemoPage> {
final _fpnv = FirebasePhoneNumberVerification.instance;
final _testNumberIdController = TextEditingController();
bool _testSessionEnabled = false;
bool _busy = false;
List<VerificationSupportResult>? _supportResults;
VerifiedPhoneNumberResult? _verificationResult;
String? _error;
Future<void> _run(Future<void> Function() action) async {
setState(() {
_busy = true;
_error = null;
});
try {
await action();
} on FirebasePnvException catch (e) {
setState(() => _error = '${e.code.name}: ${e.message}');
} finally {
if (mounted) setState(() => _busy = false);
}
}
Future<void> _enableTestSession() => _run(() async {
await _fpnv.enableTestSession(_testNumberIdController.text.trim());
setState(() => _testSessionEnabled = true);
});
Future<void> _checkSupport() => _run(() async {
final results = await _fpnv.getVerificationSupportInfo();
setState(() => _supportResults = results);
});
Future<void> _verify() => _run(() async {
final result = await _fpnv.getVerifiedPhoneNumber();
setState(() => _verificationResult = result);
});
@override
void dispose() {
_testNumberIdController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Firebase PNV Example')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
Text(
'1. Test session (optional)',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
const Text(
'Generate a test number ID in the Firebase console under '
'Security > Phone Verification > Testing, and paste it here to '
'try the flow without a real SIM.',
),
const SizedBox(height: 8),
TextField(
controller: _testNumberIdController,
enabled: !_testSessionEnabled,
decoration: const InputDecoration(
labelText: 'Test number ID',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 8),
FilledButton.tonal(
onPressed: _busy || _testSessionEnabled ? null : _enableTestSession,
child: Text(
_testSessionEnabled
? 'Test session enabled'
: 'Enable test session',
),
),
const Divider(height: 32),
Text(
'2. Check support',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
FilledButton.tonal(
onPressed: _busy ? null : _checkSupport,
child: const Text('Check verification support'),
),
if (_supportResults != null) ...[
const SizedBox(height: 8),
for (final r in _supportResults!)
Card(
child: ListTile(
leading: Icon(
r.isSupported ? Icons.check_circle : Icons.cancel,
color: r.isSupported ? Colors.green : Colors.red,
),
title: Text('SIM slot ${r.simSlot}'),
subtitle: Text(
'Carrier: ${r.carrierId.isEmpty ? '(unknown)' : r.carrierId}'
' • reason: ${r.reason}',
),
),
),
if (_supportResults!.isEmpty)
const Text('No SIM support info returned.'),
],
const Divider(height: 32),
Text(
'3. Verify phone number',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
FilledButton(
onPressed: _busy ? null : _verify,
child: _busy
? const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text('Get verified phone number'),
),
if (_verificationResult != null) ...[
const SizedBox(height: 8),
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Verified: ${_verificationResult!.phoneNumber}',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
Text('Issued at: ${_verificationResult!.issuedAt}'),
Text('Expires at: ${_verificationResult!.expiresAt}'),
const SizedBox(height: 8),
Text(
'Token: ${_verificationResult!.token}',
maxLines: 4,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
),
],
if (_error != null) ...[
const SizedBox(height: 16),
Card(
color: Theme.of(context).colorScheme.errorContainer,
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(
_error!,
style: TextStyle(
color: Theme.of(context).colorScheme.onErrorContainer,
),
),
),
),
],
],
),
);
}
}