尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

url_launcher_ios 深度解析:Flutter iOS 端 URL 启动插件的实现原理与实战配置

url_launcher_ios 深度解析:Flutter iOS 端 URL 启动插件的实现原理与实战配置 url_launcher_ios 深度解析Flutter iOS 端 URL 启动插件的实现原理与实战配置【免费下载链接】pluginsPlugins for Flutter maintained by the Flutter team项目地址: https://gitcode.com/gh_mirrors/pl/plugins导读url_launcher_ios是 Flutter 官方维护的url_launcher插件在 iOS 平台上的联邦化federated实现负责在 iOS 设备上打开https、mailto、tel、sms等各类 URL。本文基于本仓库 packages/url_launcher/url_launcher_ios 目录下的官方文档与源码讲解其使用方法、Info.plist 配置要点、canLaunch/launch等核心方法通道的底层实现以及SFSafariViewController会话管理与 Universal Links 处理原理帮助读者从会用进阶到懂实现。一、插件定位联邦化架构下的 iOS 实现url_launcher_ios的官方 README即 README.md用一句话概括了它的定位它是url_launcher的 iOS implementation。这个定位背后是 Flutter 的endorsed federated plugin联邦化插件架构主插件url_launcher只负责定义面向开发者的统一 API而各平台的具体实现由独立包承担。README 明确说明由于该包是 endorsed 状态开发者只需要正常使用url_launcheriOS 实现包会被自动引入应用无需手动添加依赖。这一点在 pubspec.yaml 中得到了印证flutter: plugin: implements: url_launcher platforms: ios: pluginClass: FLTURLLauncherPlugin dartPluginClass: UrlLauncherIOSimplements: url_launcher声明了该包是主插件的实现端pluginClass: FLTURLLauncherPlugin指向原生 Objective-C 类dartPluginClass: UrlLauncherIOS则指向 Dart 侧的实现类。主插件url_launcher的 README.md 支持矩阵中标注 iOS 最低版本为 11.0与 CHANGELOG.md 中 6.1.0 版本Updates minimum Flutter version to 3.3 and iOS 11的记录一致podspec 文件 url_launcher_ios.podspec 中同样声明了s.platform :ios, 11.0。二、基础用法一行代码启动 URL虽然本仓库的url_launcher_ios文档没有重复贴主插件的示例代码但作为 iOS 实现它的使用入口完全来自url_launcher的公开 API。主插件 README 给出了最典型的用法代码摘自 url_launcher/README.mdimport package:flutter/material.dart; import package:url_launcher/url_launcher.dart; final Uri _url Uri.parse(https://flutter.dev); Futurevoid _launchUrl() async { if (!await launchUrl(_url)) { throw Exception(Could not launch $_url); } }要点如下传入Uri而非字符串URL 必须经过Uri.parse正确编码尤其是包含空格或特殊字符时。Dart 的Uri类一般会自动处理编码。返回值判断launchUrl返回bool为false时说明启动失败应当抛出异常或提供降级逻辑。launchUrl优于canLaunchUrl主插件 README 特别提醒canLaunchUrl在部分场景如 Web 端、移动端缺少必要配置时即使launchUrl可用也会返回false因此当可以提供兜底行为时直接调用launchUrl并处理失败是更好的选择。非 http/https 方案的编码注意主插件 README 指出对http/https之外的 scheme如mailto、sms构造查询参数时应使用query参数配合encodeQueryParameters而不是Uri的queryParameters构造参数——后者受 Dart SDK 编码 bug 影响会把空格编码为String? encodeQueryParameters(MapString, String params) { return params.entries .map((MapEntryString, String e) ${Uri.encodeComponent(e.key)}${Uri.encodeComponent(e.value)}) .join(); } final Uri emailLaunchUri Uri( scheme: mailto, path: smithexample.com, query: encodeQueryParameters(String, String{ subject: Example Subject Symbols are allowed!, }), ); launchUrl(emailLaunchUri);三、iOS 关键配置Info.plist 中的 LSApplicationQueriesSchemes这是 iOS 平台上最容易踩坑的配置点。主插件 README 的 Configuration 一节明确要求凡是在代码里传给canLaunchUrl检查的 URL scheme都必须作为LSApplicationQueriesSchemes条目加入应用的 Info.plist否则canLaunchUrl会返回false。keyLSApplicationQueriesSchemes/key array stringsms/string stringtel/string /array底层原因对应 iOS 的-[UIApplication canOpenURL:]机制系统出于隐私考虑只允许应用查询在LSApplicationQueriesSchemes中声明过的 scheme 是否可打开。在 FLTURLLauncherPlugin.m 中canLaunch正是通过该 API 实现- (BOOL)canLaunchURL:(NSString *)urlString { NSURL *url [NSURL URLWithString:urlString]; UIApplication *application [UIApplication sharedApplication]; return [application canOpenURL:url]; }注意本仓库示例工程 example/ios/Runner/Info.plist 未配置该键因此示例中若要检查sms:、tel:等 scheme必须自行在 Runner 的 Info.plist 中添加。如果 scheme 未声明却调用canLaunch方法返回false而非抛错。四、Dart 侧实现MethodChannel 的封装与注册Dart 端实现位于 lib/url_launcher_ios.dart类名为UrlLauncherIOS实现了url_launcher_platform_interface包中的UrlLauncherPlatform抽象类。4.1 通道定义与实例注册const MethodChannel _channel MethodChannel(plugins.flutter.io/url_launcher_ios); class UrlLauncherIOS extends UrlLauncherPlatform { static void registerWith() { UrlLauncherPlatform.instance UrlLauncherIOS(); } ... }通道名plugins.flutter.io/url_launcher_ios与原生侧registerWithRegistrar中创建的 MethodChannel 名称完全一致见 FLTURLLauncherPlugin.m两者由此建立桥接。registerWith()将UrlLauncherIOS实例注册为全局平台默认实现这在测试代码中也有直接验证单元测试 test/url_launcher_ios_test.dart 断言UrlLauncherIOS.registerWith()之后UrlLauncherPlatform.instance就是UrlLauncherIOS的实例。4.2 三个方法通道Dart 侧共暴露三个方法调用与原生侧handleMethodCall的分发逻辑一一对应FLTURLLauncherPlugin.m通道方法Dart 侧 API原生行为canLaunchcanLaunch(String url)通过canOpenURL:检查 scheme 是否可打开launchlaunch(url, {useSafariVC, ...})根据useSafariVC决定用系统 openURL 还是 SFSafariViewControllercloseWebViewcloseWebView()关闭当前 Safari 会话Dart 侧launch会携带一组参数useSafariVC、enableJavaScript、enableDomStorage、universalLinksOnly、headers跨通道传给原生侧尽管其中enableJavaScript、enableDomStorage、headers在 iOS 原生实现中并未被实际消费——它们主要服务于 Android 与 Web 端的WebView模式。原生侧真正读取的是url、useSafariVC、universalLinksOnly三个参数。五、原生实现原理launch 的两条路径FLTURLLauncherPlugin的handleMethodCall对launch做了分支处理FLTURLLauncherPlugin.mNSNumber *useSafariVC call.arguments[useSafariVC]; if (useSafariVC.boolValue) { [self launchURLInVC:url result:result]; } else { [self launchURL:url call:call result:result]; }5.1 路径一launchURL—— 系统级打开当useSafariVC为false对应LaunchMode.externalApplication时走系统UIApplication打开流程FLTURLLauncherPlugin.mNSNumber *universalLinksOnly call.arguments[universalLinksOnly] ?: 0; NSDictionary *options {UIApplicationOpenURLOptionUniversalLinksOnly : universalLinksOnly}; [application openURL:url options:options completionHandler:^(BOOL success) { result((success)); }];这里值得展开两点universalLinksOnly默认取0。当置为YES即LaunchMode.externalNonBrowserApplication时URL 只会交给注册了该 Universal Link 的应用处理而不会回退到浏览器。若没有匹配的应用openURL的 completionHandler 会收到success NO方法通道即返回false。异步回调openURL:options:completionHandler:是异步的插件把completionHandler的success直接透传给 Flutter 的result因此 Dart 侧的launch是真正的异步等待结果而不是立即返回。5.2 路径二launchURLInVC—— SFSafariViewController 会话当useSafariVC为true对应LaunchMode.inAppBrowserView时URL 会在应用内以SFSafariViewController呈现。这一路径由FLTURLLaunchSession类负责会话管理FLTURLLauncherPlugin.m- (instancetype)initWithUrl:url withFlutterResult:result { self [super init]; if (self) { self.url url; self.flutterResult result; self.safari [[SFSafariViewController alloc] initWithURL:url]; self.safari.delegate self; } return self; }关键逻辑结果在页面加载完成后才返回FLTURLLaunchSession实现了SFSafariViewControllerDelegate在safariViewController:didCompleteInitialLoad:回调中根据didLoadSuccessfully决定向 Flutter 返回YES还是错误对象——也就是说launchUrl的 Future 会一直挂起到 Safari 首屏加载完成。会话单例持有FLTURLLauncherPlugin通过currentSession强引用当前会话closeWebView通道方法会调用会话的close最终触发safariViewControllerDidFinish:回调完成关闭与清理currentSession置 nil。顶层控制器递归查找topViewController会递归遍历视图层级见 FLTURLLauncherPlugin.m兼容 UINavigationController、UITabBarController 以及 presentedViewController 三种常见场景确保 Safari 控制器能被正确 present。六、行为验证单元测试与集成测试仓库提供了两层测试来验证上述行为。单元测试test/url_launcher_ios_test.dart 使用MethodChannel的 mock handler 拦截通道调用并记录日志逐一断言canLaunch(http://example.com/)发送的 channel 参数为{url: http://example.com/}launch发送的参数完整包含useSafariVC、enableJavaScript、enableDomStorage、universalLinksOnly、headers键当平台返回null时Dart 侧canLaunch/launch均回退为false对应源码中的value ?? falsecloseWebView发送的 arguments 为null。集成测试example/integration_test/url_launcher_test.dart 在真实设备/模拟器上运行验证了平台语义expect(await launcher.canLaunch(randomstring), false); // 一般设备上默认都有浏览器 expect(await launcher.canLaunch(http://flutter.dev), true); // 测试设备默认支持 SMS 处理 expect(await launcher.canLaunch(sms:5555555555), true); // tel: 与 mailto: 不一定可打开iOS 模拟器尤其不支持这段测试还揭示了主插件 README 提到的平台限制iOS 模拟器默认没有邮件与电话应用因此mailto:、tel:在模拟器上无法打开。此外URL scheme 能否打开完全取决于设备上是否安装了对应 App。七、常用 URL scheme 速查表主插件 README 提供了跨平台的 scheme 速查表iOS 同样适用Scheme示例动作https:URLhttps://flutter.dev在默认浏览器中打开mailto:email?subjectsubjectbodybodymailto:smithexample.org?subjectNewsbodyNew%20plugin在默认邮件应用中写邮件tel:phonetel:1-555-010-999用默认电话应用拨号sms:phonesms:5550101234用默认短信应用发短信file:pathfile:/home桌面平台按文件关联打开使用sms构造 URI 时主插件 README 特别给出了一种编码写法用Uri.encodeComponent处理 body 中的特殊字符final Uri smsLaunchUri Uri( scheme: sms, path: 0118 999 881 999 119 7253, queryParameters: String, String{ body: Uri.encodeComponent(Example Subject Symbols are allowed!), }, );八、版本与环境要求小结iOS 最低版本11.0见 url_launcher_ios.podspec 与主插件支持矩阵Flutter 最低版本3.3.0、Dart SDK2.18.0 3.0.0见 pubspec.yaml依赖url_launcher_platform_interface: ^2.0.3实现类继承自该包的UrlLauncherPlatform接入方式无需手动依赖url_launcher_ios在 pubspec.yaml 中添加url_launcher即可自动带入本实现包。如需查看更复杂的多场景示例含按钮触发、错误处理等可参考 url_launcher/example 中的示例应用代码。【免费下载链接】pluginsPlugins for Flutter maintained by the Flutter team项目地址: https://gitcode.com/gh_mirrors/pl/plugins创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表