基于Flutter实现短信验证码监控与转发

 更新时间:2025年03月08日 10:35:28   作者:代码轨迹  
这篇文章主要为大家详细介绍了如何基于Flutter实现短信验证码监控与转发功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

1. 前言

前段时间,我基于deepseek制作了一个基于小红书的自动推文生成发送工作流。然而,先前制作的windows端的工作流到小红书发布时显得异常繁琐,原先的思路是在手机接收到验证码后进入系统进行人为输入,这显然太麻烦了。同时,这一问题当部署到linux服务器上时显得尤为突出,这与自动化的理念显然有些背道而驰。因此,我决定基于flutter制作一个验证码提取转发应用,将手机短信验证码提取出来,通过http接口转发给工作流,从而实现自动化的工作流。

2.开发环境

  • IDE:VSCode
  • 语言:Dart 3.7.0
  • 框架:Flutter 3.29.0

3. 实现思路

3.1 验证码提取

flutter中存在大量不错的第三方短信处理库,例如flutter_sms_inbox, sms_v2, sms_receiver等,但经过测试,许多库在当前开发环境下存在许多问题,因此我最终选择了sms_advanced库进行短信处理。

sms_advanced提供了querySms()这样一个方法,这个方法可以根据条件进行短信查询。以下是方法源码:

/// Query a list of SMS
Future<List<SmsMessage>> querySms({
    int? start,
    int? count,
    String? address,
    int? threadId,
    List<SmsQueryKind> kinds = const [SmsQueryKind.Inbox],
    bool sort = true}) async {
    List<SmsMessage> result = [];
    for (var kind in kinds) {
        result.addAll(await _querySmsWrapper(
        start: start,
        count: count,
        address: address,
        threadId: threadId,
        kind: kind,
    ));
}
if (sort == true) {
    result.sort((a, b) => a.compareTo(b));
}
return (result);
}

可以看到,querySms()方法可以接受多个参数,其中address参数可以指定短信发送者的手机号,kinds参数默认为SmsQueryKind.Inbox,即从收件箱获取短信,从而实现短信提取。然后使用正则表达式对短信内容进行匹配,提取出验证码。

if (messages.isNotEmpty) {
    // 获取第一条短信
    SmsMessage firstMessage = messages.first;
    String? messageBody = firstMessage.body;

    // 使用正则表达式匹配验证码,假设验证码是 6 位数字
    RegExp regex = RegExp(r'\d{6}');
    Match? match = regex.firstMatch(messageBody!);

    if (match != null) {
        String smsCode = match.group(0)!;
        // 发送验证码到 API
        result = await _sendCodeToAPI(smsCode);
    } else {
        result = '未在短信中找到验证码';
    }
} else {
    result = '未找到短信';
}

但后面我发现小红书的验证码发送者手机号并非固定,因此我选择制作一个多条件筛选器。在条件筛选中,我选择先根据手机号做一次短信筛选,如果没有找到,则根据短信内容做一次筛选,如果还是没有找到,则返回未找到短信。这样用户就可以在仅知道验证码发送应用名称的情况下,不填写发送者手机号,获取到短信并提取到验证码。实现代码如下:

SmsQuery query = SmsQuery();
List<SmsMessage>? messages = await query.querySms(
    address: _phoneNumber,
    kinds: [SmsQueryKind.Inbox],
); // 获取收件箱中的短信
if (messages.isEmpty) {
    List<SmsMessage>? messages = await query.querySms(
    kinds: [SmsQueryKind.Inbox],
    ); // 根据条件二进行查询
    for (SmsMessage message in messages) {
        if (message.body?.contains(_targetApp) ?? false) {
            final code = _extractCode(message.body);
            if (code != null) {
                return await _sendCodeToAPI(code);
            }
        }
    }
}

3.2 验证码转发

验证码转发是将提取到的验证码通过http接口转发给工作流。这里我选择使用http库进行http请求,实现代码如下:

Future<String?> _sendCodeToAPI(String code) async {
    try {
        final response = await http.post(
            Uri.parse(_apiEndpoint),
            headers: {'Content-Type': 'application/json'},
            body: jsonEncode({'code': code}),
        );
        if (response.statusCode != 200) {
            return ('Failed to send code: ${response.statusCode}');
        } else {
            return 'Successfully Code sent ';
        }
    } catch (e) {
        return ('Error sending code: $e');
    }
}

3.3 线程通信

由于验证码提取是一个耗时操作,因此我选择将其放在一个子线程中执行,以避免阻塞主线程。这里我选择使用flutter的Isolate进行线程通信。同时,为了更新监控状态并控制监控开始和停止,设计了两个Port,分别是mainpPort和isolatePort。mainpPort用于向接收子线程的监控状态消息,实现监控状态的实时更新;isolatePort用于接收主线程发来的启停信息,当点击停止监控后,由父线程告知子线程停止作业。实现代码如下:

