Flutter电话号码快速登录插件easy_phone_sign_in的使用

Flutter电话号码快速登录插件easy_phone_sign_in的使用

此Flutter包旨在简化Google电话登录功能在应用中的集成,使其过程无缝且高效。

特性

  • 与Google登录轻松集成
  • 支持Android和iOS平台
  • 提供简洁的UI用于手机号码输入和验证

安装

要使用此包,请在pubspec.yaml文件中添加phone_sign_in依赖:

dependencies:
  phone_sign_in: ^x.x.x

并根据Firebase电话认证设置进行设置。

调试

如果在PhoneSignIn小部件中将debug设置为true,该小部件将在控制台中显示调试消息。这对于调试非常有用。

手机号输入

如果用户的手机号以+开头,则用户输入的手机号将被视为国际格式。在这种情况下,应用会忽略选定的国家信息,并使用用户输入的手机号进行登录。

例如,如果用户选择国家为“菲律宾”,然后输入类似+1 1111 1111 11的美国手机号码,则尽管国家选择设置为“菲律宾”,它会忽略这一点,并尝试使用用户输入的手机号码+1 1111 1111 11进行Firebase登录。

如果手机号以0开头,则会被移除。因此,在检查手机号码时,不要让它以0开头。

国家选择选项

  • countryPickerOptions:允许用户选择国家。

  • 如果省略此选项,则小部件不会在屏幕上显示国家选择。

    • 如果不需要国家选择?
      • 可能因为你只需要在应用中使用一个国家代码。
      • 或者,用户可以与手机号一起输入国家代码,如+82
  • moveAlongWithKeyboard:默认情况下,搜索结果可能会被键盘隐藏。将其设置为true,则结果不会被键盘遮挡。

固定国家代码

启用此选项后,将隐藏选择国家代码的按钮,并将国家代码固定为一个固定的值。因此,用户无法更改国家选择。

Firebase认证语言代码

firebaseAuthLanguageCode是Firebase电话登录使用的默认语言。这与Firebase Auth中使用的选项相同。

完整手机号码回调

PhoneSignIn小部件会自动将用户输入的手机号格式化为国际号码,基于所选国家。

但是,如果你希望自定义国际手机号码,可以利用onCompletePhoneNumber回调。当小部件需要国际格式的手机号码时,会调用此函数。它只会传递用户输入的手机号码,不包括国家代码。因此,如果你想使用用户选择的国家代码,应避免设置此回调函数,让小部件自动构建国际手机号码。

你可能希望通过自动添加国家代码来简化用户流程。例如,在韩国,手机号码总是以010开头,在菲律宾,它们总是以09开头。因此,如果用户输入他们的手机号码为01012345678,你可以通过程序将其更新为+821012345678转换为国际格式。同样,如果用户输入他们的手机号码为091212345678,你可以将其更新为+6391212345678

此函数主要用于当未指定countryPickerOptionscountryCode时。在这种情况下,由于用户无法在屏幕上选择国家代码,因此用于通过程序添加国家代码。

注意,如果用户输入的手机号以+开头,onCompletePhoneNumber函数不会被调用。换句话说,小部件识别用户的输入为完整的国际手机号码,不会调用onCompletePhoneNumber函数将其转换为国际格式。

因此,如果未指定countryPickerOptionscountryCode,并且用户未输入以+开头的手机号,onCompletePhoneNumber将被调用并返回国际格式的手机号。

onCompletePhoneNumber函数接收用户输入的手机号,去除不必要的特殊字符。如果手机号以0开头,它将被移除。例如,如果用户输入010-1111-2222,则1011112222作为参数传递。

当前用户链接

如果linkCurrentUser设置为true,它将尝试将当前用户帐户链接到提供的电话登录凭据。如果提供的电话登录账户已存在,则只执行正常登录现有账户。如果用户尚未登录,则仅执行正常登录。

请注意,当用户以电话号码登录时,他不能与另一个电话号码凭证链接。更多详情请参阅Firebase Auth文档。

是否手机号码已注册

linkCurrentUser设置为true时,必须设置此回调函数,并返回true表示手机号码已注册。

显示手机号码回调

onDisplayPhoneNumber是一个回调函数,返回要在屏幕上显示的手机号码。onCompletePhoneNumber函数返回的值用于Firebase电话登录,但此函数返回的手机号码只是以用户友好的格式显示在屏幕上。

例如,如果只有韩国人注册,你可以通过countryCode选项将国家代码固定为+82。在这种情况下,显示为010-1234-5678而不是+821012345678更符合用户习惯。这就是这个函数的作用。

特别是在输入手机号码并发送短信验证码之后,你需要在屏幕上显示手机号码。你可以使用此函数以用户友好的格式显示手机号码。

