🕌

FlutterのTextFieldにメンション機能を実装する

2024/09/07に公開

こんにちは 😁
今回は Flutter の TextField にメンション機能を実装してみました。いくつか気になることはありますがとりあえず公開しようと思います 🎉

こんな感じ
flutter_mention_text_field

  • @{userId}のように入力すると、対応するユーザー名に自動変換される
  • メンションの前でデリートキーを入力すると、メンション全体が削除される
  • +ボタンを押すとユーザーを追加
  • 値の実態は@{userId}

リポジトリはこちら
https://github.com/sh0o0/flutter-mention-text-field

何が嬉しいか

  • ユーザー名が変更されても変更後のユーザー名を表示できる
  • メンションごとに TextStyle を指定できる

どんな時に使うか

ユーザーがユーザー ID を入力することはないと思うので、リプライボタンを押した時に自動でユーザー ID を入力してあげたり、@を入力した時にユーザー名一覧を表示し選択するとユーザー ID が入力されるように実装することになると思います。

実装

実装するのはTextEditingControllerを継承したクラスのみ。あとはそれをTextFieldなり使うだけ。

TextEditingControllerを継承

class MentionTextEditingController extends TextEditingController {
  
  TextSpan buildTextSpan({
    BuildContext? context,
    TextStyle? style,
    bool? withComposing,
  }) {
    ...
  }
}

buildTextSpanを override し、ユーザー ID をユーザー名に変更

  
  TextSpan buildTextSpan({
    BuildContext? context,
    TextStyle? style,
    bool? withComposing,
  }) {
    final children = <InlineSpan>[];

    text.splitMapJoin(
      RegExp(r'@([a-zA-Z0-9_]+)(\u200b*)');, // NOTE: \u200b*については後ほど説明します。
      onMatch: (Match match) {
        ...
        children.add(TextSpan(text: fixedText, style: style));
        return '';
      },
      onNonMatch: (String text) {
        children.add(TextSpan(text: text, style: style));
        return '';
      },
    );

    return TextSpan(style: style, children: children);
  }

ユーザー ID とユーザー名の文字列数は違うのでカーソル位置にバグが発生します。それを修正するために以下をします。

  • ユーザ ID.length > ユーザー名.length: 実値より表示テキストの方が短いので、表示テキストにWidgetSpan(child: SizedBox())(ゼロ幅 Widget)を足りない分だけ足します。
  • ユーザー ID.length < ユーザー名.length: 実値より表示されるテキストの方が長いので、実値に\u200b(ゼロ幅スペース)を足りない分だけ足します。

https://github.com/sh0o0/flutter-mention-text-field/blob/c02c1d20b5d41d000a80829b7242ac7308582744/lib/mention_text_editing_controller.dart#L49-L131

以下ちょっとトリッキーなことしていますが、説明が難しいので省略させてください 🙏
https://github.com/sh0o0/flutter-mention-text-field/blob/c02c1d20b5d41d000a80829b7242ac7308582744/lib/mention_text_editing_controller.dart#L99-L101

コード全体

https://github.com/sh0o0/flutter-mention-text-field/blob/c02c1d20b5d41d000a80829b7242ac7308582744/lib/mention_text_editing_controller.dart#L1-L152

気になること

  • トリッキーな実装のせいでメンションの後に 1 つ見えない文字ができてしまい、左にカーソルを移動すると一度目は動かない
  • メンションの途中でデリートキーを押すと、削除のされ方にむらが出る
    • 全部削除されたり、途中から前半だけ削除されたり、その逆だったり
  • 外部から text プロパティを参照したときに、ゼロ幅スペースをトリムしたい

いつか調査して直したいなと思ますが、今はやる気が出ないので置いておきます。

最後に

もっと簡単な方法があれば教えていただきたいです!

Discussion