Flutter Webでリンクテキストを表示する方法を比較する
はじめに
Flutter Webでリンクテキストを表示する実装をしたい際、ブラウザ上で以下のような挙動が自然にできてほしいです。
- テキスト選択がマウスドラッグで可能であること
- リンクを右クリックして「新しいタブで開く」の操作ができること
- リンクをクリックすると、埋め込んだURLに遷移すること
これらすべてを満たすことは意外なことにFlutter Webでは難しいです。この記事では、リンクテキストの表示の方法についていくつか考えて見ます。
URLに遷移すること自体の実装
URLの遷移自体は、url_launcher
パッケージを用い、以下のように実装することで可能です。
if (await canLaunchUrlString(url)) {
await launchUrlString(url);
}
これをonTap
などでいい感じに呼び出せば良さそうです。問題はその表示部品をどのように実装するか、です。
1. SelectableText.rich を使用する方法
1つ目に実装するのが、SelectableText
を使って実装する方法です。
SelectableText.rich(
TextSpan(
text: 'これは ',
style: const TextStyle(color: Colors.black),
children: [
TextSpan(
text: 'リンクテキスト',
style: const TextStyle(
color: Colors.blue,
decoration: TextDecoration.underline,
),
recognizer: TapGestureRecognizer()
..onTap = () async {
final url = 'https://example.com';
if (await canLaunchUrlString(url)) {
await launchUrlString(url);
}
},
),
const TextSpan(
text: ' を含む文章です。テキストの選択が可能です。',
),
],
),
);
こんな感じです。良い点としては、マウスカーソルでテキストの選択が可能です。悪い点としては、リンクテキストの上で右クリックしても「新しいタブで開く」の操作はできません。
2. Link ウィジェットを使用する方法
url_launcher
のLink
ウィジェットを使用して実装する方法もあります。
Link(
uri: Uri.parse('https://example.com'),
target: LinkTarget.blank, // 新しいタブで開く
builder: (BuildContext context, Future<void> Function()? followLink) {
return GestureDetector(
onTap: followLink,
child: Text(
'リンクテキスト(右クリックメニュー有効)',
style: const TextStyle(
color: Colors.blue,
decoration: TextDecoration.underline,
),
),
);
},
);
こんな感じです。良い点としては、リンクテキストの上で右クリックして「新しいタブで開く」が選択できます。悪い点としては、テキスト選択の操作はできません。
3. RichTextを使用する方法
以下のようにRichText
を使用する方法もあります。
テキストのレイアウトをグリグリしたいときなどは良いかもしれません。
RichText(
text: TextSpan(
text: 'これは ',
style: const TextStyle(color: Colors.black),
children: [
TextSpan(
text: 'リンクテキスト',
style: const TextStyle(
color: Colors.blue,
decoration: TextDecoration.underline,
),
recognizer: TapGestureRecognizer()
..onTap = () async {
final url = 'https://example.com';
if (await canLaunchUrlString(url)) {
await launchUrlString(url);
}
},
),
const TextSpan(
text: ' を含む文章です。テキストの選択はできません。',
),
],
),
);
こんな感じです。悪い点としては、テキスト選択の操作はできませんし、リンクテキストの上で右クリックして「新しいタブで開く」もできません。
4. TextButton を使用する方法
以下のようにTextButton
を使用する方法もあります。
通常はボタンっぽく外枠や色つけをするので、あまりこういった形では実装しないかもしれません。
TextButton(
onPressed: () async {
final url = 'https://example.com';
if (await canLaunchUrlString(url)) {
await launchUrlString(url);
}
},
child: const Text(
'リンクテキスト(ボタン)',
style: TextStyle(
decoration: TextDecoration.underline,
),
),
);
こんな感じです。悪い点としては、テキスト選択の操作はできませんし、リンクテキストの上で右クリックして「新しいタブで開く」もできません。
良い点として、ボタン選択時のアニメーションは可愛い感じで好きです。
5. InkWell を使用する方法
以下のようにInkWell
を使用する方法もあります。
InkWell(
onTap: () async {
final url = 'https://example.com';
if (await canLaunchUrlString(url)) {
await launchUrlString(url);
}
},
child: Text(
'リンクテキスト(InkWell)',
style: const TextStyle(
color: Colors.blue,
decoration: TextDecoration.underline,
),
),
);
こんな感じです。悪い点としては、テキスト選択の操作はできませんし、リンクテキストの上で右クリックして「新しいタブで開く」もできません。
良い点として、タップ時のエフェクトは可愛い感じで好きです。
まとめ
今回検証した範囲では、以下の挙動となりました。
方法 | テキスト選択 | 右クリックメニューでのリンク操作 |
---|---|---|
SelectableText.rich | 可能 | 無効 |
Link ウィジェット | 不可 | 有効 |
RichText | 不可 | 無効 |
TextButton | 不可 | 無効 |
InkWell | 不可 | 無効 |
リンクテキストをFlutter Webで表示する場合、テキスト選択を可能にしたいのであればSelectableText
、右クリックでのリンク操作を有効にしたいのであればLink
ウィジェットが良いかもしれません。
一方で、テキストのレイアウトによりこだわりたい場合には、他のウィジェットで実装する選択肢もあり得ると思います。
Discussion