onDisplayPhoneNumber函数接收国际手机号码。更准确地说,它接收onCompletePhoneNumber函数返回的值。

无国家代码选择器

如果应用未设置countryCodecountryPickerOption,则小部件无法生成国际手机号码。而且,用户无法选择国家。但是,用户可以通过手动输入国际手机号码来实现。

错误处理

如果发生错误,onSignInFailed回调函数将被调用,并将FirebaseAuthException作为参数传递。你可以使用此错误参数适当地通知用户错误消息。

PhoneSignIn(
  onSignInFailed: (FirebaseAuthException e) {
    if (e.code == 'web-context-cancelled') {
      print('The interaction was cancelled by the user.');
    } else if (e.code == 'missing-client-identifier') {
      print("We couldn't verify your phone number at the moment. Please ensure you entered a valid phone number and try again.");
    } else if (e.code == 'too-many-requests') {
      print('We have blocked all requests from this device due to unsual activity. Please try again later');
    } else if (e.code == 'invalid-verification-code') {
      print('Oops! Incorrect code, Please double-check the code sent to your phone and try again.');
    } else {
      print('FirebaseAuthException : $e');
      throw e;
    }
  }
)

示例

以下是包含国家选择的完整示例代码。您可以自由复制、粘贴并根据您的应用需求进行调整。

Padding(
  padding: const EdgeInsets.symmetric(horizontal: sm),
  child: PhoneSignIn(
    labelCountryPicker: Padding(
      padding: const EdgeInsets.only(bottom: xxs),
      child: Padding(
        padding: const EdgeInsets.fromLTRB(0, 24, 0, 8),
        child: Text(
          '1. 选择国家',
          style: context.titleLarge,
        ),
      ),
    ),
    labelCountryPickerSelected: Padding(
      padding: const EdgeInsets.only(bottom: xxs),
      child: Padding(
        padding: const EdgeInsets.fromLTRB(0, 24, 0, 8),
        child: Text(
          '1. 选择国家',
        ),
      ),
    ),
    labelEmptyCountry: Container(
      decoration: BoxDecoration(
        border: Border.all(
          color: context.colorScheme.onSurface,
          width: 1.8,
        ),
        borderRadius: BorderRadius.circular(16),
      ),
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Text('选择国家描述'),
      ),
    ),
    labelChangeCountry: Padding(
      padding: const EdgeInsets.fromLTRB(0, 8, 0, 8),
      child: Text('更改国家'),
    ),
    labelPhoneNumber: Padding(
      padding: const EdgeInsets.fromLTRB(0.0, 48, 0, 8.0),
      child: Text(
        '2. 输入您的手机号码',
        style: context.titleLarge,
      ),
    ),
    labelPhoneNumberSelected: Padding(
      padding: const EdgeInsets.only(top: 48),
      child: Text('2. 电话号码'),
    ),
    hintTextPhoneNumberTextField: '电话号码',
    labelOnSmsCodeTextField: Padding(
      padding: const EdgeInsets.fromLTRB(0, 48, 0, 8),
      child: Text(
        '3. 输入短信验证码',
        style: context.titleLarge,
      ),
    ),
    hintTextSmsCodeTextField: '短信验证码',
    labelVerifyPhoneNumberButton: Text('验证电话号码'),
    labelVerifySmsCodeButton: Text('验证短信验证码'),
    labelRetry: Text('重试'),
    linkCurrentUser: true,
    onSignInSuccess: afterSignIn,
    onSignInFailed: onSignInFailed,
    countryPickerOptions: const CountryPickerOptions(
      favorite: ['US', 'KR'],
    ),
    specialAccounts: const SpecialAccounts(
      reviewEmail: 'review@email.com',
      reviewPassword: '12345zB,*c',
      reviewPhoneNumber: '+11234567890',
      reviewSmsCode: '123456',
      emailLogin: true,
    ),
  ),
)

/// 清理之前匿名用户,登录到现有账户。
///
afterSignIn() async {
  if (context.mounted) {
    context.pop();
  }
}

onSignInFailed(FirebaseAuthException e) {
  print('onSignInFailed() -> FirebaseAuthException : $e');
  if (e.code == 'web-context-cancelled') {
    // 交互被用户取消
    error(
      context: context,
      message: Text('登录被取消'),
    );
  } else if (e.code == 'missing-client-identifier') {
    error(
      context: context,
      message: Text('您的电话号码无法验证。请确保您输入了正确的电话号码并重试。'),
    );
  } else if (e.code == 'too-many-requests') {
    error(
      context: context,
      message: Text('由于多次尝试,所有来自此设备的请求都被阻止。请稍后再试。'),
    );
  } else if (e.code == 'invalid-verification-code') {
    error(
      context: context,
      message: Text('哦!验证码错误。请检查发送到您手机上的验证码并重试。'),
    );
  } else if (e.code == 'invalid-phone-number') {
    error(
      context: context,
      message: const Text('无效的电话号码'),
    );
  } else {
    print('FirebaseAuthException : $e');
    throw e;
  }
}

