【Flutter】アプリから電話の発信とメーラー起動

対象者

  • Flutterを用いて電話発信やメール送信機能をアプリケーションに組み込みたい開発者

はじめに

Flutterアプリで電話を発信したり、メールを送信したいと思いまして、試しで実装しました。Androidは動作確認しましたが、iOSはしておりません(なんか、テスト環境で動かなそうだし)。
それにしても、何年も使ってきたurl_launcherですが、電話やメールを送るのに使用するのは初めてです!

ソース

Androidの設定

まずはAndroidの設定から見ていきましょう。AndroidManifest.xmlファイルを開き、以下のように追加します。

<queries>
  <!-- Call support scheme -->
  <intent>
    <action android:name="android.intent.action.VIEW" />
    <data android:scheme="tel" />
  </intent>
  <!-- Email support scheme -->
  <intent>
    <action android:name="android.intent.action.VIEW" />
    <data android:scheme="mailto" />
  </intent>
</queries>

iOSの設定

次にiOSの設定を見ていきます。Runner/Info.plistファイルを開き、以下のように追加します。

<key>LSApplicationQueriesSchemes</key>
<array>
  <string>tel</string>
  <string>mailto</string>
</array>

これらのスキームをプラットフォームに宣言することで、それらを使用することができます。

パッケージのインストール

次に、url_launcherパッケージをpubspec.yamlファイルに追加します。

flutter pub add url_launcher
flutter pub get

プログラム

パッケージをインポートします。

import 'package:url_launcher/url_launcher.dart';

電話の発信

117(時報)に電話を掛けるプログラムです。

void openPhoneCall() async {
    final Uri callLaunchUri = Uri(
      scheme: 'tel',
      path: '117',
    );

    canLaunchUrl(callLaunchUri).then((value) {
      if (value) {
            launchUrl(callLaunchUri).then((value) {
              print('launchUrl result: $value');
            });
      } else {
            print('cannot call');
      }
    });
}

ここでは、url_launcherパッケージを利用してます。スキーム(電話の場合はtel)がミソです。
canLaunchUrlで該当のURLにアクセス可能か確認し、可能そうであればlaunchUrlで実行します。

メールの送信

メーラを起動するプログラムです。

  void openEmail() {
    final Uri emailLaunchUri = Uri(
      scheme: 'mailto',
      path: 'testtest@gmail.com',
      query: _encodeQueryParameters(<String, String>{
        'subject': 'It is subject',
        'body': 'It is message',
      }),
    );

    canLaunchUrl(emailLaunchUri).then((value) {
      if (value) {
        launchUrl(emailLaunchUri).then((value) {
          print('launchUrl result: $value');
        });
      } else {
        print('cannot call');
      }
    });
  }

  String? _encodeQueryParameters(Map<String, String> params) {
    return params.entries
        .map((MapEntry<String, String> e) =>
            '${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
        .join('&');
  }

クエリパラメータをエンコードするためのヘルパーメソッドを使用しています。メールの送信では、スキームはmailtoを使用し、クエリにはメール送信用の2つのパラメータ、subjectとbodyを定義します。

電話の発信 by flutter_phone_direct_calle

url_launcherパッケージの記事書いていたら、「flutter_phone_direct_caller」というパッケージがあった。(Androidでは)こちらは、電話画面が表示した途端に電話が掛かるので、ユーザの手間が減ると思われる。(いきなり電話が掛かって驚くかも知れませんが)

flutter pub add flutter_phone_direct_caller
flutter pub get
import 'package:flutter_phone_direct_caller/flutter_phone_direct_caller.dart';
Future<bool?> _callNumber() {
    const number = '117'; //set the number here
    return FlutterPhoneDirectCaller.callNumber(number);
}

まとめ

この記事では、Flutterを使用して電話発信やメール送信機能を追加する方法を学びました。具体的には、url_launcherパッケージを使用し、特定のスキーム(電話の場合は’tel’、メールの場合は’mailto’)を使用してデバイスのデフォルトの通話やメールアプリを起動する方法を見てきました。この方法で、電話番号やメールアドレスを変更すれば、アプリから通話・メール送信することができます。
また、電話発信用の「flutter_phone_direct_caller」も紹介しました。

参考

全ソース

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

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Column(
          children: [
            FilledButton(
              onPressed: openPhoneCall,
              child: Text('call'),
            ),
            FilledButton(
              onPressed: openEmail,
              child: Text('email'),
            ),
            FilledButton(
              onPressed: _callNumber,
              child: Text('call'),
            ),
          ],
        ));
  }

  void openPhoneCall() async {
    final Uri callLaunchUri = Uri(
      scheme: 'tel',
      path: '117',
    );

    canLaunchUrl(callLaunchUri).then((value) {
      if (value) {
        launchUrl(callLaunchUri).then((value) {
          print('launchUrl result: $value');
        });
      } else {
        print('cannot call');
      }
    });
  }

  void openEmail() {
    final Uri emailLaunchUri = Uri(
      scheme: 'mailto',
      path: 'testtest@gmail.com',
      query: _encodeQueryParameters(<String, String>{
        'subject': 'It is subject',
        'body': 'It is message',
      }),
    );

    canLaunchUrl(emailLaunchUri).then((value) {
      if (value) {
        launchUrl(emailLaunchUri).then((value) {
          print('launchUrl result: $value');
        });
      } else {
        print('cannot call');
      }
    });
  }

  String? _encodeQueryParameters(Map<String, String> params) {
    return params.entries
        .map((MapEntry<String, String> e) =>
            '${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
        .join('&');
  }

  Future<bool?> _callNumber() {
    const number = '117'; //set the number here
    return FlutterPhoneDirectCaller.callNumber(number);
  }
}