Flutter URL启动与增强功能插件enhanced_url_launcher的使用
Flutter URL启动与增强功能插件enhanced_url_launcher的使用
enhanced_url_launcher
是一个用于在 Flutter 应用中启动 URL 的插件。
支持情况
Android | iOS | Linux | macOS | Web | Windows | |
---|---|---|---|---|---|---|
支持 | SDK 16+ | 11.0+ | Any | 10.11+ | Any | Windows 10+ |
使用方法
要使用此插件,请将 enhanced_url_launcher
添加为 pubspec.yaml
文件中的依赖项。
示例代码
import 'package:flutter/material.dart';
import 'package:enhanced_url_launcher/enhanced_url_launcher.dart';
final Uri _url = Uri.parse('https://flutter.dev');
void main() => runApp(
const MaterialApp(
home: Material(
child: Center(
child: ElevatedButton(
onPressed: _launchUrl,
child: Text('Show Flutter homepage'),
),
),
),
),
);
Future<void> _launchUrl() async {
if (!await launchUrl(_url)) {
throw Exception('Could not launch $_url');
}
}
配置
iOS
在你的 Info.plist
文件中添加任何通过 canLaunchUrl
传递的 URL 方案作为 <key>LSApplicationQueriesSchemes</key>
的条目,否则它将返回 false
。
例如:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>sms</string>
<string>tel</string>
</array>
更多详情请参阅 Apple 文档。
Android
在你的 AndroidManifest.xml
中添加任何通过 canLaunchUrl
传递的 URL 方案作为 <queries>
条目,否则它将从 Android 11 (API 30) 或更高版本开始返回 false
。必须将 <queries>
元素作为根元素的子元素添加到清单中。
例如:
<!-- 提供 API 级别 30 及以上所需的可见性配置 -->
<queries>
<!-- 如果您的应用检查短信支持 -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="sms" />
</intent>
<!-- 如果您的应用检查通话支持 -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="tel" />
</intent>
</queries>
更多详情请参阅 Android 文档。
支持的 URL 方案
提供的 URL 直接传递给主机平台进行处理。因此,支持的 URL 方案取决于平台和已安装的应用。
常用方案包括:
Scheme | Example | Action |
---|---|---|
https:<URL> | https://flutter.dev | 在默认浏览器中打开 <URL> |
mailto:<email address>?subject=<subject>&body=<body> | mailto:smith@example.org?subject=News&body=New%20plugin | 在默认邮件应用中创建发送到 <email address> 的邮件 |
tel:<phone number> | tel:+1-555-010-999 | 使用默认电话应用拨打电话 <phone number> |
sms:<phone number> | sms:5550101234 | 使用默认短信应用发送短信到 <phone number> |
file:<path> | file:/home | 使用默认应用关联打开文件或文件夹,仅在桌面平台上支持 |
更多详情请参阅 iOS 文档 和 Android 文档。
检查支持的方案
如果你需要在运行时知道某个方案是否保证可以工作(例如,根据可用情况调整 UI),你可以使用 canLaunchUrl
进行检查。
但是,canLaunchUrl
即使在某些情况下 launchUrl
会正常工作,也可能会返回 false
(例如,在网络应用程序中,或者在移动设备上缺少必要的配置等)。因此,在可以提供回退行为的情况下,最好直接使用 launchUrl
并处理失败情况。例如,一个原本会使用 mailto
URL 发送反馈电子邮件的 UI 按钮可以在失败时打开基于网页的反馈表单,而不是禁用按钮如果 canLaunchUrl
返回 false
。
编码 URL
URL 必须正确编码,特别是当包含空格或其他特殊字符时。通常这由 Uri
类自动处理。
然而,对于除 http
或 https
之外的任何方案,你应该使用 query
参数和 encodeQueryParameters
函数来处理任何查询参数,而不是使用 Uri
的 queryParameters
构造函数参数,因为存在一个关于 Uri
编码查询参数的 bug。使用 queryParameters
将导致许多情况下空格被转换为 +
。
示例代码
String? encodeQueryParameters(Map<String, String> params) {
return params.entries
.map((MapEntry<String, String> e) =>
'${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
.join('&');
}
// ...
final Uri emailLaunchUri = Uri(
scheme: 'mailto',
path: 'smith@example.com',
query: encodeQueryParameters(<String, String>{
'subject': 'Example Subject & Symbols are allowed!',
}),
);
launchUrl(emailLaunchUri);
SMS 编码略有不同:
final Uri smsLaunchUri = Uri(
scheme: 'sms',
path: '0118 999 881 999 119 7253',
queryParameters: <String, String>{
'body': Uri.encodeComponent('Example Subject & Symbols are allowed!'),
},
);
不由 Uri 处理的 URL
在极少数情况下,你可能需要启动一个主机系统认为有效但无法通过 Uri
表达的 URL。对于这些情况,导入 enhanced_url_launcher_string.dart
后提供了使用字符串的替代 API。
在其他情况下使用这些 API 是强烈不建议的,因为提供无效的 URL 字符串是该插件原始 API 中非常常见的错误来源。
文件方案处理
file:
方案可以在桌面平台上使用:Windows、macOS 和 Linux。
我们建议在调用 launchUrl
之前先检查目录或文件是否存在。
示例代码
final String filePath = testFile.absolute.path;
final Uri uri = Uri.file(filePath);
if (!File(uri.toFilePath()).existsSync()) {
throw Exception('$uri does not exist!');
}
if (!await launchUrl(uri)) {
throw Exception('Could not launch $uri');
}
macOS 文件访问配置
如果需要访问应用沙箱外的文件,你需要有必要的 权限。
浏览器与应用内处理
在某些平台上,web URL 可以在应用内网页视图中启动,也可以在默认浏览器中启动。默认行为取决于平台(请参阅 launchUrl
的文档以了解详细信息),但在支持的平台上可以通过传递 LaunchMode
来使用特定模式。
完整示例 Demo
以下是完整的示例代码,展示了如何使用 enhanced_url_launcher
插件的不同功能。
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:enhanced_url_launcher/enhanced_url_launcher.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'URL Launcher',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'URL Launcher'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
[@override](/user/override)
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _hasCallSupport = false;
Future<void>? _launched;
String _phone = '';
[@override](/user/override)
void initState() {
super.initState();
// 检查电话呼叫支持。
canLaunchUrl(Uri(scheme: 'tel', path: '123')).then((bool result) {
setState(() {
_hasCallSupport = result;
});
});
}
Future<void> _launchInBrowser(Uri url) async {
if (!await launchUrl(
url,
mode: LaunchMode.externalApplication,
)) {
throw Exception('Could not launch $url');
}
}
Future<void> _launchInWebViewOrVC(Uri url) async {
if (!await launchUrl(
url,
mode: LaunchMode.inAppWebView,
webViewConfiguration: const WebViewConfiguration(
headers: <String, String>{'my_header_key': 'my_header_value'}),
)) {
throw Exception('Could not launch $url');
}
}
Future<void> _launchInWebViewWithoutJavaScript(Uri url) async {
if (!await launchUrl(
url,
mode: LaunchMode.inAppWebView,
webViewConfiguration: const WebViewConfiguration(enableJavaScript: false),
)) {
throw Exception('Could not launch $url');
}
}
Future<void> _launchInWebViewWithoutDomStorage(Uri url) async {
if (!await launchUrl(
url,
mode: LaunchMode.inAppWebView,
webViewConfiguration: const WebViewConfiguration(enableDomStorage: false),
)) {
throw Exception('Could not launch $url');
}
}
Future<void> _launchUniversalLinkIos(Uri url) async {
final bool nativeAppLaunchSucceeded = await launchUrl(
url,
mode: LaunchMode.externalNonBrowserApplication,
);
if (!nativeAppLaunchSucceeded) {
await launchUrl(
url,
mode: LaunchMode.inAppWebView,
);
}
}
Widget _launchStatus(BuildContext context, AsyncSnapshot<void> snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return const Text('');
}
}
Future<void> _makePhoneCall(String phoneNumber) async {
final Uri launchUri = Uri(
scheme: 'tel',
path: phoneNumber,
);
await launchUrl(launchUri);
}
[@override](/user/override)
Widget build(BuildContext context) {
// 使用此 URL 的按压事件未基于 'canLaunch' 检查
// 因为假设每个设备都可以启动一个 web URL。
final Uri toLaunch =
Uri(scheme: 'https', host: 'www.cylog.org', path: 'headers/');
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ListView(
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: TextField(
onChanged: (String text) => _phone = text,
decoration: const InputDecoration(
hintText: '输入要拨打的电话号码')),
),
ElevatedButton(
onPressed: _hasCallSupport
? () => setState(() {
_launched = _makePhoneCall(_phone);
})
: null,
child: _hasCallSupport
? const Text('拨打手机')
: const Text('不支持拨打电话'),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(toLaunch.toString()),
),
ElevatedButton(
onPressed: () => setState(() {
_launched = _launchInBrowser(toLaunch);
}),
child: const Text('在浏览器中启动'),
),
const Padding(padding: EdgeInsets.all(16.0)),
ElevatedButton(
onPressed: () => setState(() {
_launched = _launchInWebViewOrVC(toLaunch);
}),
child: const Text('在应用中启动'),
),
ElevatedButton(
onPressed: () => setState(() {
_launched = _launchInWebViewWithoutJavaScript(toLaunch);
}),
child: const Text('在应用中启动 (JavaScript 关闭)'),
),
ElevatedButton(
onPressed: () => setState(() {
_launched = _launchInWebViewWithoutDomStorage(toLaunch);
}),
child: const Text('在应用中启动 (DOM 存储关闭)'),
),
const Padding(padding: EdgeInsets.all(16.0)),
ElevatedButton(
onPressed: () => setState(() {
_launched = _launchUniversalLinkIos(toLaunch);
}),
child: const Text(
'在原生应用中启动通用链接,失败时回退到 Safari。(YouTube)'),
),
const Padding(padding: EdgeInsets.all(16.0)),
ElevatedButton(
onPressed: () => setState(() {
_launched = _launchInWebViewOrVC(toLaunch);
Timer(const Duration(seconds: 5), () {
closeInAppWebView();
});
}),
child: const Text('在应用中启动 + 5 秒后关闭'),
),
const Padding(padding: EdgeInsets.all(16.0)),
Link(
uri: Uri.parse(
'https://pub.dev/documentation/enhanced_url_launcher/latest/link/link-library.html'),
target: LinkTarget.blank,
builder: (BuildContext ctx, FollowLink? openLink) {
return TextButton.icon(
onPressed: openLink,
label: const Text('链接小部件文档'),
icon: const Icon(Icons.read_more),
);
},
),
const Padding(padding: EdgeInsets.all(16.0)),
FutureBuilder<void>(future: _launched, builder: _launchStatus),
],
),
],
),
);
}
}
更多关于Flutter URL启动与增强功能插件enhanced_url_launcher的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter URL启动与增强功能插件enhanced_url_launcher的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
在Flutter中,enhanced_url_launcher
是一个增强版的 URL 启动插件,它提供了比官方 url_launcher
插件更丰富的功能和更好的用户体验。下面是一个使用 enhanced_url_launcher
的代码示例,展示了如何启动一个 URL 并处理一些增强功能。
首先,你需要在 pubspec.yaml
文件中添加 enhanced_url_launcher
依赖:
dependencies:
flutter:
sdk: flutter
enhanced_url_launcher: ^x.y.z # 替换为最新版本号
然后,运行 flutter pub get
来获取依赖。
接下来,在你的 Dart 文件中,你可以这样使用 enhanced_url_launcher
:
import 'package:flutter/material.dart';
import 'package:enhanced_url_launcher/enhanced_url_launcher.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Enhanced URL Launcher Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Enhanced URL Launcher Demo'),
),
body: Center(
child: ElevatedButton(
onPressed: () async {
try {
// 配置 EnhancedUrlLauncherOptions
final options = EnhancedUrlLauncherOptions(
// 可选:指定启动 URL 的浏览器(如果设备支持)
browserPackage: 'com.android.chrome', // 仅 Android
// 可选:是否使用 WebView 打开 URL(如果插件支持且设备允许)
useWebView: false,
// 可选:WebView 的配置(如果 useWebView 为 true)
webViewConfiguration: WebViewConfiguration(
// 例如:是否启用 JavaScript
javascriptMode: JavascriptMode.unrestricted,
),
// 可选:头部信息,可以用于某些需要认证的 URL
headers: <String, String>{
'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
},
// 可选:处理启动 URL 后的回调
onLaunchCompleted: (bool success, String? errorMessage) {
if (success) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('URL launched successfully!')),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to launch URL: $errorMessage'),
backgroundColor: Colors.red,
),
);
}
},
);
// 启动 URL
await EnhancedUrlLauncher.launchUrl(
Uri.parse('https://www.example.com'),
options: options,
);
} catch (e) {
// 处理异常
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error launching URL: ${e.message}'),
backgroundColor: Colors.red,
),
);
}
},
child: Text('Launch URL'),
),
),
);
}
}
在这个示例中,我们展示了如何使用 EnhancedUrlLauncher
来启动一个 URL,并配置了几个增强功能:
- 指定浏览器:通过
browserPackage
参数指定要使用的浏览器(仅 Android)。 - 使用 WebView:通过
useWebView
参数指定是否使用 WebView 打开 URL。 - WebView 配置:如果
useWebView
为true
,可以通过webViewConfiguration
参数配置 WebView 的行为,例如是否启用 JavaScript。 - 头部信息:通过
headers
参数添加 HTTP 头部信息,这对于某些需要认证的 URL 可能很有用。 - 回调处理:通过
onLaunchCompleted
参数处理 URL 启动完成后的回调,可以用来显示成功或失败的提示信息。
请注意,实际使用时,你需要根据具体需求调整这些配置参数。