异常

当用户输入无效手机号码时,会抛出以下异常:

Exception: [@phone_sign_in](/user/phone_sign_in)/malformed-phone-number Phone number is empty or malformed.

更多关于Flutter电话号码快速登录插件easy_phone_sign_in的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter电话号码快速登录插件easy_phone_sign_in的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用easy_phone_sign_in插件实现电话号码快速登录的示例代码。easy_phone_sign_in插件使得集成电话号码登录变得简单,通常用于Firebase身份验证服务。

首先,确保你已经在pubspec.yaml文件中添加了easy_phone_sign_in依赖:

dependencies:
  flutter:
    sdk: flutter
  easy_phone_sign_in: ^最新版本号  # 请替换为实际最新版本号

然后运行flutter pub get来获取依赖。

接下来,你需要配置Firebase项目以支持电话号码身份验证。这通常涉及在Firebase控制台中启用电话号码身份验证功能,并可能需要进行一些验证步骤(例如,通过短信验证)。

以下是一个基本的Flutter应用程序示例,展示了如何使用easy_phone_sign_in插件进行电话号码登录:

import 'package:flutter/material.dart';
import 'package:easy_phone_sign_in/easy_phone_sign_in.dart';
import 'package:firebase_auth/firebase_auth.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Phone Sign In Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: PhoneSignInScreen(),
    );
  }
}

class PhoneSignInScreen extends StatefulWidget {
  @override
  _PhoneSignInScreenState createState() => _PhoneSignInScreenState();
}

class _PhoneSignInScreenState extends State<PhoneSignInScreen> {
  final EasyPhoneSignIn _easyPhoneSignIn = EasyPhoneSignIn();
  String _verificationId;
  String _smsCode;
  String _phoneNumber = '+1'; // 默认国家代码,可以根据需要更改

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Phone Sign In'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            TextField(
              decoration: InputDecoration(
                labelText: 'Phone Number',
                prefixText: _phoneNumber,
                border: OutlineInputBorder(),
              ),
              keyboardType: TextInputType.phone,
              onChanged: (value) {
                setState(() {
                  _phoneNumber = _phoneNumber + value;
                });
              },
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () async {
                if (_phoneNumber.length <= 3) { // 简单验证,确保输入了至少一些数字
                  return;
                }

                // 发送验证码
                await _easyPhoneSignIn.verifyPhoneNumber(
                  phoneNumber: _phoneNumber,
                  verificationCompleted: (AuthCredential credential) async {
                    // 自动验证完成(即时验证)
                    FirebaseAuth.instance.signInWithCredential(credential);
                    // 处理登录成功逻辑
                  },
                  verificationFailed: (FirebaseAuthException e) {
                    // 处理验证失败
                    print('Verification failed: ${e.message}');
                  },
                  codeSent: (String id, [String token]) {
                    // 保存验证码ID
                    setState(() {
                      _verificationId = id;
                    });
                    // 提示用户输入验证码
                  },
                  codeAutoRetrievalTimeout: (String id) {
                    // 自动检索验证码超时
                    setState(() {
                      _verificationId = id;
                    });
                  },
                );
              },
              child: Text('Send Code'),
            ),
            SizedBox(height: 20),
            TextField(
              decoration: InputDecoration(
                labelText: 'SMS Code',
                border: OutlineInputBorder(),
              ),
              keyboardType: TextInputType.number,
              onChanged: (value) {
                setState(() {
                  _smsCode = value;
                });
              },
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () async {
                if (_verificationId == null || _smsCode.isEmpty) {
                  return;
                }

                // 使用验证码登录
                AuthCredential credential = PhoneAuthProvider.credential(
                  verificationId: _verificationId,
                  smsCode: _smsCode,
                );
                await FirebaseAuth.instance.signInWithCredential(credential);

                // 处理登录成功逻辑
                print('Sign in successful');
              },
              child: Text('Sign In'),
            ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个简单的UI,允许用户输入他们的电话号码并发送验证码。用户收到验证码后,可以输入验证码并完成登录过程。这个示例代码涵盖了主要的步骤:发送验证码、处理验证码发送结果、以及使用验证码进行登录。

请确保在实际项目中处理更多的错误情况和边界情况,并根据需要调整UI和用户体验。

回到顶部