【Flutter/Dart】メールアプリを起動してメールを送信する(url_launcher)

目次

概要

Flutterを使用していて、時にサービスの内容をユーザーに通知したりするときもあるだろう。
そんなときはFlutterで入力した内容を元にメールアプリを起動することができる。
手段は二つ。

  • Flutterアプリ内でメールアプリを起動する
  • Flutterアプリからメールアプリに切り替える

これはWebブラウザでも、アプリ内でWebページを表示するか、ブラウザで表示するかのようなものだ。

今回は、「Flutterアプリからメールアプリに切り替える」というものをやってみる。

実装方法

メール送信に必要なパッケージを追加する

dependencies:
  flutter:
    sdk: flutter
    url_launcher: ^6.2.2

追加したら以下のコマンドを実行してパッケージのインストール状態を更新しよう。

$ flutter pub upgrade

ソースコード

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'SendMailView/SendMailView.dart';

void main() async{

  runApp(const ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
        useMaterial3: true,
      ),
      home: SendMailView(),
    );
  }
}
import 'package:flutter_riverpod/flutter_riverpod.dart';

final sendMailModelProvider = Provider((ref) => SendMailModel());

class SendMailModel {

}
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'SendMailModel.dart';

final sendMailRepositoryProvider = Provider((ref) => SendMailRepositoryImpl(model: ref.read(sendMailModelProvider)));

abstract class SendMailRepository {

}

class SendMailRepositoryImpl implements SendMailRepository {
  SendMailRepositoryImpl({required SendMailModel model}): _model = model;

  final SendMailModel _model;
}
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'SendMailViewModel.dart';

class SendMailView extends ConsumerWidget {
  SendMailView({Key? key}) : super(key: key);
  SendMailViewModel _viewModel = SendMailViewModel();

  @override
  Widget build(BuildContext context, WidgetRef ref) {

    return Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: const Text("メールを送ろう"),
        ),
        body: Builder(
            builder: ((context) => SingleChildScrollView(
                child: Column(
                    children: [
                      _sendTo(),
                      _mailTitle(),
                      _carbonCopiesTextField(),
                      _blindCarbonCopiesTextField(),
                      _mailMassage(),
                      _sendButton()
                    ]
                )
            ))
        )
    );
  }

  Widget _sendTo() {
    return Column(
      children: [
        const Text("宛先"),
        TextField(
          controller: _viewModel.recipientsEditingController,
          obscureText: false,
          decoration: const InputDecoration(
            border: OutlineInputBorder(),
            labelText: "",
          ),
        )
      ],
    );
  }

  Widget _mailTitle() {
    return Column(
      children: [
        const Text("件名"),
        TextField(
          controller: _viewModel.titleEditingController,
          obscureText: false,
          decoration: const InputDecoration(
            border: OutlineInputBorder(),
            labelText: "",
          ),
        )
      ],
    );
  }

  Widget _carbonCopiesTextField() {
    return Column(
      children: [
        const Text("CC:"),
        TextField(
          controller: _viewModel.carbonCopiesEditingController,
          obscureText: false,
          decoration: const InputDecoration(
            border: OutlineInputBorder(),
            labelText: "",
          ),
        )
      ],
    );
  }

  Widget _blindCarbonCopiesTextField() {
    return Column(
      children: [
        const Text("BCC:"),
        TextField(
          controller: _viewModel.blindCarbonCopiesEditingController,
          obscureText: false,
          decoration: const InputDecoration(
            border: OutlineInputBorder(),
            labelText: "",
          ),
        )
      ],
    );
  }

  Widget _mailMassage() {
    return Column(
      children: [
        const Text("本文"),
        TextField(
          controller: _viewModel.mailBodyEditingController,
          obscureText: false,
          maxLines: 6,
          keyboardType: TextInputType.multiline,
          decoration: const InputDecoration(
            border: OutlineInputBorder(),
            labelText: "",
          ),
        ),
      ],
    );
  }

  Widget _sendButton() {
    return OutlinedButton(
        onPressed: () { _viewModel.sendMail(); },
        child: const Text("送信")
    );
  }
}
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_email_sender/flutter_email_sender.dart';
import 'package:url_launcher/url_launcher.dart';

import 'SendMailRepository.dart';

final sendMailViewModelProvider = ChangeNotifierProvider((ref) => SendMailViewModel(repository: ref.read(sendMailRepositoryProvider)));

class SendMailViewModel extends ChangeNotifier {
  SendMailRepository? repository;

  var recipientsEditingController = TextEditingController();
  var titleEditingController = TextEditingController();
  var carbonCopiesEditingController = TextEditingController();
  var blindCarbonCopiesEditingController = TextEditingController();
  var mailBodyEditingController = TextEditingController();

  SendMailViewModel({this.repository}) {

  }

  void sendMail()  {
    launchMailApp();
  }

  Future<bool> launchMailApp() {
    final Uri emailUri = Uri(
        scheme: 'mailto',
        path: recipientsEditingController.text,
        queryParameters: {
          'subject': titleEditingController.text,
          'cc':carbonCopiesEditingController.text,
          'bcc':blindCarbonCopiesEditingController.text,
          'body':mailBodyEditingController.text
        }
    );

    return launchUrl(
        emailUri
    );
  }
}

スクリーンショット

左:入力画面
右:送信ボタン押下時の

詳細

今回の目玉はこれだ。

まずはインストールしたパッケージをimportしよう。
これを書かないとメールアプリを立ち上げるのに必要なものが使えない。

import 'package:url_launcher/url_launcher.dart';

そして、launchMailAppメソッドの中身が大事だ。
URIオブジェクトを生成して、launchUrlメソッドに渡してあげればメールアプリを立ち上げることができる。
大元は画面遷移と変わらない。リンクをタップするとLINEが立ち上がったりする経験があると思う。

void sendMail()  {
  launchMailApp();
}

Future<bool> launchMailApp() {
  final Uri emailUri = Uri(
      scheme: 'mailto',
      path: recipientsEditingController.text,
      queryParameters: {
        'subject': titleEditingController.text,
        'cc':carbonCopiesEditingController.text,
        'bcc':blindCarbonCopiesEditingController.text,
        'body':mailBodyEditingController.text
      }
  );

  return launchUrl(
      emailUri
  );
}

設定内容は以下のようになる。

引数名(キー名)内容
scheme使用するアプリ名
メールの場合は「mailto」になる
pathメールの宛先
「,」で区切ることで複数相手に送信することができる
queryParametersURIに補助的な情報を格納するためのもの。
会員制のサイトへアクセスすると、
URLの「?」以降にユーザーIDが表示されていたりすると思う。
それと同じようなものだ。
 subjectメールの件名
 ccCCの宛先。
「,」で区切ることで複数人を設定できる
 bccBCCの宛先。
「,」で区切ることで複数人を設定できる
 bodyメールの本文

そして、それらを設定してlaunchUrlメソッドに上記で作成したオブジェクトを渡すと、
その入力内容が入力された状態でメールアプリを起動することができる。

ただ、注意点として、AndroidにおいてもiOSにおいても、端末の標準メールアプリにログインしておくことが大前提だ。

もし企業で使うなら、その企業アカウントでログインした端末上で操作しよう。

また、今回はTextFieldに入力された文字列を取得したり、TextFieldが複数行入力できたりと、いろいろと使える項目も含まれている。

参考ページ