Flutter短信自动填充插件flutter_sms_autofill的使用

发布于 1周前 作者 ionicwang 来自 Flutter

Flutter短信自动填充插件flutter_sms_autofill的使用

Flutter插件用于提供短信验证码自动填充支持。

对于iOS设备,短信自动填充功能是默认提供的,但对于Android设备,该插件非常有用。由于使用了SMSRetriever API,因此无需请求用户授权读取短信权限。

使用方法

您可以使用两个小部件来自动填充短信验证码:PinFieldAutoFill 和 TextFieldPinAutoFill。

在向后端发送手机号码之前,您需要告知插件监听包含验证码的短信。

为此,您需要执行以下操作:

await SmsAutoFill().listenForCode;

这将在5分钟内监听包含验证码的短信,接收到短信后将自动填充相应的组件。

PinFieldAutoFill

PinFieldAutoFill
PinFieldAutoFill(
  decoration: // 可以选择UnderlineDecoration, BoxLooseDecoration或BoxTightDecoration,更多信息请访问https://github.com/TinoGuo/pin_input_text_field
  currentCode: // 预填充验证码
  onCodeSubmitted: // 点击提交按钮时的回调
  onCodeChanged: // 验证码改变时的回调
  codeLength: // 验证码长度,默认为6
)

TextFieldPinAutoFill

TextFieldPinAutoFill
TextFieldPinAutoFill(
  decoration: // 基本的InputDecoration
  currentCode: // 预填充验证码
  onCodeSubmitted: // 点击提交按钮时的回调
  onCodeChanged: // 验证码改变时的回调
  codeLength: // 验证码长度,默认为6
)

Android短信约束

为了使验证码能够被接收,短信必须遵循以下规则(详情请参阅:Google Identity SMS Retriever 文档):

  • 长度不得超过140字节
  • 包含一次性验证码,客户端将其发送回服务器以完成验证流程
  • 结尾必须有11个字符的哈希字符串以标识您的应用

例如:

ExampleApp: 您的验证码是123456
FA+9qCX9VSu

辅助功能

PhoneFieldHint [仅限Android]

PhoneFieldHint 是一个允许用户输入系统电话号码的小部件,并且如果用户选择了电话号码,会自动填充该小部件。

Phone hint
PhoneFieldHint()

自定义CodeAutoFill

如果您想要创建一个可以自动填充短信验证码的自定义组件,可以使用CodeAutoFill混合类,它提供了以下功能:

  • listenForCode():在接收到短信时监听验证码,需要在initState中调用。
  • cancel():取消监听验证码,需要在dispose中调用。
  • codeUpdated():当验证码接收时被调用,可以通过code字段获取值。
  • unregisterListener():注销广播接收器,需要在dispose中调用。

应用签名

要在运行时获取应用签名,只需调用SmsAutoFill上的getAppSignature属性。示例代码可以在示例应用中找到。

示例代码

import 'package:flutter/material.dart';
import 'package:flutter_sms_autofill/flutter_sms_autofill.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.light(),
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  [@override](/user/override)
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  String _code = "";
  String signature = "{{ app signature }}";

  [@override](/user/override)
  void initState() {
    super.initState();
  }

  [@override](/user/override)
  void dispose() {
    SmsAutoFill().unregisterListener();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.light(),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('插件示例应用'),
        ),
        body: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Column(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              PhoneFieldHint(),
              Spacer(),
              PinFieldAutoFill(
                decoration: UnderlineDecoration(
                  textStyle: TextStyle(fontSize: 20, color: Colors.black),
                  colorBuilder: FixedColorBuilder(Colors.black.withOpacity(0.3)),
                ),
                currentCode: _code,
                onCodeSubmitted: (code) {},
                onCodeChanged: (code) {
                  if (code!.length == 6) {
                    FocusScope.of(context).requestFocus(FocusNode());
                  }
                },
              ),
              Spacer(),
              TextFieldPinAutoFill(
                currentCode: _code,
              ),
              Spacer(),
              ElevatedButton(
                child: Text('监听短信验证码'),
                onPressed: () async {
                  await SmsAutoFill().listenForCode;
                },
              ),
              ElevatedButton(
                child: Text('设置验证码为123456'),
                onPressed: () async {
                  setState(() {
                    _code = '123456';
                  });
                },
              ),
              SizedBox(height: 8.0),
              Divider(height: 1.0),
              SizedBox(height: 4.0),
              Text("应用签名: $signature"),
              SizedBox(height: 4.0),
              ElevatedButton(
                child: Text('获取应用签名'),
                onPressed: () async {
                  signature = await SmsAutoFill().getAppSignature;
                  setState(() {});
                },
              ),
              ElevatedButton(
                onPressed: () {
                  Navigator.of(context).push(MaterialPageRoute(builder: (_) => CodeAutoFillTestPage()));
                },
                child: Text("测试CodeAutoFill混合类"),
              )
            ],
          ),
        ),
      ),
    );
  }
}

class CodeAutoFillTestPage extends StatefulWidget {
  [@override](/user/override)
  _CodeAutoFillTestPageState createState() => _CodeAutoFillTestPageState();
}

