【Flutter】Flutterアプリからメールを送信するパッケージについて

## まえがき
2020年からFlutterの勉強を初めました。
Firebase連携でアプリを作っているのですが、その時ユーザー作成したタイミングとかで認証メールってどうやって送るんだろと思いました。

結果それはFirebase Authenticationの機能であるので、コード一文だけでできるのですが、
調べる中で、メール関係のパッケージってたくさんあるんだなと知りました。

メールを送信するんだろうなと思われるパッケージでもいくつかあって、何が違うのかよくわからないんですよね。

なのでよく見るこの二つを使ってみました。
- [url_launcher](https://pub.dev/packages/url_launcher)
- [flutter_email_sender](https://pub.dev/packages/flutter_email_sender/example)

とはいえ、深いところはあまりわからず、表面的な使い方を紹介すると言う記事になっているので、そこらへんはご了承を。

Firebaseから認証メールの送り方も最後に簡単に説明しますね、

## url_launcher
このパッケージは僕は前にも記事を書いたのですが。→[url_launcherの使い方](url_launcher)
URLをアプリ内で起動させるパッケージです。

その中の機能はURLを起動させるだけでなく、メール送信や電話やSMSなどがあります。

今回はそのメール送信についてですね

## flutter_email_sender
このパッケージは単純にメールを送るためのパッケージですね

こっちの方が他の機能がない分シンプルでいいかも

## サンプル

フォームにメールアドレスを入力してそこのメールアドレスにメールを送るというサンプルを作成しました。

送り元のメールアドレスは、デバイスのメールアドレスが使用されるので実機でテストしてみてください。
（ちなみに僕はiOSです。)

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

class MailPackage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _MailPackage();
}

class _MailPackage extends State<MailPackage> {
  TextEditingController emailController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('メールパッケージ'),
      ),
      body: Container(
        padding: EdgeInsets.all(30),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextFormField(
              controller: emailController,
              decoration: InputDecoration(hintText: 'メールアドレスを入力してください'),
            ),
            RaisedButton(
              child: Text('url_launcherで送る'),
              onPressed: () {
                urlLauncherMail();
              },
            ),
            RaisedButton(
              child: Text('flutter_email_senderで送る'),
              onPressed: () {
                flutterEmailSenderMail();
              },
            )
          ],
        ),
      ),
    );
  }

//url_launcherを使ってメールを送信
  urlLauncherMail() {
    final Uri _emailLaunchUri = Uri(
      scheme: 'mailto',
      path: '${emailController.text}',
      queryParameters: {
        'subject': 'url_launcherを使ってメールを送ります！',
        'body': '${Template().text}'
      },
    );

    return launch(
      _emailLaunchUri.toString(),
    );
  }

//flutter_email_senderを使ってメールを送信
  flutterEmailSenderMail() async {
    final Email email = Email(
      body: '${Template().text}',
      subject: 'flutter_email_senderを使ってメールを送っています！',
      recipients: ['${emailController.text}'],
      attachmentPaths: ['images/sample1.png'],
    );

    await FlutterEmailSender.send(email);
  }
}

//本文のテンプレート的な
class Template {
  String text =
      'お疲れ様です。テスト通知です。\n\nメールは届いているでしょうか？\n\n届いておりましたら、お返事お待ちしております。\nこちらのURLもみてくださいね\n\nhttps://qiita.com/ryota47/items/fb8e32f1a409026ba065';
}

import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter_email_sender/flutter_email_sender.dart';

class MailPackage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _MailPackage();
}

class _MailPackage extends State<MailPackage> {
  TextEditingController emailController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('メールパッケージ'),
      ),
      body: Container(
        padding: EdgeInsets.all(30),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextFormField(
              controller: emailController,
              decoration: InputDecoration(hintText: 'メールアドレスを入力してください'),
            ),
            RaisedButton(
              child: Text('url_launcherで送る'),
              onPressed: () {
                urlLauncherMail();
              },
            ),
            RaisedButton(
              child: Text('flutter_email_senderで送る'),
              onPressed: () {
                flutterEmailSenderMail();
              },
            )
          ],
        ),
      ),
    );
  }

//url_launcherを使ってメールを送信
  urlLauncherMail() {
    final Uri _emailLaunchUri = Uri(
      scheme: 'mailto',
      path: '${emailController.text}',
      queryParameters: {
        'subject': 'url_launcherを使ってメールを送ります！',
        'body': '${Template().text}'
      },
    );

    return launch(
      _emailLaunchUri.toString(),
    );
  }

