How to Launch URLs from Your App with Flutter’s URL_Launcher Package
You should learn about the url_launcher package to add more functionality to your Flutter app. With this package, you can launch URLs, open email apps, dial phone numbers, and more from within your app. Utilizing this package can create a more seamless and user-friendly experience for your app’s users, making it an essential tool for any Flutter developer.
We will be coding an example in this article, and the source code will be available at the end. Let’s begin!
Install the package
To get started, install the url_launcher package from pub.dev.
You can add the dependency to your pubspec.yaml
but I prefer to run the command bellow in my terminal:
flutter pub add url_launcher
IOS update info.plist
When launching an external app or service via a URL, the app needs to know which schemes to allow. By default, iOS only allows a limited number of schemes, such as HTTP, HTTPS, and mailto. To launch other schemes, such as sms and tel, they must be explicitly added to the LSApplicationQueriesSchemes
array in the info.plist
file.
Open and add the code bellow to the following file:ios/Runner/info.plist
<key>LSApplicationQueriesSchemes</key>
<array>
<string>sms</string>
<string>tel</string>
</array>
After adding the code above you need to restart the IOS simulator.
Android update AndroidManifest.xml
We need to modify the following file:android/app/src/main/AndroidManifest.xml
For the url_launcher to work you need to add the code bellow at the top of the AndroidManifest.xml
file just above <application>
<!-- Provide required visibility configuration for API level 30 and above -->
<queries>
<!-- If your app opens https URLs -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
<!-- If your app opens http URLs -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http" />
</intent>
<!-- 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>
By default http urls are not supported so to enable the opening of them, we need to add android:usesCleartextTraffic=”true”
at the top inside <application>
, so in short change from:
<application
android:label="MyInvoices"
android:name="${applicationName}"
android:icon="@mipmap/launcher_icon">
to:
<application
android:label="MyInvoices"
android:name="${applicationName}"
android:icon="@mipmap/launcher_icon"
android:usesCleartextTraffic="true">
After making these changes, restart the Android simulator as well.
Setting up…
Now we just need to make a few adjustments
Importing URL_Launcher
Now in your Dart code, you can use the following code to import the URL_Launcher package:
import 'package:url_launcher/url_launcher.dart';
Create a URL file
To keep the code architecture clean I create a separate file with URLs like bellow, this makes changing the URLs easier in the future.
String httpsLink = 'https://medium.com/@filip.doganowski';
String myPhoneNumber = '+48000000000';
Creating custom UI buttons
Below is an example of a custom UI button I used for calling functions. You can create any button that you wish, this is just an example.
class UrlLauncherButton extends StatelessWidget {
const UrlLauncherButton({super.key, required this.text, this.onPressed});
final String text;
final void Function()? onPressed;
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
child: SizedBox(width: 200, height: 40, child: Center(child: Text(text))),
);
}
}
Launching file
Create a Future<void>
for each launch that you need. I suggest creating a separate file for example url_launches.dart
to keep it all nice and tidy.
Launch email in app
This future launches an email in your main email app
Future<void> launchEmail() async {
final String email = Uri.encodeComponent("test@gmail.com");
final Uri mail = Uri.parse("mailto:$email");
try {
final bool launched = await launchUrl(mail);
if (launched) {
// email app opened
} else {
// email app is not opened
throw Exception('Could not launch email app');
}
} on PlatformException catch (e) {
throw Exception('Error launching email: $e');
}
}
If you want to add a default subject or body to the mail you have to parse it in the Uri like, bellow is an example:
Future<void> launchEmail() async {
final String email = Uri.encodeComponent("test@gmail.com");
final String subject = Uri.encodeComponent("Mail subject");
final String body = Uri.encodeComponent("Mail body");
final Uri mail = Uri.parse("mailto:$email?subject=$subject&body=$body");
try {
final bool launched = await launchUrl(mail);
if (launched) {
// email app opened
} else {
// email app is not opened
throw Exception('Could not launch email app');
}
} on PlatformException catch (e) {
throw Exception('Error launching email: $e');
}
}
Launch a phone call
This future opens the default phone app with the phone number of your choice.
Future<void> launchCall({required String phoneNumber}) async {
final Uri urlParsed = Uri.parse('tel:$phoneNumber');
if (await canLaunchUrl(urlParsed)) {
await launchUrl(urlParsed);
} else {
throw 'Could not launch call to: $phoneNumber';
}
}
Launch SMS message:
This future on the other hand opens up the default messages app with a blank message.
Future<void> launchSMS({required String phoneNumber}) async {
final Uri urlParsed = Uri.parse('sms:$phoneNumber');
if (await canLaunchUrl(urlParsed)) {
await launchUrl(urlParsed);
} else {
throw 'Could not launch sms to: $phoneNumber';
}
}
Launch website
Bellow are two options of opening a website, the first is in an in app browser.
Future<void> launchUrlSite({required String url}) async {
final Uri urlParsed = Uri.parse(url);
if (await canLaunchUrl(urlParsed)) {
await launchUrl(urlParsed);
} else {
throw 'Could not launch $url';
}
}
The second is in an external browser.
Future<void> launchUrlSiteBrowser({required String url}) async {
final Uri urlParsed = Uri.parse(url);
if (await canLaunchUrl(urlParsed)) {
await launchUrl(urlParsed, mode: LaunchMode.externalApplication);
} else {
throw 'Could not launch $url';
}
}
All done…
Adding functions to buttons
Now you can add the futures to your buttons like bellow, again this is just an example, and you are all done!
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
UrlLauncherButton(
text: 'Launch Email',
onPressed: () => launchEmail(),
),
UrlLauncherButton(
text: 'Launch SMS',
onPressed: () => launchSMS(phoneNumber: myPhoneNumber),
),
UrlLauncherButton(
text: 'Launch Phone Call',
onPressed: () => launchCall(phoneNumber: myPhoneNumber),
),
UrlLauncherButton(
text: 'Launch url in app',
onPressed: () => launchUrlSite(url: httpsLink),
),
UrlLauncherButton(
text: 'Launch url in external app',
onPressed: () => launchUrlSiteBrowser(url: httpsLink),
),
],
),
Disclaimer
Don’t panic if calling the phone number or opening the mail app doesn’t work from the simulator, not all simulators have an e-mail and phone app installed, my suggestion is to try this on a real device.
Conclusion
As you can see adding url launches is not that difficult, just follow the steps above and you are all set to launch!
For the full source code visit the repository below.
If you would like to explore further functionalities of this package then please let me know in the response!
Outcome
Bellow is a gif with the outcome:
Have any thoughts? Have a different method you would like to share? Leave a response!