はじめに
テキスト中の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をうまく利用することで可能となる場合があります。