url_launcher

url_launcher は、アプリから外部のアプリやサービスを起動するパッケージです!
比喩で言うと: スマホの受付係のようなもの。「ブラウザ開いて!」「電話アプリ起動して!」とお願いできます! 📱

🤔 何ができるの?
できること:
✅ Webサイトを開く – ブラウザ起動
✅ 電話をかける – 電話アプリ起動
✅ メールを送る – メールアプリ起動
✅ SMSを送る – SMSアプリ起動
✅ 地図を開く – マップアプリ起動
✅ 他のアプリを開く – カスタムURL

使う場面の例:

用途:

  • チケット購入・・・チケットサイトへ誘導
  • お問い合わせ・・・電話番号タップで発信
  • サポート・・・メールアドレスタップでメール作成
  • 会場案内・・・住所タップで地図表示
  • SNS連携・・・TwitterやInstagramを開く
  • 利用規約・・・PDFやWebページを表示

インストール方法

pubspec.yaml に追加

dependencies:
  flutter:
    sdk: flutter
  
  url_launcher: ^6.2.0

パッケージを取得

flutter pub get

import する

import 'package:url_launcher/url_launcher.dart';

基本的な使い方

基本パターン:3ステップ! 簡単

// 1. Uri を作る
final Uri url = Uri.parse('https://example.com');

// 2. 開けるかチェック
if (await canLaunchUrl(url)) {
  // 3. 開く
  await launchUrl(url);
} else {
  // エラー処理
  print('URLを開けません');
}
実例
Webサイトを開く
基本形:
Future<void> openWebsite() async {
  final Uri url = Uri.parse('https://www.google.com');
  
  if (await canLaunchUrl(url)) {
    await launchUrl(url);
  } else {
    print('URLを開けません');
  }
}

アプリ内ブラウザで開く:

Future<void> openWebsiteInApp() async {
  final Uri url = Uri.parse('https://www.google.com');
  
  if (await canLaunchUrl(url)) {
    await launchUrl(
      url,
      mode: LaunchMode.inAppWebView,  // アプリ内で開く
    );
  }
}

外部ブラウザで開く:

Future<void> openWebsiteExternal() async {
  final Uri url = Uri.parse('https://www.google.com');
  
  if (await canLaunchUrl(url)) {
    await launchUrl(
      url,
      mode: LaunchMode.externalApplication,  // 外部ブラウザで開く
    );
  }
}

LaunchMode の種類:

  • platformDefault・・・OSのデフォルト(推奨)
  • inAppWebView・・・アプリ内ブラウザ
  • externalApplication・・・外部ブラウザ(Chrome, Safari)
  • externalNonBrowserApplication・・・ブラウザ以外(PDFビューアなど)
実践
実践例2: 電話をかける
Future<void> makePhoneCall(String phoneNumber) async {
  final Uri url = Uri.parse('tel:$phoneNumber');
  
  if (await canLaunchUrl(url)) {
    await launchUrl(url);
  } else {
    print('電話をかけられません');
  }
}

// 使い方
ElevatedButton(
  onPressed: () => makePhoneCall('03-1234-5678'),
  child: Text('お問い合わせ'),
)

ポイント: tel: というスキームを使います!

makePhoneCall('03-1234-5678');    // OK
makePhoneCall('(03) 1234-5678');  // OK
makePhoneCall('0312345678');      // OK
実践
メールを送る
// bool を保存
await prefs.setBool('isDarkMode', true);

// int を保存
await prefs.setInt('age', 35);

// double を保存
await prefs.setDouble('volume', 0.8);

// String を保存
await prefs.setString('userName', '花子');

// List<String> を保存
await prefs.setStringList('favorites', ['りんご', 'バナナ', 'みかん']);

件名や本文も設定:

Future<void> sendEmailWithSubject({
  required String email,
  String subject = '',
  String body = '',
}) async {
  final Uri url = Uri(
    scheme: 'mailto',
    path: email,
    query: _encodeQueryParameters({
      'subject': subject,
      'body': body,
    }),
  );
  
  if (await canLaunchUrl(url)) {
    await launchUrl(url);
  }
}