MainPort:

// 在主isolate的接收端口监听中添加状态更新
void _initReceivePort() {
_receivePort = ReceivePort();
_receivePort.listen((message) {
    if (message is String) {
    if (message == 'isolate_stopped') {
        // 处理isolate退出通知
        if (mounted) {
        setState(() {
            _isolate = null;
            isMonitoring = false;
            buttonText = '开始监控';
        });
        }
    } else {
        setState(() => response = message);
        if(message == 'Successfully Code sent') {
        _stopIsolate();
        }
    }
    } else if (message is SendPort) {
    _isolateSendPort = message;
    }
});
}

IsolatePort:

static void _monitorSmsInBackground(List<dynamic> args) async {
    final rootIsolateToken = args[4] as RootIsolateToken;
    BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);

    final apiEndpoint = args[0] as String;
    final targetApp = args[1] as String;
    final phoneNumber = args[2] as String;
    final mainSendPort = args[3] as SendPort;
    final smsHandler = SMSHandler(apiEndpoint, targetApp, phoneNumber);
    final controlPort = ReceivePort();
    mainSendPort.send(controlPort.sendPort);

    final stopCompleter = Completer<void>();
    controlPort.listen((message) {
      if (message == 'stop') {
        stopCompleter.complete();
      }
    });

    try {
      while (!stopCompleter.isCompleted) {
        final value = await smsHandler.initSMSListener().timeout(
          const Duration(seconds: 1),
          onTimeout: () => null,
        );
        print(value);
        mainSendPort.send(value);

        if (stopCompleter.isCompleted) break;
      }
    } finally {
      controlPort.close();
      mainSendPort.send('isolate_stopped'); // 添加退出通知
    }
}

StopIsolate:

// 修改 _stopIsolate 方法,仅发送停止信号,不强制终止Isolate
void _stopIsolate() {
    if (_isolate != null) {
        _isolateSendPort?.send('stop');
    }
}

4. 总结

总体来说,整体项目还是挺简单的。主要就是利用flutter的插件进行短信的监听,然后通过正则表达式提取验证码,最后通过http接口将验证码发送给工作流。但因为初次学习flutter,许多地方没有做详细的优化,仅仅实现了整体功能。工程代码放在github上,有兴趣的可以看看:verify_code_app

到此这篇关于基于Flutter实现短信验证码监控与转发的文章就介绍到这了,更多相关Flutter短信验证码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 深入探究Android动态代理的原理及用途

    深入探究Android动态代理的原理及用途

    动态代理是一种在编程中非常有用的设计模式,它允许你在运行时创建一个代理对象来代替原始对象,以便在方法调用前后执行额外的逻辑,本文将深入探讨Android动态代理的原理、用途和实际示例
    2023-09-09
  • Flutter中数据存储的四种方式小结

    Flutter中数据存储的四种方式小结

    在 Flutter 中,存储是指用于本地和远程存储和管理数据的机制,本给大家介绍了Flutter中不同存储选项的概述和示例,通过代码示例讲解的非常详细,具有一定的参考价值,需要的朋友可以参考下
    2023-11-11
  • Android 自定义加载动画Dialog弹窗效果的示例代码

    Android 自定义加载动画Dialog弹窗效果的示例代码

    这篇文章主要介绍了Android 自定义加载动画Dialog弹窗效果,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-06-06
  • Android窗口小部件基础编写代码实例

    Android窗口小部件基础编写代码实例

    这篇文章主要介绍了Android窗口小部件基础编写代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • Android数据加密之Rsa加密

    Android数据加密之Rsa加密

    这篇文章主要为大家详细介绍了Android数据加密之Rsa加密,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-09-09
  • Android自定义控件实现底部菜单(下)

    Android自定义控件实现底部菜单(下)

    这篇文章主要为大家详细介绍了Android自定义控件实现底部菜单的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-01-01
  • Android读取本地图库与调用摄像头拍摄

    Android读取本地图库与调用摄像头拍摄

    这篇文章主要为大家详细介绍了Android读取本地图库与调用摄像头拍摄,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-04-04
  • Android程序开发之使用PullToRefresh实现下拉刷新和上拉加载

    Android程序开发之使用PullToRefresh实现下拉刷新和上拉加载

    这篇文章主要介绍了Android程序开发之使用PullToRefresh实现下拉刷新和上拉加载的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-07-07
  • Android路由框架Router分析详解

    Android路由框架Router分析详解

    本篇文章主要介绍了Android路由框架Router分析详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • Android 中 FrameLayout 布局及属性的使用详解

    Android 中 FrameLayout 布局及属性的使用详解

    这篇文章主要介绍了Android 中 FrameLayout 布局及属性的使用,FrameLayout 在实现简单布局时非常方便,特别适用于叠加式布局,如显示叠加的图层或浮动按钮等,需要的朋友可以参考下
    2024-03-03

最新评论