🐶

【Flutter】ユーザーメンション機能をFlutterTaggerで手軽に実装💡

2025/03/03に公開

はじめに

SNS やチャットアプリでは、@ を使ってユーザーをメンションする機能がよく使われます。
メンション機能のパッケージはいくつかありますが、使用数とメンテナンス頻度から今回はFlutterTaggerを選択しました。
本記事では、FlutterTagger を利用してユーザーメンション機能を実装する方法を解説します。

1. FlutterTagger とは?

FlutterTagger は、@# などのトリガー文字を検出し、それに応じた検索やテキストのスタイリングを適用できるウィジェットです。

1-1. FlutterTagger の主な機能

  • @# で検索をトリガー
  • トリガー文字のスタイル適用
  • 検索結果をオーバーレイ表示
  • フォーマット済みのタグ付きテキストを取得可能
  • onSearch で検索時の処理を実装可能
  • overlay で検索結果をカスタマイズ可能

2. パッケージのインストール

まず、fluttertagger をプロジェクトに追加します。

flutter pub add fluttertagger

3. 全体の流れ

3-1. 実装の流れ

  1. テキストの入力
  2. メンション可能ユーザーの表示
  3. メンション先の選択
  4. テキストの送信

3-2. イメージ

gifから確認できること

  • テキスト入力時に@に続くユーザー名をリストから検索できる
  • リストのユーザー名をタップすることでテキストフィールドに自動で入力される
  • 投稿された文字のうち、メンションに関わるものはテキストスタイルが変わっている

3-3. ソースコード

今回のコードは以下のレポジトリに格納しています
https://github.com/gentarokai/mentionable_text_field/tree/main

3-4. 必要な関数・Widget

  1. 関数
    • onSearch : メンション可能なユーザーの検索
    • onUserSelected : メンションユーザーの選択
    • onSubmit : textFieldを送信(投稿の追加など)
  2. Widget
    • SearchResultView :
      • メンション可能なユーザーの一覧を表示するためのView
    • TextField :
      • 検索に使用するTextField

4. 本実装

4-1. 実装コード(全体)

main.dart
class MentionScreen extends StatefulWidget {
  const MentionScreen({super.key});

  
  State<MentionScreen> createState() => _MentionScreenState();
}

class _MentionScreenState extends State<MentionScreen> {
  final _controller = FlutterTaggerController();
  final FocusNode _focusNode = FocusNode();
  // メンション可能なユーザーのリスト
  final List<User> users = [
    User(id: '1', userName: 'Alice'),
    User(id: '2', userName: 'Bob'),
    User(id: '3', userName: 'Charlie'),
    User(id: '4', userName: 'David'),
  ];
  List<User> searchResults = [];
  List<Post> posts = [];

  // 検索関数
  void _onSearch(String query, String triggerCharacter) {
   // 後ほど解説
  }

  // メンションする人を選んだ際の関数
  void _onUserSelected(User user) {
   // 後ほど解説
  }

  // テキストフィールドを送信した際の関数
  void _onSubmit(String caption) {
    // 後ほど解説
  }

  
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        FocusScope.of(context).unfocus();
      },
      child: Scaffold(
        appBar: AppBar(title: const Text('User Mention Demo')),
        body: Padding(
          padding: const EdgeInsets.all(30.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              // 投稿リスト
              PostListView(
                posts: posts,
              ),
              // Flutter Taggerライブラリ
              FlutterTagger(
                // 後ほど解説
                overlayHeight: 250,
                controller: _controller,
                onSearch: _onSearch,
                triggerCharacterAndStyles: const {
                  '@': TextStyle(color: Colors.blueAccent),
                },
                overlay: SearchResultView(
                  searchResults: searchResults,
                  onUserSelected: _onUserSelected,
                ),
                builder: (context, textFieldKey) {
                  return TextField(
                      key: textFieldKey,
                      controller: _controller,
                      focusNode: _focusNode,
                      decoration: const InputDecoration(
                        labelText: 'コメントを入力',
                        border: OutlineInputBorder(),
                      ),
                      // onEditingComplete: () => _onSubmit,
                      onSubmitted: (value) {
                        _onSubmit(value);
                        _controller.clear();
                      });
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

4-2. FlutterTaggerに渡す関数

onSearch メンション可能なユーザーの検索

triggerCharacterに特定の文字列(@)が渡された際に、queryに一致するuserを配列に追加します。

main.dart
 // 検索関数
  void _onSearch(String query, String triggerCharacter) {
   if (triggerCharacter == '@') {
      setState(() {
        searchResults = users
            .where((user) =>
                user.userName.toLowerCase().contains(query.toLowerCase()))
            .toList();
      });
    }
  }

onUserSelected メンションするユーザーの選択処理

userを引数に取り、FlutterTaggerのcontrollerに登録します。
検索可能なユーザー一覧(searchResults)をクリアーします。

main.dart
  // メンションする人を選んだ際の関数
  void _onUserSelected(User user) {
   _controller.addTag(
      id: user.id,
      name: user.userName,
    );
    setState(() {
      searchResults.clear();
    });
  }

onSubmit テキストフィールド送信時の関数

入力文字列を引数にとり、投稿オブジェクトを登録します。

main.dart
  // テキストフィールドを送信した際の関数
  void _onSubmit(String caption) {
    if (caption.isEmpty) return;

    final post = Post(
      caption: caption,
      poster: User(id: '111', userName: 'John Smith'),
      time: DateTime.now().toString(),
    );

    posts.add(post); // Firestoreなどの登録はココ!
    setState(() {});
  }

4-3. FlutterTaggerの実装

今回するライブラリのFlutterTaggerウィジットに必要な関数とWidgetを渡します。
細かいWidgetやEntityの定義についてはGithubのレポジトリをご覧ください。

main.dart
FlutterTagger(
    overlayHeight: 250,
    controller: _controller, // FlutterTaggerController()
    onSearch: _onSearch, // メンション先の検索
    triggerCharacterAndStyles: const {
        '@': TextStyle(color: Colors.blueAccent), // メンション先のスタイル
    },
    overlay: SearchResultView( // メンション可能ユーザーの一覧
    searchResults: searchResults,
    onUserSelected: _onUserSelected,
    ),
    builder: (context, textFieldKey) {
        return TextField(
            key: textFieldKey,
            controller: _controller,
            focusNode: _focusNode,
            decoration: const InputDecoration(
            labelText: 'コメントを入力',
                border: OutlineInputBorder(),
            ),
            onSubmitted: (value) {
                _onSubmit(value);
                _controller.clear();
            }
        );
    },
),

5. まとめ

本記事では、FlutterTagger を使ってユーザーメンション機能を簡単に実装する方法を紹介しました。

FlutterTagger のメリット

@# を簡単にハイライト可能
✅ 検索結果をオーバーレイ表示できる
✅ 送信時にフォーマット済みのテキストを取得可能
overlay で自由にカスタマイズ可能
onSearch で検索処理を実装可能

API 連携を追加すれば、実際のユーザーデータを使った検索も可能です。ぜひ、自分のアプリに導入してみてください!

参考になると嬉しいです!最後まで読んでいただき、ありがとうございます。

Discussion