Skip to content

Commit 3eaad3d

Browse files
[url_lancher] Don't use canLaunchUrl in Link (flutter#4400)
The pattern of calling `canLaunchUrl` when there's no fallback is an anti-pattern, and the README explicitly recommends against it; on some platforms, `canLaunchUrl` is unreliable in some cases, and/or requires extra permissions that `launchUrl` does not. This reworks the launch call to just try, and handle failure, instead of trying to pre-check.
1 parent a69b24b commit 3eaad3d

File tree

5 files changed

+18
-9
lines changed

5 files changed

+18
-9
lines changed

packages/url_launcher/url_launcher/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
## NEXT
1+
## 6.1.12
22

3+
* Removes the use of `canLaunchUrl` in `Link`, to avoid issues on platforms where `canLaunchUrl` is unreliable or requires permissions.
34
* Updates minimum supported macOS version to 10.14.
45
* Fixes stale ignore: prefer_const_constructors.
56
* Updates minimum supported SDK version to Flutter 3.10/Dart 3.0.

packages/url_launcher/url_launcher/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ To use this plugin, add `url_launcher` as a [dependency in your pubspec.yaml fil
1717
### Example
1818

1919
<?code-excerpt "basic.dart (basic-example)"?>
20-
``` dart
20+
```dart
2121
import 'package:flutter/material.dart';
2222
import 'package:url_launcher/url_launcher.dart';
2323
@@ -72,7 +72,7 @@ element must be added to your manifest as a child of the root element.
7272
Example:
7373

7474
<?code-excerpt "../../android/app/src/main/AndroidManifest.xml (android-queries)" plaster="none"?>
75-
``` xml
75+
```xml
7676
<!-- Provide required visibility configuration for API level 30 and above -->
7777
<queries>
7878
<!-- If your app checks for SMS support -->

packages/url_launcher/url_launcher/lib/src/link.dart

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,18 @@ class DefaultLinkDelegate extends StatelessWidget {
121121

122122
// At this point, we know that the link is external. So we use the
123123
// `launchUrl` API to open the link.
124-
if (await canLaunchUrl(url)) {
125-
await launchUrl(
124+
bool success;
125+
try {
126+
success = await launchUrl(
126127
url,
127128
mode: _useWebView
128129
? LaunchMode.inAppWebView
129130
: LaunchMode.externalApplication,
130131
);
131-
} else {
132+
} on PlatformException {
133+
success = false;
134+
}
135+
if (!success) {
132136
FlutterError.reportError(FlutterErrorDetails(
133137
exception: 'Could not launch link $url',
134138
stack: StackTrace.current,

packages/url_launcher/url_launcher/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports
33
web, phone, SMS, and email schemes.
44
repository: https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher
55
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22
6-
version: 6.1.11
6+
version: 6.1.12
77

88
environment:
99
sdk: ">=3.0.0 <4.0.0"

packages/url_launcher/url_launcher/test/link_test.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ void main() {
6464
)
6565
..setResponse(true);
6666
await followLink!();
67-
expect(mock.canLaunchCalled, isTrue);
67+
// Calling canLaunch just to pre-check launch is an anti-pattern, since
68+
// canLaunch doesn't always work, so ensure that it's not called.
69+
expect(mock.canLaunchCalled, isFalse);
6870
expect(mock.launchCalled, isTrue);
6971
});
7072

@@ -93,7 +95,9 @@ void main() {
9395
)
9496
..setResponse(true);
9597
await followLink!();
96-
expect(mock.canLaunchCalled, isTrue);
98+
// Calling canLaunch just to pre-check launch is an anti-pattern, since
99+
// canLaunch doesn't always work, so ensure that it's not called.
100+
expect(mock.canLaunchCalled, isFalse);
97101
expect(mock.launchCalled, isTrue);
98102
});
99103

0 commit comments

Comments
 (0)