// クエリパラメータをエンコード
String? _encodeQueryParameters(Map<String, String> params) {
  return params.entries
      .map((e) => '${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
      .join('&');
}

// 使い方
sendEmailWithSubject(
  email: 'support@example.com',
  subject: 'お問い合わせ',
  body: 'こんにちは。\n\n質問があります。',
);

CC、BCCも設定:

Future<void> sendEmailAdvanced({
  required String email,
  String? cc,
  String? bcc,
  String? subject,
  String? body,
}) async {
  final Map<String, String> params = {};
  
  if (subject != null) params['subject'] = subject;
  if (body != null) params['body'] = body;
  if (cc != null) params['cc'] = cc;
  if (bcc != null) params['bcc'] = bcc;
  
  final Uri url = Uri(
    scheme: 'mailto',
    path: email,
    query: _encodeQueryParameters(params),
  );
  
  if (await canLaunchUrl(url)) {
    await launchUrl(url);
  }
}

// 使い方
sendEmailAdvanced(
  email: 'support@example.com',
  cc: 'manager@example.com',
  subject: 'コンサート申込',
  body: 'コンサートに参加したいです。',
);
実践
実践例4: SMSを送る
Future<void> sendSMS(String phoneNumber, {String? message}) async {
  final Uri url = Uri(
    scheme: 'sms',
    path: phoneNumber,
    queryParameters: message != null ? {'body': message} : null,
  );
  
  if (await canLaunchUrl(url)) {
    await launchUrl(url);
  } else {
    print('SMSを送れません');
  }
}

// 使い方
sendSMS('090-1234-5678', message: 'コンサートの予約をしたいです');
実践
実践例5: 地図を開く
Google Maps で開く:
Future<void> openMap({
  required double latitude,
  required double longitude,
  String? label,
}) async {
  final Uri url = Uri.parse(
    'https://www.google.com/maps/search/?api=1&query=$latitude,$longitude',
  );
  
  if (await canLaunchUrl(url)) {
    await launchUrl(url);
  }
}

// 使い方
openMap(
  latitude: 35.6812,
  longitude: 139.7671,
  label: '東京タワー',
);

住所から開く:

Future<void> openMapByAddress(String address) async {
  final String encodedAddress = Uri.encodeComponent(address);
  final Uri url = Uri.parse(
    'https://www.google.com/maps/search/?api=1&query=$encodedAddress',
  );
  
  if (await canLaunchUrl(url)) {
    await launchUrl(url);
  }
}

// 使い方
openMapByAddress('東京都港区芝公園4-2-8');

マップアプリで直接開く(iOS/Android):

Future<void> openMapApp({
  required double latitude,
  required double longitude,
}) async {
  // iOS の場合は Apple Maps
  // Android の場合は Google Maps
  final Uri url = Uri.parse(
    'geo:$latitude,$longitude?q=$latitude,$longitude',
  );
  
  if (await canLaunchUrl(url)) {
    await launchUrl(url);
  }
}
実践
実践例6: カスタムURL(他のアプリ)
LINEを開く:
Future<void> openLine(String lineId) async {
  final Uri url = Uri.parse('https://line.me/ti/p/$lineId');
  
  if (await canLaunchUrl(url)) {
    await launchUrl(url);
  }
}

Twitterを開く:

Future<void> openTwitter(String username) async {
  final Uri url = Uri.parse('https://twitter.com/$username');
  
  if (await canLaunchUrl(url)) {
    await launchUrl(url);
  }
}

YouTubeを開く:

Future<void> openYouTube(String videoId) async {
  final Uri url = Uri.parse('https://www.youtube.com/watch?v=$videoId');
  
  if (await canLaunchUrl(url)) {
    await launchUrl(url);
  }
}

プラットフォーム別の設定

Android:
android/app/src/main/AndroidManifest.xml に追加:

<manifest>
    <application>
        <!-- ... -->
    </application>
    
    <!-- インターネット接続 -->
    <uses-permission android:name="android.permission.INTERNET" />
    
    <!-- 電話をかける(オプション) -->
    <uses-permission android:name="android.permission.CALL_PHONE" />
    
    <!-- クエリ設定(Android 11+) -->
    <queries>
        <!-- ブラウザ -->
        <intent>
            <action android:name="android.intent.action.VIEW" />
            <data android:scheme="https" />
        </intent>
        
        <!-- 電話 -->
        <intent>
            <action android:name="android.intent.action.DIAL" />
            <data android:scheme="tel" />
        </intent>
        
        <!-- メール -->
        <intent>
            <action android:name="android.intent.action.SENDTO" />
            <data android:scheme="mailto" />
        </intent>
        
        <!-- SMS -->
        <intent>
            <action android:name="android.intent.action.SENDTO" />
            <data android:scheme="sms" />
        </intent>
    </queries>
</manifest>

重要: Android 11 以降は タグが必要!

iOS:
ios/Runner/Info.plist に追加:

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>https</string>
    <string>http</string>
    <string>tel</string>
    <string>mailto</string>
    <string>sms</string>
</array>

エラーハンドリング

より丁寧なエラー処理:

Future<void> launchUrlSafely(String urlString) async {
  try {
    final Uri url = Uri.parse(urlString);
    
    if (await canLaunchUrl(url)) {
      final bool launched = await launchUrl(url);
      if (!launched) {
        throw Exception('URLを開けませんでした');
      }
    } else {
      throw Exception('このURLは開けません: $urlString');
    }
  } catch (e) {
    print('エラー: $e');
    // ユーザーにエラーを表示
    // showDialog(...) など
  }
}

ユーザーにエラーを表示:

Future<void> launchUrlWithDialog(
BuildContext context,
String urlString,
) async {
try {
final Uri url = Uri.parse(urlString);

if (await canLaunchUrl(url)) {
await launchUrl(url);
} else {
_showErrorDialog(context, 'このURLは開けません');
}
} catch (e) {
_showErrorDialog(context, 'エラーが発生しました: $e');
}
}

void _showErrorDialog(BuildContext context, String message) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('エラー'),
content: Text(message),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('OK'),
),
],
),
);
}

