package:url_launcher
有时候需要在app 内进行一些跳转功能,比如需要点击一个按键然后跳转到邮箱页面,并且带着收件人的信息,可以使用该package
地址:https://pub.flutter-io.cn/packages/url_launcher
支持情况:(平台支持的功能可能不一样)
Android | iOS | Linux | macOS | Web | Windows |
SDK 16+ | 9.0+ | Any | 10.11+ | Any | Windows 10+ |
功能:
功能:
- 应用内打开web
- 跳转到web 页面
- 打开电话
- 打开邮箱
- 跳转到其他的应用界面
- 打开之后定时返回
- 等等功能
使用实例和代码:
权限添加 IOS
在 Info.plist 文件里面添加如下
<key>LSApplicationQueriesSchemes</key>
<array>
<string>sms</string>
<string>tel</string>
</array>
权限添加 android
在 AndroidManifest.xml 里面添加
<!-- Provide required visibility configuration for API level 30 and above -->
<queries>
<!-- If your app checks for SMS support -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="sms" />
</intent>
<!-- If your app checks for call support -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="tel" />
</intent>
</queries>
放置的位置如图,不要放到activity里面,会导致运行报错
如果没有网络权限的还需要添加:
<uses-permission android:name="android.permission.INTERNET" />
代码功能解读
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:url_launcher/link.dart';
import 'package:url_launcher/url_launcher.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@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({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _hasCallSupport = false;
Future<void>? _launched;
String _phone = '';
@override
void initState() {
super.initState();
// Check for phone call support.
canLaunchUrl(Uri(scheme: 'tel', path: '123')).then((bool result) {
setState(() {
_hasCallSupport = result;
});
});
}
Future<void> _launchInBrowser(Uri url) async {
//使用默认浏览器打开网页地址 url 网页地址
if (!await launchUrl(
url,
mode: LaunchMode.externalApplication,
)) {
throw 'Could not launch $url';
}
}
Future<void> _launchInWebViewOrVC(Uri url) async {
//在app 内打开网页,可以添加头部文件
if (!await launchUrl(
url,
mode: LaunchMode.inAppWebView,
webViewConfiguration: const WebViewConfiguration(
headers: <String, String>{'my_header_key': 'my_header_value'}),
)) {
throw 'Could not launch $url';
}
}
Future<void> _launchInWebViewWithoutJavaScript(Uri url) async {
//禁用 JavaScript 的权限 JavaScript OFF
if (!await launchUrl(
url,
mode: LaunchMode.inAppWebView,
webViewConfiguration: const WebViewConfiguration(enableJavaScript: false),
)) {
throw 'Could not launch $url';
}
}
Future<void> _launchInWebViewWithoutDomStorage(Uri url) async {
//不带dom storage 缓存打开
if (!await launchUrl(
url,
mode: LaunchMode.inAppWebView,
webViewConfiguration: const WebViewConfiguration(enableDomStorage: false),
)) {
throw 'Could not launch $url';
}
}
Future<void> _launchUniversalLinkIos(Uri url) async {
//启动APP 功能。可以带参数,如果启动原生的app 失败
final bool nativeAppLaunchSucceeded = await launchUrl(
url,
mode: LaunchMode.externalNonBrowserApplication,
);
//启动失败的话就使用应用内的webview 打开
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('');
}
}
// map 转为参数
String? encodeQueryParameters(Map<String, String> params) {
return params.entries
.map((MapEntry<String, String> e) =>
'${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
.join('&');
}
Future<void> _openemail(String emailadd,
{String subjectinfo = "", String bodyinfo = ""}) async {
//跳转到电话页面。可以带电话号码
final Uri launchUri = Uri(
scheme: 'mailto',
path: emailadd,
//<String,String>{
// "subject":"邮件的正文内容可以在这边编写"
// }
query: encodeQueryParameters(
subjectinfo == null ? {} : {"body": bodyinfo}));
await launchUrl(launchUri);
}
Future<void> _SMS(String phonenum,
{String subjectinfo = "", String bodyinfo = ""}) async {
//跳转到电话页面。可以带电话号码
final Uri launchUri = Uri(
scheme: 'sms',
path: phonenum,
//<String,String>{
// "subject":"邮件的正文内容可以在这边编写"
// }
query: encodeQueryParameters({"body": bodyinfo}));
await launchUrl(launchUri);
}
Future<void> _openFile(String file_add) async {
//打开文件夹
final Uri launchUri = Uri(
scheme: 'file',
path: file_add,
);
await launchUrl(launchUri);
}
Future<void> _makePhoneCall(String phoneNumber) async {
//跳转到电话页面。可以带电话号码
final Uri launchUri = Uri(
scheme: 'tel',
path: phoneNumber,
);
await launchUrl(launchUri);
}
@override
Widget build(BuildContext context) {
// 如果是使用url的方式,比如没有电话功能,会变成打开网页的功能
final Uri toLaunch = Uri(scheme: 'https', host: 'www.baidu.com');
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ListView(
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
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('app 内打开 web'),
),
const Padding(padding: EdgeInsets.all(16.0)),
ElevatedButton(
onPressed: () => setState(() {
_launched = _launchInWebViewWithoutJavaScript(toLaunch);
}),
child: const Text('Launch in app (JavaScript OFF)'),
),
ElevatedButton(
onPressed: () => setState(() {
_launched = _launchInWebViewWithoutDomStorage(toLaunch);
}),
child: const Text('Launch in app (DOM storage OFF)'),
),
const Padding(padding: EdgeInsets.all(16.0)),
//打开一个应用,比如打开YouTube ,但是手机没有改应用,或者是打开失败,跳转到浏览器来打开
ElevatedButton(
onPressed: () => setState(() {
_launched = _launchUniversalLinkIos(toLaunch);
}),
child: const Text('打开应用失败的情况,使用web 打开该网页'),
),
const Padding(padding: EdgeInsets.all(16.0)),
// 加一个定时器,应用内打开web 之后,4s 关闭该webview
ElevatedButton(
onPressed: () => setState(() {
_launched = _launchInWebViewOrVC(toLaunch);
Timer(const Duration(seconds: 5), () {
print('Closing WebView after 5 seconds...');
closeInAppWebView();
});
}),
child: const Text('app 内打开,然后5s之后关闭该网页'),
),
//打开邮箱
ElevatedButton(
onPressed: () => setState(() {
_launched = _openemail("LOVEYOU@gmail.com",
subjectinfo: '邮件的主题', bodyinfo: '内容就是好好学习天天向上');
}),
child: const Text('打开邮箱'),
),
//发送短信
ElevatedButton(
onPressed: () => setState(() {
_launched = _SMS("188888888888", bodyinfo: '内容就是好好学习天天向上');
}),
child: const Text('短信功能'),
),
//打开文件
ElevatedButton(
onPressed: () => setState(() {
_launched = _openFile(
r'F:\my_work\OTA_EXE\can_log\2022-09-24 11 02 13');
}),
child: const Text('打开文件夹'),
),
const Padding(padding: EdgeInsets.all(16.0)),
// Link 功能 widget
Link(
uri: Uri.parse(
'https://pub.flutter-io.cn/documentation/url_launcher/latest/link/link-library.html'),
target: LinkTarget.blank, //新建一个page
builder: (BuildContext ctx, FollowLink? openLink) {
return TextButton.icon(
onPressed: openLink,
label: const Text('Link Widget documentation'),
icon: const Icon(Icons.read_more),
);
},
),
const Padding(padding: EdgeInsets.all(16.0)),
FutureBuilder<void>(future: _launched, builder: _launchStatus),
],
),
],
),
);
}
}
截图:
#####手机应用之间的跳转
【IOS 需要配置应用白名单】
QQ: mqq://
微信: weixin://
'京东': "openApp.jdMobile://",
淘宝: taobao://
美团: imeituan://
点评: dianping://
1号店: wccbyihaodian://
支付宝: alipay://
微博: sinaweibo://
腾讯微博: TencentWeibo://
weico微博: weico://
知乎: zhihu://
豆瓣fm: doubanradio://
网易公开课: ntesopen://
Chrome: googlechrome://
QQ浏览器: mqqbrowser://
uc浏览器: ucbrowser://
搜狗浏览器: SogouMSE://
百度地图: baidumap:// bdmap://
优酷: youku://
人人: renren://
我查查: wcc://
有道词典: yddictproapp://
微盘: sinavdisk://
名片全能王: camcard://
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// ignore_for_file: public_member_api_docs
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:url_launcher/link.dart';
import 'package:url_launcher/url_launcher.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
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({Key? key, required this.title}) : super(key: key);
final String title;
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _hasCallSupport = false;
Future<void>? _launched;
String _phone = '';
void initState() {
super.initState();
// Check for phone call support.
canLaunchUrl(Uri(scheme: 'tel', path: '123')).then((bool result) {
setState(() {
_hasCallSupport = result;
});
});
}
Future<void> _launchInBrowser(Uri url) async {
//使用默认浏览器打开网页地址 url 网页地址
if (!await launchUrl(
url,
mode: LaunchMode.externalApplication,
)) {
throw 'Could not launch $url';
}
}
Future<void> _launchInWebViewOrVC(Uri url) async {
//在app 内打开网页,可以添加头部文件
if (!await launchUrl(
url,
mode: LaunchMode.inAppWebView,
webViewConfiguration: const WebViewConfiguration(
headers: <String, String>{'my_header_key': 'my_header_value'}),
)) {
throw 'Could not launch $url';
}
}
Future<void> _launchInWebViewWithoutJavaScript(Uri url) async {
//禁用 JavaScript 的权限 JavaScript OFF
if (!await launchUrl(
url,
mode: LaunchMode.inAppWebView,
webViewConfiguration: const WebViewConfiguration(enableJavaScript: false),
)) {
throw 'Could not launch $url';
}
}
Future<void> _launchInWebViewWithoutDomStorage(Uri url) async {
//不带dom storage 缓存打开
if (!await launchUrl(
url,
mode: LaunchMode.inAppWebView,
webViewConfiguration: const WebViewConfiguration(enableDomStorage: false),
)) {
throw 'Could not launch $url';
}
}
Future<void> _launchUniversalLinkIos(Uri url) async {
//启动APP 功能。可以带参数,如果启动原生的app 失败
final bool nativeAppLaunchSucceeded = await launchUrl(
url,
mode: LaunchMode.externalNonBrowserApplication,
);
//启动失败的话就使用应用内的webview 打开
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);
}
Map<String, String> namemap = {
'QQ': "mqq://",
'微信': "weixin://",
'京东':
"openApp.jdMobile://virtual?params=%7B%22category%22%3A%22jump%22%2C%22des%22%3A%22m%22%2C%22sourceValue%22%3A%22babel-act%22%2C%22sourceType%22%3A%22babel%22%2C%22url%22%3A%22https%3A%2F%2Fu.jd.com%2F3I1C9vl%22%2C%22M_sourceFrom%22%3A%22h5auto%22%2C%22msf_type%22%3A%22auto%22%7D",
// "openApp.jdMobile://item.m.jd.com/product/100028779303.html",
'淘宝':
"taobao://item.taobao.com/item.html?id=41700658839", //taobao://item.taobao.com/item.html?id=41700658839
'美团': "imeituan://",
'支付宝': "alipay://",
"qq音乐": "qqmusic://"
};
List<Widget> makelistwidget() {
List<Widget> listw = [];
namemap.forEach((key, value) {
Widget widgettemp = ElevatedButton(
onPressed: () async {
_launchUniversalLinkIos(Uri.parse(value)); // android 正常
},
child: Text(key));
listw.add(widgettemp);
});
return listw;
}
Widget build(BuildContext context) {
// 如果是使用url的方式,比如没有电话功能,会变成打开网页的功能
final Uri toLaunch = Uri(scheme: 'https', host: 'www.baidu.com');
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ListView(children: makelistwidget())
// <Widget>[
// Column(
// mainAxisAlignment: MainAxisAlignment.center,
// children: <Widget>[
// ElevatedButton(
// onPressed: () async {
// _launchUniversalLinkIos(Uri.parse("mqq://")); // android 正常
// },
// child: Text('QQ')),
// ],
// ),
// ],
);
}
}