🕸️

Flutter Webでリンクテキストを表示する方法を比較する

2024/12/03に公開

はじめに

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: ' を含む文章です。テキストの選択が可能です。',
      ),
    ],
  ),
);

こんな感じです。良い点としては、マウスカーソルでテキストの選択が可能です。悪い点としては、リンクテキストの上で右クリックしても「新しいタブで開く」の操作はできません。

url_launcherLinkウィジェットを使用して実装する方法もあります。

https://pub.dev/documentation/url_launcher/latest/link/Link-class.html

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