便利なヘルパークラス

再利用しやすいように、クラスにまとめる:

class UrlLauncherHelper {
  // Webサイトを開く
  static Future<bool> openWebsite(String url) async {
    final Uri uri = Uri.parse(url);
    if (await canLaunchUrl(uri)) {
      return await launchUrl(uri, mode: LaunchMode.externalApplication);
    }
    return false;
  }
  
  // 電話をかける
  static Future<bool> makePhoneCall(String phoneNumber) async {
    final Uri uri = Uri.parse('tel:$phoneNumber');
    if (await canLaunchUrl(uri)) {
      return await launchUrl(uri);
    }
    return false;
  }
  
  // メールを送る
  static Future<bool> sendEmail({
    required String email,
    String? subject,
    String? body,
  }) async {
    final Map<String, String> params = {};
    if (subject != null) params['subject'] = subject;
    if (body != null) params['body'] = body;
    
    final Uri uri = Uri(
      scheme: 'mailto',
      path: email,
      query: _encodeQueryParameters(params),
    );
    
    if (await canLaunchUrl(uri)) {
      return await launchUrl(uri);
    }
    return false;
  }
  
  // 地図を開く
  static Future<bool> openMap({
    required double latitude,
    required double longitude,
  }) async {
    final Uri uri = Uri.parse(
      'https://www.google.com/maps/search/?api=1&query=$latitude,$longitude',
    );
    if (await canLaunchUrl(uri)) {
      return await launchUrl(uri);
    }
    return false;
  }
  
  // SMS を送る
  static Future<bool> sendSMS(String phoneNumber, {String? message}) async {
    final Uri uri = Uri(
      scheme: 'sms',
      path: phoneNumber,
      queryParameters: message != null ? {'body': message} : null,
    );
    if (await canLaunchUrl(uri)) {
      return await launchUrl(uri);
    }
    return false;
  }
  
  static String? _encodeQueryParameters(Map<String, String> params) {
    if (params.isEmpty) return null;
    return params.entries
        .map((e) => '${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
        .join('&');
  }
}

// 使い方
await UrlLauncherHelper.openWebsite('https://example.com');
await UrlLauncherHelper.makePhoneCall('03-1234-5678');
await UrlLauncherHelper.sendEmail(
  email: 'support@example.com',
  subject: 'お問い合わせ',
);

注意点とコツ

必ず canLaunchUrl でチェック

// ❌ 危険
await launchUrl(Uri.parse('https://example.com'));  // エラーになるかも

// ✅ 安全
if (await canLaunchUrl(uri)) {
  await launchUrl(uri);
}

Uri.parse を使う

// ❌ 間違い
launchUrl('https://example.com');  // 型が違う!

// ✅ 正しい
launchUrl(Uri.parse('https://example.com'));

日本語URLはエンコード

// ❌ 動かないかも
final url = 'https://example.com/検索';

// ✅ エンコードする
final encoded = Uri.encodeFull('https://example.com/検索');
final url = Uri.parse(encoded);

電話番号は文字列のまま

// ✅ OK
makePhoneCall('03-1234-5678');
makePhoneCall('0312345678');
makePhoneCall('+81-3-1234-5678');

ハイフンや括弧があっても大丈夫!

  1. プラットフォーム別の挙動
機能iOSAndroid
tel:確認ダイアログが出る直接電話アプリ起動
mailto:メールアプリ選択メールアプリ選択
sms:SMSアプリ起動SMSアプリ起動

プラットフォームによって挙動が違う

主要なURLスキーム一覧

スキーム用途
https://Webサイトhttps://example.com
tel:電話tel:03-1234-5678
mailto:メールmailto:support@example.com
sms:SMSsms:090-1234-5678
geo:地図geo:35.6812,139.7671

一言まとめ:
url_launcher はURLや電話、メールを開くパッケージ! Uri.parse でURLを作って、canLaunchUrl でチェックして、launchUrl で開く。WebサイトやSNS、地図、電話など何でも開ける万能選手! 🚀✨