Flutter には RichText
という Widget があります。
その名の通り、テキストの一部だけ色やサイズなどを変えられるという、その名の通りリッチな 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: ' です。',
),
],
),
),
RichText
の text
プロパティに InlineSpan
の子クラス(通常使うのは TextSpan
かと思います)を指定し、さらに TextSpan
の children
プロパティに、スタイルを変更したい場所で文章を区切って TextSpan
のリストを指定します。

なお、RichText
のデフォルトの文字色は Flutter のデフォルト背景色である黒に合わせて白となっていますので、白背景で消えないように親の TextSpan
で文字色を黒く指定しています。
「あれ、Flutter の背景色って白じゃない?」という声が聞こえてくる気がしますが、それは MaterialApp
のデフォルトスタイルです。 MaterialApp
背景色を白に、文字色を DefaultTextStyle
で白に設定するのですが、どうやら RichText
は Text
と違ってこの 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
など正規表現を使って文章を分割しておく必要があります。
注意点
上の方で書いた RichText
は DefaultTextStyle
が適用されないのと同じ話なのか、ユーザーが端末の「アクセシビリティ」設定から文字サイズを変えた場合、 Text
は勝手に文字が大きくなるのに RichText
はそのまま、と言う問題が発生します。
以下は同じフォントサイズを指定した、Text
と RichText
を縦にならべ、端末の設定から文字サイズを大きくした例です。

この問題は RichText
の textScaleFactor
に MediaQuery.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
を検討してみると良いでしょう。