34
18

More than 3 years have passed since last update.

【Flutter】RichText でハッシュタグとかテキスト内リンクとか

Posted at

Flutter には RichText という Widget があります。

RichText class | Flutter

その名の通り、テキストの一部だけ色やサイズなどを変えられるという、その名の通りリッチな Text です。

RichText の基本

RichText の最小限のサンプルコードは以下の通りです。

RichText(
  text: TextSpan(
    style: TextStyle(color: Colors.black),
    children: [
      TextSpan(
        text: 'これは ',
      ),
      TextSpan(
        text: 'RichText',
        style: TextStyle(color: Colors.blue, fontSize: 24),
      ),
      TextSpan(
        text: ' です。',
      ),
    ],
  ),
),

RichTexttext プロパティに InlineSpan の子クラス(通常使うのは TextSpan かと思います)を指定し、さらに TextSpanchildren プロパティに、スタイルを変更したい場所で文章を区切って TextSpan のリストを指定します。

なお、RichTextのデフォルトの文字色は Flutter のデフォルト背景色である黒に合わせて白となっていますので、白背景で消えないように親の TextSpan で文字色を黒く指定しています。

「あれ、Flutter の背景色って白じゃない?」という声が聞こえてくる気がしますが、それは MaterialApp のデフォルトスタイルです。 MaterialApp 背景色を白に、文字色を DefaultTextStyle で白に設定するのですが、どうやら RichTextText と違ってこの DefaultTextStyle を適用しないため、何も指定しないと文字色が白くなってしまう、という理屈だそうです。

参考:
Using the same TextStyle on Text and RichText renders text with different font size

ひとまずここまで知っていれば文章中の特定の文字だけスタイルを変えることができるでしょう。

ハッシュタグとかテキスト内リンクとか

RichText は(というか TextSpan は)、recognizer プロパティでそれぞれの TextSpan のタップを検出できるようになっています。

RichText(
  text: TextSpan(
    style: TextStyle(color: Colors.black),
    children: [
      TextSpan(
        text: 'これは ',
        recognizer: TapGestureRecognizer()..onTap = () {
          print('"これは" がタップされました');
        },
      ),
      TextSpan(
        text: 'RichText',
        style: TextStyle(color: Colors.blue, fontSize: 24),
        recognizer: TapGestureRecognizer()..onTap = () {
          print('"RichText" がタップされました');
        },
      ),
      TextSpan(
        text: ' です。',
        recognizer: TapGestureRecognizer()..onTap = () {
          print('"です" がタップされました');
        },
      ),
    ],
  ),
),

これを使うことで、例えばハッシュタグやテキスト内リンクが実現できるようになります。
(なんで TapGestureRecognizer への onTap への関数の設定がコンストラクタでできないのかがとても気になりますが、そういうもののようです)

これを使うことで、 テキストの特定部分をタップされたら処理を実行する という仕組みを作れるようになります。

つまり、例えば ハッシュタグ や HTML の <a> タグのような リンク などの仕組みが実現できる、ということですね。


RichText(
  text: TextSpan(
    style: TextStyle(color: Colors.black),
    children: [
      TextSpan(
        text: 'これは ',
      ),
      TextSpan(
        text: '#flutter',
        style: TextStyle(color: Colors.blue),
        recognizer: TapGestureRecognizer()..onTap = () {
          // なにか同じハッシュタグの投稿を検索し直す処理
        },
      ),
      TextSpan(
        text: ' についての記事です。\n詳細は ',
      ),
      TextSpan(
        style: TextStyle(color: Colors.blue, decoration: TextDecoration.underline),
        text: 'https://api.flutter.dev/',
        recognizer: TapGestureRecognizer()..onTap = () async {
          await open('https://api.flutter.dev/');
        },
      ),
      TextSpan(
        text: ' を参照してください。',
      ),
    ],
  ),
),

こんな感じですね。

今回は例として分かりやすくするために文言も分割方法も固定でしたが、例えば Twitter のようにユーザーが自由に投稿した内容から自動でこれらのハッシュタグやリンクを抽出するのであれば、事前に Matcher など正規表現を使って文章を分割しておく必要があります。

注意点

上の方で書いた RichTextDefaultTextStyle が適用されないのと同じ話なのか、ユーザーが端末の「アクセシビリティ」設定から文字サイズを変えた場合、 Text は勝手に文字が大きくなるのに RichText はそのまま、と言う問題が発生します。

以下は同じフォントサイズを指定した、TextRichText を縦にならべ、端末の設定から文字サイズを大きくした例です。

この問題は RichTexttextScaleFactorMediaQuery.of(context).textScaleFactor を指定することで解決するようです。(動作確認した限りは解決しました)

RichText(
  textScaleFactor: MediaQuery.of(context).textScaleFactor,
  text: TextSpan(
    style: TextStyle(color: Colors.black),
    children: [
(以下略)

端末の設定で文字サイズが変わった時に、 Text で表示している部分はちゃんと変わってくれたけど RichText の部分は変わらない、ということにならないように、この設定も忘れずに指定しておくと良いでしょう。

参考:
widget RichText font size problem

まとめ

個人的な経験では、 RichText はここで紹介したハッシュタグやリンクの他、スプレッドシートにスタイル付きで入力された元データを同じ見た目でアプリに表示する、という要件でも活躍してくれました。

文字の一部のスタイルを変えたい、文字の一部をタップ可能にしたい、という要件が出てきたらこの RichText を検討してみると良いでしょう。

34
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
18