Flutter电话认证插件firebase_phone_auth_handler_flave的使用
Flutter电话认证插件firebase_phone_auth_handler_flave的使用
- 一个易于使用的Firebase电话认证包,用于发送和验证一次性密码(OTP),支持自动获取OTP。
- 该包在网页端支持OTP。
截图

入门指南
步骤1: 在将Firebase添加到您的应用之前,您需要创建一个Firebase项目来连接您的应用。 访问Firebase项目简介了解更多信息。
步骤2: 若要在您的应用中使用Firebase,您需要将您的应用注册到Firebase项目中。 注册应用通常称为“将您的应用添加到项目”。
如果在网页上使用,请也注册一个网页应用。按照屏幕上的指示初始化项目。
从这里添加最新版本的’firebase-auth’ CDN。 (测试版本为8.6.1)
步骤3: 添加Firebase配置文件和SDK(google-services)。
步骤4: 当基本设置完成后,打开控制台并转到项目,然后从左侧菜单选择Authentication。
步骤5: 点击Sign-in method旁边的Users选项卡,并启用Phone。
步骤6: 按照平台的附加配置步骤以避免任何错误。
步骤7: 重要提示:不要忘记启用Google Cloud Platform的Android Device Verification服务。(确保选择了正确的项目)。
步骤8: 最后,在pubspec.yaml文件中添加firebase_core作为依赖项。
并在main方法中调用Firebase.initializeApp(),如示例所示:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(_MainApp());
}
使用说明
要使用此插件,需要在pubspec.yaml文件中添加firebase_phone_auth_handler作为依赖项。
dependencies:
flutter:
sdk: flutter
firebase_phone_auth_handler:
首先导入该插件。
import 'package:firebase_phone_auth_handler/firebase_phone_auth_handler.dart';
用FirebasePhoneAuthProvider包装MaterialApp以启用电话认证。
class _MainApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return FirebasePhoneAuthProvider(
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: HomeScreen(),
),
);
}
}
现在可以在你的小部件树中添加FirebasePhoneAuthHandler小部件,并传递所有必需的参数开始使用。
FirebasePhoneAuthHandler(
phoneNumber: "+919876543210",
builder: (context, controller) {
return SizedBox.shrink();
},
),
手机号码是将发送OTP的号码,应按以下格式进行格式化:
+919876543210 - 其中+91是国家代码,9876543210是电话号码。
builder返回的小部件将在屏幕上渲染。builder暴露了一个controller,其中包含各种变量和方法。
可以传递回调函数,例如onLoginSuccess或onLoginFailed。
onLoginSuccess在成功发送OTP且自动验证或手动通过调用verifyOTP函数验证时被调用。回调暴露了UserCredential对象,可以用来查找用户UID和其他信息。布尔值表示是否自动验证了OTP(true)还是手动验证(false)。
onLoginFailed在发送OTP或验证OTP时发生错误或任何内部错误时被调用。回调暴露了FirebaseAuthException,可以用来处理错误。
onCodeSent在OTP成功发送到电话号码时被调用。
FirebasePhoneAuthHandler(
phoneNumber: "+919876543210",
// 如果为true,则在成功验证OTP后会注销当前用户。
signOutOnSuccessfulVerification: false,
linkWithExistingUser: false,
builder: (context, controller) {
return SizedBox.shrink();
},
onLoginSuccess: (userCredential, autoVerified) {
debugPrint("autoVerified: $autoVerified");
debugPrint("Login success UID: ${userCredential.user?.uid}");
},
onLoginFailed: (authException, stackTrace) {
debugPrint("An error occurred: ${authException.message}");
},
onError: (error, stackTrace) {},
),
要注销当前用户(如果有),只需调用:
await FirebasePhoneAuthHandler.signOut(context);
如果需要在同一屏幕中注销当前用户,也可以使用controller.signOut()。
网页(reCAPTCHA)
默认情况下,reCAPTCHA小部件是一个完全托管的流程,为您的网页应用提供安全性。当触发登录流程时,小部件将呈现为不可见的小部件。一个“不可见”的小部件将作为全屏模态窗口出现在您的应用之上,如下面所示:

但是,可以传递一个RecaptchaVerifier实例,用于管理小部件。
使用FirebasePhoneAuthHandler中的recaptchaVerifierForWebProvider函数,该函数提供一个布尔值来检查当前平台是否为Web。
注意:如果不是Web平台,不要传递RecaptchaVerifier实例,否则会发生错误。
示例:
recaptchaVerifierForWebProvider: (isWeb) {
if (isWeb) return RecaptchaVerifier();
},
还可以显示一个内联小部件,用户必须明确按下以验证自己。

要添加内联小部件,指定RecaptchaVerifier实例的容器参数的DOM元素ID。元素必须存在且为空,否则将抛出错误。如果没有提供容器参数,则小部件将呈现为“不可见”。
RecaptchaVerifier(
container: 'recaptcha',
size: RecaptchaVerifierSize.compact,
theme: RecaptchaVerifierTheme.dark,
onSuccess: () => print('reCAPTCHA Completed!'),
onError: (FirebaseAuthException error) => print(error),
onExpired: () => print('reCAPTCHA Expired!'),
),
如果在身份验证完成后reCAPTCHA徽章未自动消失,尝试在onLoginSuccess中添加以下代码,以便在登录过程完成后消失。
首先从dart:html导入querySelector。
import 'dart:html' show querySelector;
然后在onLoginSuccess回调中添加以下代码。
final captcha = querySelector('#__ff-recaptcha-container');
if (captcha != null) captcha.hidden = true;
如果您想完全禁用reCAPTCHA徽章(通常出现在右下角),请在web/index.html中添加以下CSS样式,位于其他标签之外。
<style>
.grecaptcha-badge { visibility: hidden; }
</style>
我通常如何使用它
我通常有一个电话号码输入字段,用于处理电话号码输入。然后将电话号码传递给VerifyPhoneNumberScreen小部件,来自示例应用。
// 可能有一些UI或对话框来获取电话号码
final phoneNumber = _getPhoneNumber();
// 然后调用
void _verifyPhoneNumber() async {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => VerifyPhoneNumberScreen(phoneNumber: phoneNumber),
),
);
}
/// 在 [VerifyPhoneNumberScreen] 的 onLoginSuccess 回调中路由到主屏幕或其他地方
示例使用
import 'package:firebase_phone_auth_handler/firebase_phone_auth_handler.dart';
import 'package:flutter/material.dart';
import 'package:phone_auth_handler_demo/screens/home_screen.dart';
import 'package:phone_auth_handler_demo/utils/helpers.dart';
import 'package:phone_auth_handler_demo/widgets/custom_loader.dart';
import 'package:phone_auth_handler_demo/widgets/pin_input_field.dart';
class VerifyPhoneNumberScreen extends StatefulWidget {
static const id = 'VerifyPhoneNumberScreen';
final String phoneNumber;
const VerifyPhoneNumberScreen({
Key? key,
required this.phoneNumber,
}) : super(key: key);
[@override](/user/override)
State<VerifyPhoneNumberScreen> createState() => _VerifyPhoneNumberScreenState();
}
class _VerifyPhoneNumberScreenState extends State<VerifyPhoneNumberScreen> with WidgetsBindingObserver {
bool isKeyboardVisible = false;
late final ScrollController scrollController;
[@override](/user/override)
void initState() {
scrollController = ScrollController();
WidgetsBinding.instance.addObserver(this);
super.initState();
}
[@override](/user/override)
void dispose() {
WidgetsBinding.instance.removeObserver(this);
scrollController.dispose();
super.dispose();
}
[@override](/user/override)
void didChangeMetrics() {
final bottomViewInsets = WidgetsBinding.instance.window.viewInsets.bottom;
isKeyboardVisible = bottomViewInsets > 0;
}
// 当pin输入字段获得焦点时滚动到底部
Future<void> _scrollToBottomOnKeyboardOpen() async {
while (!isKeyboardVisible) {
await Future.delayed(const Duration(milliseconds: 50));
}
await Future.delayed(const Duration(milliseconds: 250));
await scrollController.animateTo(
scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 250),
curve: Curves.easeIn,
);
}
[@override](/user/override)
Widget build(BuildContext context) {
return SafeArea(
child: FirebasePhoneAuthHandler(
phoneNumber: widget.phoneNumber,
signOutOnSuccessfulVerification: false,
linkWithExistingUser: false,
autoRetrievalTimeOutDuration: const Duration(seconds: 60),
otpExpirationDuration: const Duration(seconds: 60),
onCodeSent: () {
log(VerifyPhoneNumberScreen.id, msg: 'OTP sent!');
},
onLoginSuccess: (userCredential, autoVerified) async {
log(
VerifyPhoneNumberScreen.id,
msg: autoVerified
? 'OTP was fetched automatically!'
: 'OTP was verified manually!',
);
showSnackBar('Phone number verified successfully!');
log(
VerifyPhoneNumberScreen.id,
msg: 'Login Success UID: ${userCredential.user?.uid}',
);
Navigator.pushNamedAndRemoveUntil(
context,
HomeScreen.id,
(route) => false,
);
},
onLoginFailed: (authException, stackTrace) {
log(
VerifyPhoneNumberScreen.id,
msg: authException.message,
error: authException,
stackTrace: stackTrace,
);
switch (authException.code) {
case 'invalid-phone-number':
// 无效的电话号码
return showSnackBar('Invalid phone number!');
case 'invalid-verification-code':
// 输入的OTP无效
return showSnackBar('The entered OTP is invalid!');
// 处理其他错误代码
default:
showSnackBar('Something went wrong!');
// 进一步处理错误
}
},
onError: (error, stackTrace) {
log(
VerifyPhoneNumberScreen.id,
error: error,
stackTrace: stackTrace,
);
showSnackBar('An error occurred!');
},
builder: (context, controller) {
return Scaffold(
appBar: AppBar(
leadingWidth: 0,
leading: const SizedBox.shrink(),
title: const Text('Verify Phone Number'),
actions: [
if (controller.codeSent)
TextButton(
onPressed: controller.isOtpExpired
? () async {
log(VerifyPhoneNumberScreen.id, msg: 'Resend OTP');
await controller.sendOTP();
}
: null,
child: Text(
controller.isOtpExpired
? 'Resend'
: '${controller.otpExpirationTimeLeft.inSeconds}s',
style: const TextStyle(color: Colors.blue, fontSize: 18),
),
),
const SizedBox(width: 5),
],
),
body: controller.isSendingCode
? Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
CustomLoader(),
SizedBox(height: 50),
Center(
child: Text(
'Sending OTP',
style: TextStyle(fontSize: 25),
),
),
],
)
: ListView(
padding: const EdgeInsets.all(20),
controller: scrollController,
children: [
Text(
"We've sent an SMS with a verification code to ${widget.phoneNumber}",
style: const TextStyle(fontSize: 25),
),
const SizedBox(height: 10),
const Divider(),
if (controller.isListeningForOtpAutoRetrieve)
Column(
children: const [
CustomLoader(),
SizedBox(height: 50),
Text(
'Listening for OTP',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 15),
Divider(),
Text('OR', textAlign: TextAlign.center),
Divider(),
],
),
const SizedBox(height: 15),
const Text(
'Enter OTP',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 15),
PinInputField(
length: 6,
onFocusChange: (hasFocus) async {
if (hasFocus) await _scrollToBottomOnKeyboardOpen();
},
onSubmit: (enteredOtp) async {
final verified = await controller.verifyOtp(enteredOtp);
if (verified) {
// 验证成功
// 将调用 onLoginSuccess 处理程序
} else {
// 验证失败
// 将调用 onLoginFailed 或 onError 回调,并传递错误
}
},
),
],
),
);
},
),
);
}
}
更多关于Flutter电话认证插件firebase_phone_auth_handler_flave的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter电话认证插件firebase_phone_auth_handler_flave的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
firebase_phone_auth_handler_flave 是一个用于在 Flutter 应用中实现 Firebase 电话号码认证的插件。它简化了与 Firebase 电话号码认证的集成过程,使你能够轻松地在应用中实现电话号码认证功能。
以下是如何在 Flutter 项目中使用 firebase_phone_auth_handler_flave 插件的步骤:
1. 添加依赖
首先,你需要在 pubspec.yaml 文件中添加 firebase_phone_auth_handler_flave 插件的依赖。
dependencies:
flutter:
sdk: flutter
firebase_phone_auth_handler_flave: ^1.0.0 # 请使用最新版本
然后运行 flutter pub get 来获取依赖。
2. 配置 Firebase
在使用 Firebase 电话号码认证之前,你需要在 Firebase 控制台中配置你的项目。
- 登录 Firebase 控制台。
- 创建一个新的 Firebase 项目(如果还没有)。
- 在 Firebase 控制台中,导航到 Authentication 部分,并启用 电话 作为登录方法。
- 按照 Firebase 的指示配置你的 Android 和 iOS 应用(如果需要)。
3. 初始化 Firebase
在你的 Flutter 项目中,确保你已经初始化了 Firebase。通常,你可以在 main.dart 文件中进行初始化。
import 'package:firebase_core/firebase_core.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
4. 使用 firebase_phone_auth_handler_flave
接下来,你可以在你的应用中使用 firebase_phone_auth_handler_flave 插件来实现电话号码认证。
import 'package:flutter/material.dart';
import 'package:firebase_phone_auth_handler_flave/firebase_phone_auth_handler_flave.dart';
class PhoneAuthScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Phone Authentication'),
),
body: Center(
child: FirebasePhoneAuthHandler(
onCodeSent: (verificationId) {
// 当验证码发送时调用
print('Verification code sent: $verificationId');
},
onVerificationFailed: (error) {
// 当验证失败时调用
print('Verification failed: $error');
},
onVerificationCompleted: (credential) {
// 当验证成功时调用
print('Verification completed: $credential');
},
onCodeAutoRetrievalTimeout: (verificationId) {
// 当自动获取验证码超时时调用
print('Auto retrieval timeout: $verificationId');
},
phoneNumber: '+1234567890', // 替换为用户的电话号码
),
),
);
}
}
5. 处理验证码
在 onCodeSent 回调中,你可以处理验证码的输入。通常,你需要显示一个输入框,让用户输入收到的验证码,然后使用 PhoneAuthProvider.credential 来验证验证码。
FirebasePhoneAuthHandler(
onCodeSent: (verificationId) {
// 显示验证码输入框
showDialog(
context: context,
builder: (context) {
String smsCode = '';
return AlertDialog(
title: Text('Enter Verification Code'),
content: TextField(
onChanged: (value) {
smsCode = value;
},
decoration: InputDecoration(hintText: 'Enter code'),
),
actions: [
TextButton(
onPressed: () async {
final credential = PhoneAuthProvider.credential(
verificationId: verificationId,
smsCode: smsCode,
);
await FirebaseAuth.instance.signInWithCredential(credential);
Navigator.pop(context);
},
child: Text('Submit'),
),
],
);
},
);
},
// 其他回调...
);
6. 处理认证结果
在 onVerificationCompleted 回调中,你可以处理认证成功后的逻辑,例如导航到主屏幕或显示欢迎消息。
FirebasePhoneAuthHandler(
onVerificationCompleted: (credential) async {
await FirebaseAuth.instance.signInWithCredential(credential);
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => HomeScreen()),
);
},
// 其他回调...
);
7. 处理错误
在 onVerificationFailed 回调中,你可以处理认证失败的情况,例如显示错误消息。
FirebasePhoneAuthHandler(
onVerificationFailed: (error) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Verification failed: $error')),
);
},
// 其他回调...
);

