Flutter アプリでURLを開く方法

##  url_launcher
ネイティブアプリからWebサイトを開く場合は「url_launcher」というパッケージを使用します。
例えば、Webサイトとして公開されている利用規約をネイティブアプリから開く際などに使用されることが多いです。

## パッケージの準備
まずは [url_launcherパッケージ](https://pub.dev/packages/url_launcher) を pubspeck.yaml に記述し、Pub get します。

```markdown: pubspeck.yaml
dependencies:
  flutter:
    sdk: flutter
  url_launcher: ^6.1.5
```
<br>
次にパッケージを使いたい画面でインポートします。

```dart: main.dart
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher/url_launcher_string.dart';
```

SMSなどを使用する場合は他の準備も必要ですが、今回は実装しません。
これで準備は完了です！

URLへの遷移を実装する方法は以下の二つがあります。
+ String 型のURLを使用する
+ Uri 型のURLを使用する

それぞれ実装方法を解説します。

## String型のURL
[ChangeLog](https://pub.dev/packages/url_launcher/changelog) を見ると、バージョン6.1.0から大きな変更が加えられています。
それまでは String型のURLで遷移先を指定することが一般的だったものが、Uri 型で指定するように変更されました。しかし、String 型で指定する方法も残されているので紹介します。 

コードは以下の通りです。

```dart:main.dart
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher/url_launcher_string.dart';

class UrlLauncherExample extends StatelessWidget {
  UrlLauncherExample({Key? key}) : super(key: key);

  final _urlLaunchWithStringButton = UrlLaunchWithStringButton();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("UrlLauncherExample"),
      ),
      body: Center(
        child: ElevatedButton(
          child: const Text("Google"),
          onPressed: () {
            _urlLaunchWithStringButton.launchUriWithString(
              context,
              "https://www.google.com",
            );
          }),
        ),
      );
  }
}

class UrlLaunchWithStringButton {
  final alertSnackBar = SnackBar(
    content: const Text('このURLは開けませんでした'),
    action: SnackBarAction(
      label: '戻る',
      onPressed: () {},
    ),
  );

  Future launchUriWithString(BuildContext context, String url) async {
    if (await canLaunchUrlString(url)) {
      await launchUrlString(url);
    } else {
      alertSnackBar;
      ScaffoldMessenger.of(context).showSnackBar(alertSnackBar);
    }
  }
}
```
今回は`UrlLaunchWithStringButton`というクラスを作成しました。
<br>

```dart
Future launchUriWithString(BuildContext context, String url) async {
    if (await canLaunchUrlString(url)) {
      await launchUrlString(url);
    } else {
      alertSnackBar;
      ScaffoldMessenger.of(context).showSnackBar(alertSnackBar);
    }
  }
```
このコードでは String 型のURLで遷移するための処理を記述しています。
引数として ``context``と``url``を取得しています。

``canLaunchUrlString()``メソッドは引数に指定したURLに遷移することができるかを確かめるためのメソッドです。遷移可能であれば True、不可能であれば False を返すため、遷移前の条件分岐に使用されています。

``launchUrlString()``メソッドで引数に指定したURLへ遷移します。
<br>

```dart: main.dart
final alertSnackBar = SnackBar(
  content: const Text('このURLは開けませんでした'),
    action: SnackBarAction(
      label: '戻る',
      onPressed: () {},
  ),
);
```
このコードでは先述した``launchUrlString()``メソッドで引数に指定したURLへ遷移できなかった際に表示する SnackBar を記述しています。
<br>

```dart: main.dart
final _urlLaunchWithStringButton = UrlLaunchWithStringButton();
```
このコードでは作成した `UrlLaunchWithStringButton`クラスをインスタンス化し、`_urlLaunchWithStringButton`という変数に代入しています。
<br>

```dart: main.dart
onPressed: () {
  _urlLaunchWithStringButton.launchUriWithString(
    context,
    "https://www.google.com",
  );
}
```
このコードでは`_urlLaunchWithStringButton`のメソッドを使って、引数に `context`と遷移先のURL（今回は Googleのページ）を指定しています。

第二引数のURLを変更することで遷移先を変更することができます。


## Uri 型のURL
```dart:main.dart
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher/url_launcher_string.dart';

class UrlLauncherExample extends StatelessWidget {
  UrlLauncherExample({Key? key}) : super(key: key);

  final _urlLaunchWithUriButton = UrlLaunchWithUriButton();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("UrlLauncherExample"),
      ),
      body: Center(
        child: ElevatedButton(
          child: const Text("Flutter"),
          onPressed: () {
            _urlLaunchWithUriButton.launchUrlWithUri(context);
          }),
        ),
      );
  }
}

class UrlLaunchWithUriButton {
  final Uri _flutterUrl = Uri.parse('https://flutter.dev');
  final alertSnackBar = SnackBar(
    content: const Text('このURLは開けませんでした'),
    action: SnackBarAction(
      label: '戻る',
      onPressed: () {},
    ),
  );

  Future<void> launchUrlWithUri(BuildContext context) async {
    if (!await launchUrl(
      _flutterUrl,
    )) {
      alertSnackBar;
      ScaffoldMessenger.of(context).showSnackBar(alertSnackBar);
    }
  }
}
```
String 型のURLと区別するため、`UrlLaunchWithUriButton`というクラスを作成しています。
<br>

```dart:main.dart
final Uri _flutterUrl = Uri.parse('https://flutter.dev');
```
このコードでは Uri 型へ変更した URL を `_flutterUrl` という変数に代入しています。
<br>

```dart: main.dart
Future<void> launchUrlWithUri(BuildContext context) async {
  if (!await launchUrl(
    _flutterUrl,
  )) {
    alertSnackBar;
    ScaffoldMessenger.of(context).showSnackBar(alertSnackBar);
  }
}
```
遷移先と指定したURLがString型だったときは `launchUrlString()`メソッドで引数に指定したURLへ遷移していましたが、このコードでは遷移先の URL が Uri 型であるため、 `launchUrl()`メソッドを使用しています。混同しないよう気をつけましょう。

その他のコードは String 型の URL とほぼ同じであるため、省略します。

## mode
mode とは URL を開く際の遷移方法を指します。
6.1.0バージョンまで `launchUrl()` メソッドには `forceSafariVC` や `forceWebView` というプロパティがありましたが、アップデートされたパッケージでは `mode`というプロパティに変更されています。
`mode` には以下の4種類があります。

+ inAppWebView
+ externalApplication
+ externalNonBrowserApplication
+ platformDefault

| コード | 説明 |
|:-:|:-:|
| LaunchMode.inAppWebView | デフォルトで指定されているモード。<br>遷移先のページをアプリ内の一つのページで開いているかのように遷移するモード |
| LaunchMode.externalApplication | Webページを Safari など<br>別のアプリケーションで開くモード |
| LaunchMode.externalNonBrowserApplication | ブラウザ以外の外部のアプリケーションを<br>開く際に使用するモード　　|
| LaunchMode.platformDefault |　　プラットフォームのデフォルトの <br> 遷移方法を採用するモード　　|

iOSシュミレータでは `platformDefault` は `inAppWebView` と同じになっていました。

### inAppWebView と externalApplication の違い

inAppWebView
![url_launcher.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609901/ab9fcec5-c948-8bb2-f282-0c0d68ae73b7.gif)
inAppWebView ではアプリ内の一ページのように遷移し、画面左上の「完了」ボタンを押すと元のページに戻ることができます。
<br>

externalApplication
![url_launcher_gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609901/1839ad33-630f-9276-6237-3d2d49960ec9.gif)
externalApplication では開いていたアプリとは別のアプリとして Safari を開いています。
元のページに戻るには「完了」ボタンではなく、SafeArea にある 「◀︎ アプリ名」 の表示を押す必要があります。
inAppWebView と比較するとユーザにとって元のアプリに戻るコストが高いと言えるでしょう。

逆に、Webページ上でユーザにじっくり読んでもらいたいコンテンツがある場合は externalApplication による遷移を実装した方が良いと言えるのではないでしょうか。

以上です。

## あとがき
今回は簡単な Webサイトへの遷移のみを行いましたが、実機で試す場合にはインストールされているアプリとの連携も可能なので、使ってみたいと思います。

参考にしていただければ幸いです。
誤っている箇所があればご指摘いただければ幸いです。

### 参考にしたサイト
https://pub.dev/packages/url_launcher