//flutter_email_senderを使ってメールを送信
  flutterEmailSenderMail() async {
    final Email email = Email(
      body: '${Template().text}',
      subject: 'flutter_email_senderを使ってメールを送っています！',
      recipients: ['${emailController.text}'],
      attachmentPaths: ['images/sample1.png'],
    );

    await FlutterEmailSender.send(email);
  }
}

//本文のテンプレート的な
class Template {
  String text =
      'お疲れ様です。テスト通知です。\n\nメールは届いているでしょうか？\n\n届いておりましたら、お返事お待ちしております。\nこちらのURLもみてくださいね\n\nhttps://qiita.com/ryota47/items/fb8e32f1a409026ba065';
}

```

## 説明
全体は、フォームとボタン二つを配置して、各パッケージのメール送信ができるように関数を作りボタンの中に置きました。
メールの本文用のテンプレートとなる`Template`クラスも作ってみました。

### url_launcherの使い方

url_launcherパッケージを使ってメールを送信するのはこちらのコードです。

```dart
urlLauncherMail() {
  final Uri _emailLaunchUri = Uri(
    scheme: 'mailto',
    path: '${emailController.text}',
    queryParameters: {
      'subject': 'url_launcherを使ってメールを送ります！',
      'body': '${Template().text}'
    },
  );

  return launch(
    _emailLaunchUri.toString(),
  );
}
```

`Uri`クラスを定義して、
- `scheme`：`mailto`
- `path`：送る先のメールアドレス
- `queryParameters`：内容。`subject`が件名、`body`が本文

それを、`launch`の引数で渡すことで、メール送信画面に遷移します。`toString()`することを忘れずに。

### flutter_email_senderの使い方

flutter_email_senderを使ってメールを送信するのはこちらのコードです。

```dart
flutterEmailSenderMail() async {
  final Email email = Email(
    body: '${Template().text}',
    subject: 'flutter_email_senderを使ってメールを送っています！',
    recipients: ['${emailController.text}'],
  );

  await FlutterEmailSender.send(email);
}
```

`Email`で
- `body`：本文
- `subject`：件名
- `recipients`：送る先のメールアドレス
他にも`cc`,`bcc`,`isHTML`,`attachmentPaths`を渡せます。
`attachmentPaths`はなんなのかはまだちょっと理解できてないです。。

これを`FlutterEmailSender.send(email)`のように引数で渡せばメール送信画面に遷移します。
また`recipients`,`cc`,`bcc`は`List<String>`で定義されているため、複数のメールアドレスを渡すことができそうです。

## 違いと、どっちがいいか
二つともコードはなんとなく似ていますが、この二つのパッケージは大きな違いがあります。

それぞれメール送信画面に遷移するんですが、
url_launcherはメールのアプリに遷移します。(おそらくユニバーサルリンクを踏んでいる)したがって、メールを送信した後はメールのアプリを開いている状態になります。

しかしflutter_email_senderはそのアプリ内でメール送信画面が起動します。したがってメールを送信してもそのアプリが開かれた状態になります。

これならflutter_email_senderの方を使っちゃいそうだな〜

flutter_email_senderの方が本文、件名を設定するのも若干簡単だし。
cc,bccが簡単に作れるので便利そう。

## ソースコードを読んでみた感想

flutter_email_senderの`Email`クラスを開いたら、すごくシンプルで、「あれもしかしてメール送信ってパッケージ使わなくても簡単なのか？」と思いました。
しかし`return`で返してる`MethodChannel`を開くとなんか複雑で、ヒョエ〜ってなってやめました。



## Firebaseからメール送る方法

最後におまけでFirebase Authenticationからメールを送る方法を紹介します。
Firebase Authenticationをブラウザで開き、**Templates**を開きます。
そこに、メール認証のテンプレートがあります。


- メールアドレスの確認

```dart
FirebaseUser currentUser = await FirebaseAuth.instance.currentUser();

await currentUser.sendEmailVerification();
```

- パスワードの再設定

 
```dart
FirebaseAuth auth = FirebaseAuth.instance;

await auth.sendPasswordResetEmail(email: null);
```

- メールアドレスの変更

```dart
FirebaseAuth auth = FirebaseAuth.instance;

await auth.sendSignInWithEmailLink(
    email: null,
    url: null,
    handleCodeInApp: null,
    iOSBundleID: null,
    androidPackageName: null,
    androidInstallIfNotAvailable: null,
    androidMinimumVersion: null);
```

これで送れそうです。
`null`のところにはそれぞれ値を入れる必要があります。
メールアドレスの変更には、リセット用のリンクがありその時は匿名のサインインが必要になるらしく
匿名のサインイン機能をオンにしておく必要がありそうです。
（まだちゃんと試してないので、違かったらすいません。）