Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit ca63d96

Browse files
[url_launcher] Replace primary APIs with cleaner versions (#5310)
1 parent bde713a commit ca63d96

17 files changed

+1058
-264
lines changed

packages/url_launcher/url_launcher/CHANGELOG.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
1-
## NEXT
2-
1+
## 6.1.0
2+
3+
* Introduces new `launchUrl` and `canLaunchUrl` APIs; `launch` and `canLaunch`
4+
are now deprecated. These new APIs:
5+
* replace the `String` URL argument with a `Uri`, to prevent common issues
6+
with providing invalid URL strings.
7+
* replace `forceSafariVC` and `forceWebView` with `LaunchMode`, which makes
8+
the API platform-neutral, and standardizes the default behavior between
9+
Android and iOS.
10+
* move web view configuration options into a new `WebViewConfiguration`
11+
object. The default behavior for JavaScript and DOM storage is now enabled
12+
rather than disabled.
13+
* Also deprecates `closeWebView` in favor of `closeInAppWebView` to clarify
14+
that it is specific to the in-app web view launch option.
315
* Adds OS version support information to README.
16+
* Reorganizes and clarifies README.
417

518
## 6.0.20
619

packages/url_launcher/url_launcher/README.md

Lines changed: 57 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,23 @@ To use this plugin, add `url_launcher` as a [dependency in your pubspec.yaml fil
1818
import 'package:flutter/material.dart';
1919
import 'package:url_launcher/url_launcher.dart';
2020
21-
const String _url = 'https://flutter.dev';
21+
final Uri _url = Uri.parse('https://flutter.dev');
2222
2323
void main() => runApp(
2424
const MaterialApp(
2525
home: Material(
2626
child: Center(
2727
child: RaisedButton(
28-
onPressed: _launchURL,
28+
onPressed: _launchUrl,
2929
child: Text('Show Flutter homepage'),
3030
),
3131
),
3232
),
3333
),
3434
);
3535
36-
void _launchURL() async {
37-
if (!await launch(_url)) throw 'Could not launch $_url';
36+
void _launchUrl() async {
37+
if (!await launchUrl(_url)) throw 'Could not launch $_url';
3838
}
3939
```
4040

@@ -43,7 +43,7 @@ See the example app for more complex examples.
4343
## Configuration
4444

4545
### iOS
46-
Add any URL schemes passed to `canLaunch` as `LSApplicationQueriesSchemes` entries in your Info.plist file.
46+
Add any URL schemes passed to `canLaunchUrl` as `LSApplicationQueriesSchemes` entries in your Info.plist file.
4747

4848
Example:
4949
```
@@ -59,7 +59,7 @@ See [`-[UIApplication canOpenURL:]`](https://developer.apple.com/documentation/u
5959
### Android
6060

6161
Starting from API 30 Android requires package visibility configuration in your
62-
`AndroidManifest.xml` otherwise `canLaunch` will return `false`. A `<queries>`
62+
`AndroidManifest.xml` otherwise `canLaunchUrl` will return `false`. A `<queries>`
6363
element must be added to your manifest as a child of the root element.
6464

6565
The snippet below shows an example for an application that uses `https`, `tel`,
@@ -94,34 +94,53 @@ for examples of other queries.
9494

9595
## Supported URL schemes
9696

97-
The [`launch`](https://pub.dev/documentation/url_launcher/latest/url_launcher/launch.html) method
98-
takes a string argument containing a URL. This URL
99-
can be formatted using a number of different URL schemes. The supported
100-
URL schemes depend on the underlying platform and installed apps.
97+
The provided URL is passed directly to the host platform for handling. The
98+
supported URL schemes therefore depend on the platform and installed apps.
10199

102100
Commonly used schemes include:
103101

104102
| Scheme | Example | Action |
105103
|:---|:---|:---|
106-
| `https:<URL>` | `https://flutter.dev` | Open URL in the default browser |
107-
| `mailto:<email address>?subject=<subject>&body=<body>` | `mailto:[email protected]?subject=News&body=New%20plugin` | Create email to <email address> in the default email app |
108-
| `tel:<phone number>` | `tel:+1-555-010-999` | Make a phone call to <phone number> using the default phone app |
109-
| `sms:<phone number>` | `sms:5550101234` | Send an SMS message to <phone number> using the default messaging app |
104+
| `https:<URL>` | `https://flutter.dev` | Open `<URL>` in the default browser |
105+
| `mailto:<email address>?subject=<subject>&body=<body>` | `mailto:[email protected]?subject=News&body=New%20plugin` | Create email to `<email address>` in the default email app |
106+
| `tel:<phone number>` | `tel:+1-555-010-999` | Make a phone call to `<phone number>` using the default phone app |
107+
| `sms:<phone number>` | `sms:5550101234` | Send an SMS message to `<phone number>` using the default messaging app |
110108
| `file:<path>` | `file:/home` | Open file or folder using default app association, supported on desktop platforms |
111109

112110
More details can be found here for [iOS](https://developer.apple.com/library/content/featuredarticles/iPhoneURLScheme_Reference/Introduction/Introduction.html)
113111
and [Android](https://developer.android.com/guide/components/intents-common.html)
114112

115-
**Note**: URL schemes are only supported if there are apps installed on the device that can
113+
URL schemes are only supported if there are apps installed on the device that can
116114
support them. For example, iOS simulators don't have a default email or phone
117115
apps installed, so can't open `tel:` or `mailto:` links.
118116

117+
### Checking supported schemes
118+
119+
If you need to know at runtime whether a scheme is guaranteed to work before
120+
using it (for instance, to adjust your UI based on what is available), you can
121+
check with [`canLaunchUrl`](https://pub.dev/documentation/url_launcher/latest/url_launcher/canLaunchUrl.html).
122+
123+
However, `canLaunchUrl` can return false even if `launchUrl` would work in
124+
some circumstances (in web applications, on mobile without the necessary
125+
configuration as described above, etc.), so in cases where you can provide
126+
fallback behavior it is better to use `launchUrl` directly and handle failure.
127+
For example, a UI button that would have sent feedback email using a `mailto` URL
128+
might instead open a web-based feedback form using an `https` URL on failure,
129+
rather than disabling the button if `canLaunchUrl` returns false for `mailto`.
130+
119131
### Encoding URLs
120132

121133
URLs must be properly encoded, especially when including spaces or other special
122-
characters. This can be done using the
134+
characters. In general this is handled automatically by the
123135
[`Uri` class](https://api.dart.dev/dart-core/Uri-class.html).
124-
For example:
136+
137+
**However**, for any scheme other than `http` or `https`, you should use the
138+
`query` parameter and the `encodeQueryParameters` function shown below rather
139+
than `Uri`'s `queryParameters` constructor argument for any query parameters,
140+
due to [a bug](https://github.com/dart-lang/sdk/issues/43838) in the way `Uri`
141+
encodes query parameters. Using `queryParameters` will result in spaces being
142+
converted to `+` in many cases.
143+
125144
```dart
126145
String? encodeQueryParameters(Map<String, String> params) {
127146
return params.entries
@@ -137,57 +156,46 @@ final Uri emailLaunchUri = Uri(
137156
}),
138157
);
139158
140-
launch(emailLaunchUri.toString());
159+
launchUrl(emailLaunchUri);
141160
```
142161

143-
**Warning**: For any scheme other than `http` or `https`, you should use the
144-
`query` parameter and the `encodeQueryParameters` function shown above rather
145-
than `Uri`'s `queryParameters` constructor argument, due to
146-
[a bug](https://github.com/dart-lang/sdk/issues/43838) in the way `Uri`
147-
encodes query parameters. Using `queryParameters` will result in spaces being
148-
converted to `+` in many cases.
149-
150-
### Handling missing URL receivers
162+
### URLs not handled by `Uri`
151163

152-
A particular mobile device may not be able to receive all supported URL schemes.
153-
For example, a tablet may not have a cellular radio and thus no support for
154-
launching a URL using the `sms` scheme, or a device may not have an email app
155-
and thus no support for launching a URL using the `mailto` scheme.
164+
In rare cases, you may need to launch a URL that the host system considers
165+
valid, but cannot be expressed by `Uri`. For those cases, alternate APIs using
166+
strings are available by importing `url_launcher_string.dart`.
156167

157-
We recommend checking which URL schemes are supported using the
158-
[`canLaunch`](https://pub.dev/documentation/url_launcher/latest/url_launcher/canLaunch.html)
159-
in most cases. If the `canLaunch` method returns false, as a
160-
best practice we suggest adjusting the application UI so that the unsupported
161-
URL is never triggered; for example, if the `mailto` scheme is not supported, a
162-
UI button that would have sent feedback email could be changed to instead open
163-
a web-based feedback form using an `https` URL.
168+
Using these APIs in any other cases is **strongly discouraged**, as providing
169+
invalid URL strings was a very common source of errors with this plugin's
170+
original APIs.
164171

165-
## Browser vs In-app Handling
166-
By default, Android opens up a browser when handling URLs. You can pass
167-
`forceWebView: true` parameter to tell the plugin to open a WebView instead.
168-
If you do this for a URL of a page containing JavaScript, make sure to pass in
169-
`enableJavaScript: true`, or else the launch method will not work properly. On
170-
iOS, the default behavior is to open all web URLs within the app. Everything
171-
else is redirected to the app handler.
172+
### File scheme handling
172173

173-
## File scheme handling
174-
`file:` scheme can be used on desktop platforms: `macOS`, `Linux` and `Windows`.
174+
`file:` scheme can be used on desktop platforms: Windows, macOS, and Linux.
175175

176-
We recommend checking first whether the directory or file exists before calling `launch`.
176+
We recommend checking first whether the directory or file exists before calling `launchUrl`.
177177

178178
Example:
179179
```dart
180180
var filePath = '/path/to/file';
181181
final Uri uri = Uri.file(filePath);
182182
183183
if (await File(uri.toFilePath()).exists()) {
184-
if (!await launch(uri.toString())) {
184+
if (!await launchUrl(uri)) {
185185
throw 'Could not launch $uri';
186186
}
187187
}
188188
```
189189

190-
### macOS file access configuration
190+
#### macOS file access configuration
191191

192192
If you need to access files outside of your application's sandbox, you will need to have the necessary
193193
[entitlements](https://docs.flutter.dev/desktop#entitlements-and-the-app-sandbox).
194+
195+
## Browser vs in-app Handling
196+
197+
On some platforms, web URLs can be launched either in an in-app web view, or
198+
in the default browser. The default behavior depends on the platform (see
199+
[`launchUrl`](https://pub.dev/documentation/url_launcher/latest/url_launcher/launchUrl.html)
200+
for details), but a specific mode can be used on supported platforms by
201+
passing a `LaunchMode`.

packages/url_launcher/url_launcher/example/integration_test/url_launcher_test.dart

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,23 @@ void main() {
1313
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
1414

1515
testWidgets('canLaunch', (WidgetTester _) async {
16-
expect(await canLaunch('randomstring'), false);
16+
expect(
17+
await canLaunchUrl(Uri(scheme: 'randomscheme', path: 'a_path')), false);
1718

1819
// Generally all devices should have some default browser.
19-
expect(await canLaunch('http://flutter.dev'), true);
20-
expect(await canLaunch('https://www.google.com/404'), true);
20+
expect(await canLaunchUrl(Uri(scheme: 'http', host: 'flutter.dev')), true);
21+
expect(await canLaunchUrl(Uri(scheme: 'https', host: 'flutter.dev')), true);
2122

2223
// SMS handling is available by default on most platforms.
2324
if (kIsWeb || !(Platform.isLinux || Platform.isWindows)) {
24-
expect(await canLaunch('sms:5555555555'), true);
25+
expect(await canLaunchUrl(Uri(scheme: 'sms', path: '5555555555')), true);
2526
}
2627

27-
// tel: and mailto: links may not be openable on every device. iOS
28-
// simulators notably can't open these link types.
28+
// Sanity-check legacy API.
29+
// ignore: deprecated_member_use
30+
expect(await canLaunch('randomstring'), false);
31+
// Generally all devices should have some default browser.
32+
// ignore: deprecated_member_use
33+
expect(await canLaunch('https://flutter.dev'), true);
2934
});
3035
}

packages/url_launcher/url_launcher/example/lib/main.dart

Lines changed: 33 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -44,67 +44,62 @@ class _MyHomePageState extends State<MyHomePage> {
4444
void initState() {
4545
super.initState();
4646
// Check for phone call support.
47-
canLaunch('tel:123').then((bool result) {
47+
canLaunchUrl(Uri(scheme: 'tel', path: '123')).then((bool result) {
4848
setState(() {
4949
_hasCallSupport = result;
5050
});
5151
});
5252
}
5353

54-
Future<void> _launchInBrowser(String url) async {
55-
if (!await launch(
54+
Future<void> _launchInBrowser(Uri url) async {
55+
if (!await launchUrl(
5656
url,
57-
forceSafariVC: false,
58-
forceWebView: false,
59-
headers: <String, String>{'my_header_key': 'my_header_value'},
57+
mode: LaunchMode.externalApplication,
6058
)) {
6159
throw 'Could not launch $url';
6260
}
6361
}
6462

65-
Future<void> _launchInWebViewOrVC(String url) async {
66-
if (!await launch(
63+
Future<void> _launchInWebViewOrVC(Uri url) async {
64+
if (!await launchUrl(
6765
url,
68-
forceSafariVC: true,
69-
forceWebView: true,
70-
headers: <String, String>{'my_header_key': 'my_header_value'},
66+
mode: LaunchMode.inAppWebView,
67+
webViewConfiguration: const WebViewConfiguration(
68+
headers: <String, String>{'my_header_key': 'my_header_value'}),
7169
)) {
7270
throw 'Could not launch $url';
7371
}
7472
}
7573

76-
Future<void> _launchInWebViewWithJavaScript(String url) async {
77-
if (!await launch(
74+
Future<void> _launchInWebViewWithoutJavaScript(Uri url) async {
75+
if (!await launchUrl(
7876
url,
79-
forceSafariVC: true,
80-
forceWebView: true,
81-
enableJavaScript: true,
77+
mode: LaunchMode.inAppWebView,
78+
webViewConfiguration: const WebViewConfiguration(enableJavaScript: false),
8279
)) {
8380
throw 'Could not launch $url';
8481
}
8582
}
8683

87-
Future<void> _launchInWebViewWithDomStorage(String url) async {
88-
if (!await launch(
84+
Future<void> _launchInWebViewWithoutDomStorage(Uri url) async {
85+
if (!await launchUrl(
8986
url,
90-
forceSafariVC: true,
91-
forceWebView: true,
92-
enableDomStorage: true,
87+
mode: LaunchMode.inAppWebView,
88+
webViewConfiguration: const WebViewConfiguration(enableDomStorage: false),
9389
)) {
9490
throw 'Could not launch $url';
9591
}
9692
}
9793

98-
Future<void> _launchUniversalLinkIos(String url) async {
99-
final bool nativeAppLaunchSucceeded = await launch(
94+
Future<void> _launchUniversalLinkIos(Uri url) async {
95+
final bool nativeAppLaunchSucceeded = await launchUrl(
10096
url,
101-
forceSafariVC: false,
102-
universalLinksOnly: true,
97+
mode: LaunchMode.externalNonBrowserApplication,
10398
);
10499
if (!nativeAppLaunchSucceeded) {
105-
await launch(
100+
await launchUrl(
106101
url,
107-
forceSafariVC: true,
102+
mode: LaunchMode.inAppWebView,
108103
);
109104
}
110105
}
@@ -118,22 +113,19 @@ class _MyHomePageState extends State<MyHomePage> {
118113
}
119114

120115
Future<void> _makePhoneCall(String phoneNumber) async {
121-
// Use `Uri` to ensure that `phoneNumber` is properly URL-encoded.
122-
// Just using 'tel:$phoneNumber' would create invalid URLs in some cases,
123-
// such as spaces in the input, which would cause `launch` to fail on some
124-
// platforms.
125116
final Uri launchUri = Uri(
126117
scheme: 'tel',
127118
path: phoneNumber,
128119
);
129-
await launch(launchUri.toString());
120+
await launchUrl(launchUri);
130121
}
131122

132123
@override
133124
Widget build(BuildContext context) {
134125
// onPressed calls using this URL are not gated on a 'canLaunch' check
135126
// because the assumption is that every device can launch a web URL.
136-
const String toLaunch = 'https://www.cylog.org/headers/';
127+
final Uri toLaunch =
128+
Uri(scheme: 'https', host: 'www.cylog.org', path: 'headers/');
137129
return Scaffold(
138130
appBar: AppBar(
139131
title: Text(widget.title),
@@ -160,9 +152,9 @@ class _MyHomePageState extends State<MyHomePage> {
160152
? const Text('Make phone call')
161153
: const Text('Calling not supported'),
162154
),
163-
const Padding(
164-
padding: EdgeInsets.all(16.0),
165-
child: Text(toLaunch),
155+
Padding(
156+
padding: const EdgeInsets.all(16.0),
157+
child: Text(toLaunch.toString()),
166158
),
167159
ElevatedButton(
168160
onPressed: () => setState(() {
@@ -179,15 +171,15 @@ class _MyHomePageState extends State<MyHomePage> {
179171
),
180172
ElevatedButton(
181173
onPressed: () => setState(() {
182-
_launched = _launchInWebViewWithJavaScript(toLaunch);
174+
_launched = _launchInWebViewWithoutJavaScript(toLaunch);
183175
}),
184-
child: const Text('Launch in app(JavaScript ON)'),
176+
child: const Text('Launch in app (JavaScript OFF)'),
185177
),
186178
ElevatedButton(
187179
onPressed: () => setState(() {
188-
_launched = _launchInWebViewWithDomStorage(toLaunch);
180+
_launched = _launchInWebViewWithoutDomStorage(toLaunch);
189181
}),
190-
child: const Text('Launch in app(DOM storage ON)'),
182+
child: const Text('Launch in app (DOM storage OFF)'),
191183
),
192184
const Padding(padding: EdgeInsets.all(16.0)),
193185
ElevatedButton(
@@ -203,7 +195,7 @@ class _MyHomePageState extends State<MyHomePage> {
203195
_launched = _launchInWebViewOrVC(toLaunch);
204196
Timer(const Duration(seconds: 5), () {
205197
print('Closing WebView after 5 seconds...');
206-
closeWebView();
198+
closeInAppWebView();
207199
});
208200
}),
209201
child: const Text('Launch in app + close after 5 seconds'),

0 commit comments

Comments
 (0)