2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Flutter】入力テキスト中のURL部分の色を動的に変える

Last updated at Posted at 2023-05-02

はじめに

入力中のテキスト内のURL部分について、入力に従って動的に色(テキストスタイル)を変える方法を紹介します。
テキスト中のURL部分の判別方法については、以下の記事を参考にしてください。

目標

入力中のテキスト内のURL部分のテキストスタイルを動的に変更する。

実装

テキストを入力するためのTextFormFiealdウィジェットのソースコードは次のとおりです。

TextFormFiealdには TextEditingControllerが設定できます。
サンプルコードは以下です。
TextEditingControllerは入力テキストに対するコントローラになります。

TextFormField(
  controller: _textEditingController, //TextEditingControllerのインスタンス
  //その他のプロパティ
)

ここで、TextEditingControllerの内部実装を確認すると次の通りです。

キーボードから入力したテキストがUI部分に反映されるのは、以下buildTextSpanメソッドの部分です。

buildTextSpan
TextSpan buildTextSpan({required BuildContext context, TextStyle? style , required bool withComposing}) {
  assert(!value.composing.isValid || !withComposing || value.isComposingRangeValid);
  // If the composing range is out of range for the current text, ignore it to
  // preserve the tree integrity, otherwise in release mode a RangeError will
  // be thrown and this EditableText will be built with a broken subtree.
  final bool composingRegionOutOfRange = !value.isComposingRangeValid || !withComposing;

  if (composingRegionOutOfRange) {
    return TextSpan(style: style, text: text);
  }

  final TextStyle composingStyle = style?.merge(const TextStyle(decoration: TextDecoration.underline))
      ?? const TextStyle(decoration: TextDecoration.underline);
  return TextSpan(
    style: style,
    children: <TextSpan>[
      TextSpan(text: value.composing.textBefore(value.text)),
      TextSpan(
        style: composingStyle,
        text: value.composing.textInside(value.text),
      ),
      TextSpan(text: value.composing.textAfter(value.text)),
    ],
  );
}

UI部分に表示するべき入力ずみテキストをTextSpanの形で返却していることがわかります。

したがって入力したテキストの表示に何らかの変更を加えたい場合は、TextEditingControllerクラスを継承したカスタムクラスを作成し、buildTextSpanメソッドをオーバライドすれば良いことがわかります。

ここではCustomTextEditingControllerクラスを以下のように作成します。

また与えたテキストからURL部分を判別し、リンクを設定するAutoLinkTextクラスを既に別途定義ずみと仮定します(詳細は前回記事参照)。

class CustomTextEdittingController extends TextEditingController {
  CustomTextEdittingController({String? text}) : super(text: text);

  @override
TextSpan buildTextSpan(
  {required BuildContext context,
    TextStyle? style,
    required bool withComposing}) {
  assert(!value.composing.isValid ||
      !withComposing ||
      value.isComposingRangeValid);

  return AutoLinkText.generate( // URL部分のみ別のスタイルを適用したTextSpanを返却するクラス
    this.text, //入力したテキスト
    hasLink: false, //リンク機能の有無のフラグ
  ).generate();
  }
}

このCustomTextEdittingControllerを使用して、次のようにTextFormFieldを使用すると、入力中のテキストの正規表現によって判別されるURL部分のテキストスタイルを任意のもので表示することが可能です。
例えばURL部分のみ青文字で表示するといった仕様が想定されます。

ただしこのままでは、変換等が未確定の文字(buildTextSpan中のvalue.composing.textInsideで取得できる範囲の文字列)につく下線が表示されないので、未確定文字に対して下線をつけてTextSpanを返却するよう修正します。

未確定文字に対して下線をつけたくない場合はここまでのコーディングで問題ありません。

前述した通り、未確定文字はbuildTextSpan中のvalue.composing.textInsideから取得することができます。

ここで改めてTextEditingControllerbuildTextSpanのコードを参照すると、

return TextSpan(
  style: style,
  children: <TextSpan>[
    TextSpan(text: value.composing.textBefore(value.text)),
    TextSpan(
      style: composingStyle,
      text: value.composing.textInside(value.text),
    ),
    TextSpan(text: value.composing.textAfter(value.text)),
  ],
);

このようにtextInsideで取得される入力が未確定な文字列の前後に分かれて、それらの文字列がtextBeforeおよびtextAfterとして取得されて、それぞれの部分ごとにTextSpanが生成されていることがわかります。

生成されたTextSpanを最終的に一つのTextSpanとしてUIに表示する仕組みです。

したがって入力が未確定の文字列に対して下線をつけるためには次のようにCustomTextEdittingControllerに変更を加えます。

class CustomTextEdittingController extends TextEditingController {
  CustomTextEdittingController({String? text}) : super(text: text);

  @override
  TextSpan buildTextSpan(
    {required BuildContext context,
    TextStyle? style,
    required bool withComposing}) {
  assert(!value.composing.isValid ||
      !withComposing ||
      value.isComposingRangeValid);

    //入力確定済みのテキストに対しての処理
    if (!value.isComposingRangeValid || !withComposing) {
      return AutoLinkTextSpan.generate(
        this.text,
        hasLink: false,
        hasUnderline: false,
      ).generate();
    }

    //入力中のテキストに対する処理
    return TextSpan(
      children: <TextSpan>[
        TextSpan(
          children: [
            AutoLinkTextSpan.generate(
              value.composing.textBefore(value.text),
              hasLink: false,
              hasUnderline: false,
            ).generate()
          ],
        ),
        TextSpan(
          children: [
            AutoLinkTextSpan.generate(
              value.composing.textInside(value.text),
              hasLink: false,
              hasUnderline: true, //下線を加えるフラグ(AutoLinkTextSpan内でTextStyle変更)
            ).generate()
          ],
        ),
        TextSpan(
          children: [
            AutoLinkTextSpan.generate(
              value.composing.textAfter(value.text),
              hasLink: false,
              hasUnderline: false,
            ).generate()
          ],
        ),
      ],
    );
  }
}

以上で実装したCustomTextEditingControllerTextFormFieldのコントローラとして指定すると、本記事の目標である「入力中のテキスト内のURL部分のテキストスタイルを動的に変更する」が達成できます。

おわりに

入力中のテキスト内のURL部分のテキストスタイルを動的に変更する方法を紹介しました。
URL部分の判別および自動でテキストスタイルや処理をセットする方法については前回記事に詳しく解説しております。
あわせてご覧ください。

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?