やりたいこと
文字列内のURL/メールアドレスを抽出し、リッチテキストでその部分だけ色変換
タップするとURLなら外部ブラウザを起動、メールアドレスならメーラーを起動する
下記画像のように青字の部分が該当になります。
調べたところflutterにはメールアドレスやURLを抽出して表示したり、onTapをつける系のライブラリがなさそうなので、仕方なく独自で正規表現を使って実装することに。。。
実装概要
方法としては下記のような流れで進めていきます
- 正規表現
RegExp
に対してallMatches
を使って一致するテキストを抽出 - 通常テキストと色付きテキストを棲み分け
- その後
List<InlineSpan> spans
のリストに全部入れて一つのwidget
として表示 - 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
を使って一致した部分を抽出するコードです。
メールアドレスの場合はurlRegExp
をmailRegExp
に変えればいいだけ
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
の設定にするのが一般的な使われ方だと思うので、かなりイレギュラーなパターンだと思いますが、そういったシチュエーションもありましたので何かのご参考まで。