LoginSignup
2

Flutter URL/メールアドレス抽出+外部ブラウザ/メーラー起動

Posted at

やりたいこと

文字列内のURL/メールアドレスを抽出し、リッチテキストでその部分だけ色変換
タップするとURLなら外部ブラウザを起動、メールアドレスならメーラーを起動する
下記画像のように青字の部分が該当になります。

スクリーンショット 2023-05-28 15.15.46.png

調べたところflutterにはメールアドレスやURLを抽出して表示したり、onTapをつける系のライブラリがなさそうなので、仕方なく独自で正規表現を使って実装することに。。。

実装概要

方法としては下記のような流れで進めていきます

  1. 正規表現RegExpに対してallMatchesを使って一致するテキストを抽出
  2. 通常テキストと色付きテキストを棲み分け
  3. その後List<InlineSpan> spansのリストに全部入れて一つのwidgetとして表示
  4. URL/メーラー起動のタップイベントの付与

前提条件: 使用テキスト(安定の坊ちゃん), 正規表現(諸説あり)

const text = '親譲りの無鉄砲で子供の時から損ばかりしている。小学校に居る時分学校の二階から飛び降りて一週間程腰を抜かした事がある。https://hogehoge.comなぜそんな無闇(むやみ)をしたと聞く人があるかもしれぬ。別段深い理由でもない。hogehoge@gmail.com新築の二階から首を出していたら,同級生の一人が冗談に,いくら威張っても,そこから飛び降りる事は出来まい。弱虫や-い。と囃(はや)し立てたからである。';

final urlRegExp = RegExp(r"https?://([\w-]+\.)+[\w-]+(/[\w-./?%&=#]*)?");
final mailRegExp = RegExp(r"[a-zA-Z0-9_.+-]+@([a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\.)+[a-zA-Z]{2,}$");

final List<InlineSpan> spans = [];

実装詳細

1. URL/メールアドレス抽出

以下は URLのRegExpのallMatchesを使って一致した部分を抽出するコードです。
メールアドレスの場合はurlRegExpmailRegExpに変えればいいだけ

for (Match match in urlRegExp.allMatches(text)) {
    urlText = match.group(0)!
}

2. 通常テキストと色付きテキストを棲み分け

var currentIndex = 0;
for (Match match in urlRegExp.allMatches(text)) {
    launchRichText(match.group(0)!);
    normalText(text.substring(currentIndex, match.start);
    currentIndex = match.end;
}

 text.substring(currentIndex, match.start)は0文字目からURL部分の初めの文字数目、URL部分の終わりの文字数目から次の一致部分の初めまでを指定します。

launchRichTextはURLもしくはメールアドレス色付きとタップイベントを追加するTextSpanのwidget
normalTextは上記以外のTextSpanのwidgetです

以下は一例

TextSpan launchRichText(String text, bool mailAddressFlag) {
    return TextSpan(
        text: text,
        style: const TextStyle(color: Colors.lightBlueAccent),
        recognizer: TapGestureRecognizer()..onTap = () {
          // タップイベント
        },
    );
}

TextSpan normalText(String text) {
    return TextSpan(
      text: text,
      style: const TextStyle(color: Colors.black),
    );
}

3. List<InlineSpan> spansのリストに追加して表示

spansのリストにlaunchとnormalのTextSpanを追加
作成したListをRichTextとしてまとめて表示
これで見た目の実装は完了!

// URLに該当の文字列を抽出
for (Match match in urlRegExp.allMatches(text)) {
    spans.add(normalText(text.substring(currentIndex, match.start)));
    spans.add(launchRichText(match.group(0)!, false));
    currentIndex = match.end;
}

return RichText(
    text: TextSpan(children: spans),
);

4. タップイベントの付与

urlの外部ブラウザ遷移やメーラーの起動にはlaunchUrlを使用します。
URLの場合はそのまま、メーラーの場合はmailto:をつけてlaunchUrlにかけるとメーラーが起動します
(テストする場合は必ず端末でビルドしてください。。。シミュレーターでメーラーが出ず、時間を無駄にした経験があります)

以下は一例です。

Future<void> openLaunch(String launchText, bool mailAddressFlag) {
    if (mailAddressFlag) {
        launchText = 'mailto:$text'
    }
    final parseAddress = Uri.parse(launchText);
    try{
        await launchUrl(parseAddress);
    } catch (e) {
        // Android端末でブラウザが入ってない場合
        Logger.e('外部機能起動時にエラー発生')
    }

}

とこんな感じでしょうか

まとめ

基本的にはベタがきでこのような文章をアプリ内に載せることはないので、url/メールアドレス抽出部分はあまり使用しないかと思います。(運用管理が必要な文書/規約などはwebviewで出すのが一般的でしょう)

今回使用したlaunchUrlも特にwebviewなどで利用規約を掲載したりする際にnavigationDelegateの設定にするのが一般的な使われ方だと思うので、かなりイレギュラーなパターンだと思いますが、そういったシチュエーションもありましたので何かのご参考まで。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2