はじめに
テキスト中のURL部分を自動でハイパーリンク化する方法をご紹介します。
目標
テキスト中のURL部分に自動でリンクをはる。
URL部分をタップすると、URLが示すWebページに遷移する。
実装
FlutterにおけるText
ウィジェットのソースコードは次のようになっており、TextSpan
ウィジェットから構成されていることがわかります。
Widget result = RichText(
...
text: TextSpan(
style: effectiveTextStyle,
text: data,
children: textSpan != null ? <InlineSpan>[textSpan!] : null,
),
);
ここで、TextSpan
にはユーザによる操作に対する処理を登録するrecognizer
というプロパティがあります。
したがって、今回の目標である「URL部分をタップしたらリンク先に遷移する」という機能は、TextSpan
にタップ時の処理を設定することで実現できます。
URLをタップするとリンク先に遷移する処理の実装
タップ時の処理については
TextSpan
のrecognizer
プロパティを設定することで実現できます。
指定したURLに遷移する機能はurl_launcher
というパッケージを使用して実現します。
仮にURL文字列をurlText
とすると、リンク先への遷移処理は次のようになります(コードサンプルでは外部ブラウザを使用するよう指定しています)。
await launchUrlString(_encadedUrl,mode: LaunchMode.externalApplication);
またTextSpan
の
recognizer
にタップされた時の処理を実装するには、TapGestureRecognizer
を使用します。
したがって、テキストをタップした際、そのテキストの文字列が示すWebページに遷移するには、次のような実装を行う必要があります。
final _recognizer = TapGestureRecognizer()
..onTap = () async {
await launchUrlString(_encadedUrl,mode: LaunchMode.externalApplication);
};
final _textSpan = TextSpan(
text: 'urlText',
recognizer: _recognizer,
);
URLのエンコード処理を追加した結果は次のようになります。
final _encadedUrl = Uri.encodeFull('urlText');
final _recognizer = TapGestureRecognizer()
..onTap = () async {
await launchUrlString(_encadedUrl,mode: LaunchMode.externalApplication);
};
final _textSpan = TextSpan(
text: 'urlText',
recognizer: _recognizer,
);
これで urlText
という文字列をタップしたときに、 urlText
というURLが表すリンク先に飛ぶ処理が実装できました。
テキストからURL部分を自動で抽出する処理の実装
任意のテキスト中のURL部分を自動でハイパーリンク化するためには、テキストからURL部分を抽出する必要があります。
URLの正規表現を仮に次の様に設定します(実装の目的によって自由に変えて構いません)。
static RegExp get _urlRegExp => RegExp(
r"(http(s)?:\/\/[a-zA-Z0-9-.!'()*;/?:@&=+$,%_#]+)",
caseSensitive: false,
);
この正規表現を使って、正規表現にマッチする部分はリンクとして、マッチしない部分はノーマルなテキストとしてTextSpan
を生成し、最終的にはText
ウィジェットの形にすることを目指します。
文字列が与えられた時に前述のrecognizer
をセットしたTextSpan
を返却するメソッドgenerateLinkTextSpan
を以下の様に定義します。
TextSpan generateLinkTextSpan(String url){
final _encadedUrl = Uri.encodeFull(url);
final _recognizer = TapGestureRecognizer()
..onTap = () async {
await launchUrlString(_encadedUrl,mode: LaunchMode.externalApplication);
};
final _textSpan = TextSpan(
text: url,
recognizer: _recognizer,
//style: リンク部分のテキストスタイル
);
return _textSpan;
}
また、URLでない文字列が与えられたときにノーマルなテキストのTextSpan
を返却するメソッドgenerateCommonTextSpan
を次のように定義します。
TextSpan generateLinkTextSpan(String url){
final _textSpan = TextSpan(
text: url,
);
return _textSpan;
}
任意のテキストを正規表現にマッチする部分と、マッチしない部分に分割する処理はsplitMapJoin
を利用することで、次のようにかけます。
this._text_.splitMapJoin(RegExp('<正規表現パターン>'),
//マッチした部分
onMatch: (Match match) {
//match.group[0]でパターン一致全体の文字列を取得できる
return ''; //String型をreturnする必要があるため
},
//マッチしなかった部分
onNonMatch: (String text) {
//マッチしなかった文字列に対する処理
return '';
}
);
よって任意のテキストを正規表現にしたがってURL部分とそれ以外の部分に分け、URLにはタップしたらWeb遷移するような機能を持ったTextSpanを返却するメソッドgenerate
は次のようになります。
//実際には引数_rawTextはコンストラクタで与えるが、簡単のため以下とする
TextSpan generate(Text _rawText) {
final List<TextSpan> _textSpans = [];
_rawText.splitMapJoin(
ThisIsAutoLinkTextSpan._urlRegExp,
onMatch: (Match match) {
final _urlSpan = this._generateUrlTextSpan(match.group(0) ?? '');
_textSpans.add(_urlSpan);
return '';
},
onNonMatch: (String text) {
final _commonSpan = this._generateCommonTextSpan(text);
_textSpans.add(_commonSpan);
return '';
},
);
return TextSpan(children: _textSpans);
}
こちらのメソッドを以下のようにText.rich
ウィジェット内で利用すると、任意の文字列のURL部分に自動でハイパーリンク機能を持たせることができます。
(ここではgenerate
が定義されるクラスを仮にAutoLinkText
とします。)
Text.rich(
AutoLinkText.generate(rawText),
)
おわりに
テキスト中のURLを自動でハイパーリンク化する方法を紹介しました。
Text
ウィジェットだけでは対応できないUI表現は、このようにTextSpan
をうまく利用することで可能となる場合があります。