class _CodeAutoFillTestPageState extends State<CodeAutoFillTestPage> with CodeAutoFill {
  String? appSignature;
  String? otpCode;

  [@override](/user/override)
  void codeUpdated() {
    setState(() {
      otpCode = code!;
    });
  }

  [@override](/user/override)
  void initState() {
    super.initState();
    listenForCode();

    SmsAutoFill().getAppSignature.then((signature) {
      setState(() {
        appSignature = signature;
      });
    });
  }

  [@override](/user/override)
  void dispose() {
    super.dispose();
    cancel();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    final textStyle = TextStyle(fontSize: 18);

    return Scaffold(
      appBar: AppBar(
        title: Text("监听验证码"),
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.fromLTRB(32, 32, 32, 0),
            child: Text(
              "当前应用签名: $appSignature",
            ),
          ),
          const Spacer(),
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 32),
            child: Builder(
              builder: (_) {
                if (otpCode == null) {
                  return Text("正在监听验证码...", style: textStyle);
                }
                return Text("验证码已接收: $otpCode", style: textStyle);
              },
            ),
          ),
          const Spacer(),
        ],
      ),
    );
  }
}

更多关于Flutter短信自动填充插件flutter_sms_autofill的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter短信自动填充插件flutter_sms_autofill的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何使用 flutter_sms_autofill 插件在 Flutter 应用中实现短信自动填充的示例代码。这个插件允许你的应用监听和读取收到的短信内容,并在适当的表单字段中自动填充验证码(如 OTP)。

1. 添加依赖

首先,在你的 pubspec.yaml 文件中添加 flutter_sms_autofill 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_sms_autofill: ^2.0.0 # 请检查最新版本号

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

2. 导入插件

在你的 Dart 文件中导入插件:

import 'package:flutter_sms_autofill/flutter_sms_autofill.dart';

3. 请求短信监听权限

在应用启动时请求短信监听权限。注意,这需要在 AndroidManifest.xml 中声明必要的权限,并且在运行时请求这些权限(针对 Android 6.0 及以上版本)。

AndroidManifest.xml 中添加权限:

<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>

在 Dart 代码中请求权限(仅 Android):

import 'package:permission_handler/permission_handler.dart';

Future<void> requestSmsPermissions() async {
  var status = await Permission.sms.status;
  if (!status.isGranted) {
    var result = await Permission.sms.request();
    if (result.isGranted) {
      print("SMS permission granted");
    } else {
      print("SMS permission denied");
    }
  } else {
    print("SMS permission already granted");
  }
}

4. 设置短信监听器

在你的主页面或需要监听短信的地方设置短信监听器:

class _MyAppState extends State<MyApp> {
  final TextEditingController _controller = TextEditingController();

  @override
  void initState() {
    super.initState();
    // 请求短信权限
    requestSmsPermissions();

    // 设置短信监听器
    FlutterSmsAutofill()
      ..addSmsListener((SmsMessage message) {
        // 假设短信内容是 OTP
        RegExp regExp = RegExp(r'\d{6}'); // 假设 OTP 是 6 位数
        Iterable<Match> matches = regExp.allMatches(message.body);
        if (matches.isNotEmpty) {
          String otp = matches.first.group(0)!;
          _controller.value = _controller.value.copyWith(text: otp);
        }
      })
      ..startListening();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter SMS Autofill Example'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            TextField(
              controller: _controller,
              decoration: InputDecoration(
                labelText: 'Enter OTP',
              ),
              keyboardType: TextInputType.number,
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    // 停止监听短信
    FlutterSmsAutofill().stopListening();
    super.dispose();
  }
}

5. 完整示例

将上述代码片段整合到一个完整的 Flutter 应用中:

import 'package:flutter/material.dart';
import 'package:flutter_sms_autofill/flutter_sms_autofill.dart';
import 'package:permission_handler/permission_handler.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final TextEditingController _controller = TextEditingController();

  @override
  void initState() {
    super.initState();
    requestSmsPermissions();

    FlutterSmsAutofill()
      ..addSmsListener((SmsMessage message) {
        RegExp regExp = RegExp(r'\d{6}'); // 假设 OTP 是 6 位数
        Iterable<Match> matches = regExp.allMatches(message.body);
        if (matches.isNotEmpty) {
          String otp = matches.first.group(0)!;
          _controller.value = _controller.value.copyWith(text: otp);
        }
      })
      ..startListening();
  }

  Future<void> requestSmsPermissions() async {
    var status = await Permission.sms.status;
    if (!status.isGranted) {
      var result = await Permission.sms.request();
      if (!result.isGranted) {
        // 处理权限被拒绝的情况
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter SMS Autofill Example'),
        ),
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              TextField(
                controller: _controller,
                decoration: InputDecoration(
                  labelText: 'Enter OTP',
                ),
                keyboardType: TextInputType.number,
              ),
            ],
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    FlutterSmsAutofill().stopListening();
    _controller.dispose();
    super.dispose();
  }
}

这段代码展示了如何使用 flutter_sms_autofill 插件来监听短信并在接收到 OTP 时自动填充到 TextField 中。注意,实际使用中可能需要根据具体需求调整正则表达式和权限请求逻辑。

回到顶部