-
Notifications
You must be signed in to change notification settings - Fork 9.7k
[url_launcher] Update README to use code excerpts. #6042
Changes from 8 commits
1248e12
7226977
8d3c9ea
8d4d5d2
8938f70
0773c55
8a5704c
d8aaa1d
2bf3dc6
9212fed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
<?code-excerpt path-base="excerpts/packages/url_launcher_example"?> | ||
|
||
# url_launcher | ||
|
||
[](https://pub.dev/packages/url_launcher) | ||
|
@@ -14,6 +16,7 @@ To use this plugin, add `url_launcher` as a [dependency in your pubspec.yaml fil | |
|
||
### Example | ||
|
||
<?code-excerpt "basic.dart (basic-example)"?> | ||
``` dart | ||
import 'package:flutter/material.dart'; | ||
import 'package:url_launcher/url_launcher.dart'; | ||
|
@@ -24,7 +27,7 @@ void main() => runApp( | |
const MaterialApp( | ||
home: Material( | ||
child: Center( | ||
child: RaisedButton( | ||
child: ElevatedButton( | ||
onPressed: _launchUrl, | ||
child: Text('Show Flutter homepage'), | ||
), | ||
|
@@ -33,8 +36,10 @@ void main() => runApp( | |
), | ||
); | ||
|
||
void _launchUrl() async { | ||
if (!await launchUrl(_url)) throw 'Could not launch $_url'; | ||
Future<void> _launchUrl() async { | ||
if (!await launchUrl(_url)) { | ||
throw 'Could not launch $_url'; | ||
} | ||
} | ||
``` | ||
|
||
|
@@ -65,18 +70,25 @@ on Android 11 (API 30) or higher. A `<queries>` | |
element must be added to your manifest as a child of the root element. | ||
|
||
Example: | ||
|
||
<?code-excerpt "../../android/app/src/main/AndroidManifest.xml (android-queries)"?> | ||
``` 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" /> | ||
<data android:scheme="https" /> | ||
</intent> | ||
<!-- If your app checks for call support --> | ||
<intent> | ||
<action android:name="android.intent.action.VIEW" /> | ||
<data android:scheme="tel" /> | ||
</intent> | ||
<!-- If your app checks for SMS support --> | ||
<intent> | ||
<action android:name="android.intent.action.VIEW" /> | ||
<data android:scheme="sms" /> | ||
</intent> | ||
</queries> | ||
``` | ||
|
||
|
@@ -133,22 +145,37 @@ due to [a bug](https://github.com/dart-lang/sdk/issues/43838) in the way `Uri` | |
encodes query parameters. Using `queryParameters` will result in spaces being | ||
converted to `+` in many cases. | ||
|
||
<?code-excerpt "encoding.dart (encode-query-parameters)"?> | ||
```dart | ||
String? encodeQueryParameters(Map<String, String> params) { | ||
return params.entries | ||
.map((e) => '${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}') | ||
.map((MapEntry<String, String> e) => | ||
'${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}') | ||
.join('&'); | ||
} | ||
// ··· | ||
final Uri emailLaunchUri = Uri( | ||
scheme: 'mailto', | ||
path: '[email protected]', | ||
query: encodeQueryParameters(<String, String>{ | ||
'subject': 'Example Subject & Symbols are allowed!', | ||
}), | ||
); | ||
|
||
launchUrl(emailLaunchUri); | ||
``` | ||
|
||
final Uri emailLaunchUri = Uri( | ||
scheme: 'mailto', | ||
path: '[email protected]', | ||
query: encodeQueryParameters(<String, String>{ | ||
'subject': 'Example Subject & Symbols are allowed!' | ||
}), | ||
); | ||
Encoding for `sms` is slightly different: | ||
|
||
launchUrl(emailLaunchUri); | ||
<?code-excerpt "encoding.dart (sms)"?> | ||
```dart | ||
final Uri smsLaunchUri = Uri( | ||
scheme: 'sms', | ||
path: '0118 999 881 999 119 7253', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤣 I was about to ask why this wasn't using some obviously fake number (like 123456789) when I realized what it was 🙂 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried to find a cool "555" number, but ended up going for the easy to remember 0118 999 881 999 119 725... 3 (Turns out I'm almost 7 years late, yikes!) |
||
queryParameters: <String, String>{ | ||
'body': Uri.encodeComponent('Example Subject & Symbols are allowed!'), | ||
}, | ||
); | ||
``` | ||
|
||
### URLs not handled by `Uri` | ||
|
@@ -168,14 +195,17 @@ original APIs. | |
We recommend checking first whether the directory or file exists before calling `launchUrl`. | ||
|
||
Example: | ||
|
||
<?code-excerpt "files.dart (file)"?> | ||
```dart | ||
var filePath = '/path/to/file'; | ||
final String filePath = testFile.absolute.path; | ||
final Uri uri = Uri.file(filePath); | ||
|
||
if (await File(uri.toFilePath()).exists()) { | ||
if (!await launchUrl(uri)) { | ||
throw 'Could not launch $uri'; | ||
} | ||
if (!File(uri.toFilePath()).existsSync() || !await canLaunchUrl(uri)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We've moved pretty strongly away from recommending There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed the call to |
||
throw "$uri does not exist, or there's no handler registered for it!"; | ||
} | ||
if (!await launchUrl(uri)) { | ||
throw 'Could not launch $uri'; | ||
} | ||
``` | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,21 +7,25 @@ | |
--> | ||
<uses-permission android:name="android.permission.INTERNET"/> | ||
|
||
<!--#docregion android-queries--> | ||
<!-- Provide required visibility configuration for API level 30 and above --> | ||
<queries> | ||
<intent> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's probably worth adding a comment saying this is only here for integration tests, and shouldn't be needed in most actual apps, so it doesn't confuse anyone else in the future. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed; I've added a comment to the |
||
<action android:name="android.intent.action.VIEW" /> | ||
<data android:scheme="https" /> | ||
</intent> | ||
<!-- If your app checks for call support --> | ||
<intent> | ||
<action android:name="android.intent.action.VIEW" /> | ||
<data android:scheme="tel" /> | ||
</intent> | ||
<!-- If your app checks for SMS support --> | ||
<intent> | ||
<action android:name="android.intent.action.VIEW" /> | ||
<data android:scheme="sms" /> | ||
</intent> | ||
</queries> | ||
<!--#enddocregion android-queries--> | ||
|
||
<application | ||
android:icon="@mipmap/ic_launcher" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
targets: | ||
$default: | ||
sources: | ||
include: | ||
- lib/** | ||
- android/app/src/main/** | ||
# Some default includes that aren't really used here but will prevent | ||
# false-negative warnings: | ||
- $package$ | ||
- lib/$lib$ | ||
exclude: | ||
- '**/.*/**' | ||
- '**/build/**' | ||
- 'android/app/src/main/res/**' | ||
builders: | ||
code_excerpter|code_excerpter: | ||
enabled: true | ||
generate_for: | ||
- '**/*.dart' | ||
- android/**/*.xml |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// 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. | ||
|
||
// Run this example with: flutter run -t lib/basic.dart -d emulator | ||
|
||
// This file is used to extract code samples for the README.md file. | ||
// Run update-excerpts if you modify this file. | ||
|
||
// #docregion basic-example | ||
import 'package:flutter/material.dart'; | ||
import 'package:url_launcher/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 'Could not launch $_url'; | ||
} | ||
} | ||
// #enddocregion basic-example |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// 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. | ||
|
||
// Run this example with: flutter run -t lib/encoding.dart -d emulator | ||
|
||
// This file is used to extract code samples for the README.md file. | ||
// Run update-excerpts if you modify this file. | ||
|
||
import 'package:flutter/material.dart'; | ||
import 'package:url_launcher/url_launcher.dart'; | ||
|
||
/// Encode [params] so it produces a correct query string. | ||
/// Workaround for: https://github.com/dart-lang/sdk/issues/43838 | ||
// #docregion encode-query-parameters | ||
String? encodeQueryParameters(Map<String, String> params) { | ||
return params.entries | ||
.map((MapEntry<String, String> e) => | ||
'${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}') | ||
.join('&'); | ||
} | ||
// #enddocregion encode-query-parameters | ||
|
||
void main() => runApp( | ||
MaterialApp( | ||
home: Material( | ||
child: Column( | ||
mainAxisAlignment: MainAxisAlignment.spaceEvenly, | ||
children: const <Widget>[ | ||
ElevatedButton( | ||
onPressed: _composeMail, | ||
child: Text('Compose an email'), | ||
), | ||
ElevatedButton( | ||
onPressed: _composeSms, | ||
child: Text('Compose a SMS'), | ||
), | ||
], | ||
), | ||
), | ||
), | ||
); | ||
|
||
void _composeMail() { | ||
// #docregion encode-query-parameters | ||
final Uri emailLaunchUri = Uri( | ||
scheme: 'mailto', | ||
path: '[email protected]', | ||
query: encodeQueryParameters(<String, String>{ | ||
'subject': 'Example Subject & Symbols are allowed!', | ||
}), | ||
); | ||
|
||
launchUrl(emailLaunchUri); | ||
// #enddocregion encode-query-parameters | ||
} | ||
|
||
void _composeSms() { | ||
// #docregion 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!'), | ||
}, | ||
); | ||
// #enddocregion sms | ||
|
||
launchUrl(smsLaunchUri); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// 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. | ||
|
||
// Run this example with: flutter run -t lib/files.dart -d linux | ||
|
||
// This file is used to extract code samples for the README.md file. | ||
// Run update-excerpts if you modify this file. | ||
import 'dart:io'; | ||
|
||
import 'package:flutter/material.dart'; | ||
import 'package:path/path.dart' as p; | ||
import 'package:url_launcher/url_launcher.dart'; | ||
|
||
void main() => runApp( | ||
const MaterialApp( | ||
home: Material( | ||
child: Center( | ||
child: ElevatedButton( | ||
onPressed: _openFile, | ||
child: Text('Open File'), | ||
), | ||
), | ||
), | ||
), | ||
); | ||
|
||
Future<void> _openFile() async { | ||
// Prepare a file within tmp | ||
final String tempFilePath = p.joinAll(<String>[ | ||
...p.split(Directory.systemTemp.path), | ||
'flutter_url_launcher_example.txt' | ||
]); | ||
final File testFile = File(tempFilePath); | ||
await testFile.writeAsString('Hello, world!'); | ||
// #docregion file | ||
final String filePath = testFile.absolute.path; | ||
final Uri uri = Uri.file(filePath); | ||
|
||
if (!File(uri.toFilePath()).existsSync() || !await canLaunchUrl(uri)) { | ||
throw "$uri does not exist, or there's no handler registered for it!"; | ||
} | ||
if (!await launchUrl(uri)) { | ||
throw 'Could not launch $uri'; | ||
} | ||
// #enddocregion file | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ environment: | |
dependencies: | ||
flutter: | ||
sdk: flutter | ||
path: ^1.8.2 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed:
|
||
url_launcher: | ||
# When depending on this package from a real application you should use: | ||
# url_launcher: ^x.y.z | ||
|
@@ -18,6 +19,7 @@ dependencies: | |
path: ../ | ||
|
||
dev_dependencies: | ||
build_runner: ^2.1.10 | ||
flutter_driver: | ||
sdk: flutter | ||
integration_test: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should excerpt the
https
block out (by making it a non-contiguous region); https is in the example because it's thing we can use in integration tests on any device that should return true, but there's basically no reason any actual app should need to check for it (for the same reason). I actually recently removed this from the README example.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, I've removed the
https
block from the docregion, and used an empty "plaster" so it doesn't even shows up as "..." in the docs.