Flutter文本提及插件simply_mentions的使用

发布于 1周前 作者 bupafengyu 来自 Flutter

Flutter文本提及插件simply_mentions的使用

简介

Simply Mentions 是一个功能丰富的提及包,支持将文本导出为计算机可读语法、支持相同显示名称的不同项目提及,并内置Markdown支持。该插件非常适合在应用中实现对用户或频道等项目的提及功能。

特性

  • 完全自定义提及列表的视觉效果
  • 内置Markdown支持
  • 导出提及到计算机可读语法
  • 初始化包含语法文本的TextField
  • 支持重复名称(可以提及不同ID但相同名字的人)
  • 无限提及类型支持(用户、频道等)
  • 可定制提及负载(为提及列表添加自定义数据)

快速入门指南

文本字段快速入门

首先,需要创建一个MentionTextEditingController实例,并将其传递给任何你选择的文本字段。

import 'package:flutter/material.dart';
import 'package:simply_mentions/simply_mentions.dart';

// 创建提及对象列表
final List<MentionObject> documentMentions = [
    MentionObject(
        id: "ExampleId1",
        displayName: "Jane Doe",
        avatarUrl: "https://placekitten.com/50/50"),
    MentionObject(
        id: "ExampleId2",
        displayName: "Jane Doe",
        avatarUrl: "https://placekitten.com/60/60"),
    MentionObject(
        id: "ExampleId3",
        displayName: "John Doe",
        avatarUrl: "https://placekitten.com/70/70")
];

// 创建控制器
MentionTextEditingController mentionTextEditingController = MentionTextEditingController(
    mentionSyntaxes: [DocumentMentionEditableSyntax(context)],
    mentionBgColor: Theme.of(context).colorScheme.primary,
    mentionTextColor: Theme.of(context).colorScheme.onPrimary,
    runTextStyle: TextStyle(),
    mentionTextStyle: TextStyle(),
    onSugggestionChanged: onSuggestionChanged,
    idToMentionObject: (BuildContext context, String id) =>
        documentMentions.firstWhere((element) => element.id == id));

// 当前提及状态变化时调用
void onSuggestionChanged(MentionSyntax? syntax, String? fullSearchString) {
    setState(() {});
}

// 使用Portal和TextField结合展示提及列表
PortalTarget(
    visible: mentionTextEditingController.isMentioning(),
    portalFollower: getMentions(),
    anchor: Aligned(
        follower: Alignment.bottomLeft,
        target: Alignment.topLeft,
        widthFactor: 1,
    ),
    child: TextField(
        decoration: InputDecoration(border: OutlineInputBorder()),
        maxLines: null,
        minLines: 5,
        controller: mentionTextEditingController,
    )
)

Markdown快速入门

一旦你的文本字段支持提及功能,并希望在非TextField的文本框中显示这些提及内容,你可以按照以下步骤操作:

import 'package:flutter_markdown/flutter_markdown.dart';

class DocumentMention extends MarkdownMentionSyntax {
  DocumentMention()
      : super(
            patternToUse: mentionPattern,
            tagName: 'docMention',
            idRegexGroup: 2,
            startCharacter: 0x3C);

  static const String mentionPattern = '<###@([a-zA-Z0-9-]{1,})###>';
}

// 创建MarkdownBody来显示提及内容
MarkdownBody(
    data: mentionTextEditingController.getMarkupText(),
    softLineBreak: true,
    builders: {
        'docMention': DocumentMentionBuilder(context: context,
            onPressed: (String pressedMentiondId) {
                // 处理点击事件
            })
    },
    inlineSyntaxes: [DocumentMention()],
)

示例Demo

以下是完整的示例代码,包括如何初始化控制器、设置文本字段以及如何显示提及列表和Markdown内容。

import 'package:flutter/material.dart';
import 'package:flutter_portal/flutter_portal.dart';
import 'package:simply_mentions/simply_mentions.dart';
import 'package:flutter_markdown/flutter_markdown.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Simply Mentions Example'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  [@override](/user/override)
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late MentionTextEditingController mentionTextEditingController;
  FocusNode focusNode = FocusNode();

  [@override](/user/override)
  void initState() {
    super.initState();
    setupController();
  }

  void setupController() {
    mentionTextEditingController = MentionTextEditingController(
      mentionSyntaxes: [DocumentMentionEditableSyntax(context)],
      mentionBgColor: Theme.of(context).primaryColor,
      mentionTextColor: Colors.white,
      onSugggestionChanged: onSuggestionChanged,
      idToMentionObject: (BuildContext context, String id) =>
          documentMentions.firstWhere((element) => element.id == id),
    );
  }

  void onSuggestionChanged(MentionSyntax? syntax, String? fullSearchString) {
    setState(() {});
  }

  Widget getMentions() {
    if (!mentionTextEditingController.isMentioning()) {
      return SizedBox.shrink();
    }

    List<Widget> possibleMentions = [];

    String safeSearch = removeDiacritics(mentionTextEditingController.getSearchText());

    documentMentions.forEach((element) {
      String safeName = removeDiacritics(element.displayName.toLowerCase());

      if (safeName.contains(safeSearch)) {
        possibleMentions.add(Padding(
            padding: EdgeInsets.only(bottom: 5),
            child: Ink(
              child: InkWell(
                onTap: () {
                  onMentionSelected(element);
                },
                splashColor: Theme.of(context).highlightColor,
                child: Row(children: [
                  Image.network(
                    element.avatarUrl,
                    width: 25,
                    height: 25,
                  ),
                  SizedBox(width: 6),
                  Text(element.displayName)
                ]),
              ),
            )));
      }
    });

    ScrollController controller = ScrollController();

    return Material(
        child: ConstrainedBox(
            constraints: BoxConstraints(maxHeight: 400),
            child: Container(
                decoration: BoxDecoration(
                    color: Color.fromARGB(255, 99, 94, 94),
                    borderRadius: BorderRadius.circular(10)),
                child: Scrollbar(
                    controller: controller,
                    child: ListView.separated(
                      controller: controller,
                      shrinkWrap: true,
                      padding: EdgeInsets.all(8.0),
                      itemCount: possibleMentions.length,
                      separatorBuilder: (context, i) => Divider(),
                      itemBuilder: (BuildContext context, int index) {
                        return possibleMentions[index];
                      },
                    )))));
  }

  void onMentionSelected(MentionObject mention) {
    setState(() {
      mentionTextEditingController.insertMention(mention);
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Portal(
      child: Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Center(
          child: ConstrainedBox(
            constraints: BoxConstraints(maxWidth: 300),
            child: Column(mainAxisSize: MainAxisSize.min, children: [
              Text("Enter your text"),
              SizedBox(height: 6),
              PortalTarget(
                visible: mentionTextEditingController.isMentioning(),
                portalFollower: getMentions(),
                anchor: Aligned(
                  follower: Alignment.bottomLeft,
                  target: Alignment.topLeft,
                  widthFactor: 1,
                ),
                child: TextField(
                  decoration: InputDecoration(border: OutlineInputBorder()),
                  focusNode: focusNode,
                  maxLines: null,
                  minLines: 5,
                  controller: mentionTextEditingController,
                ),
              ),
              SizedBox(height: 20),
              Text("Resulting markdown"),
              SizedBox(height: 20),
              MarkdownBody(
                data: mentionTextEditingController.getMarkupText(),
                softLineBreak: true,
                builders: {
                  'docMention': DocumentMentionBuilder(
                    context: context,
                    onPressed: (String pressedMentiondId) {},
                  ),
                },
                inlineSyntaxes: [DocumentMention()],
              ),
            ]),
          ),
        ),
      ),
    );
  }
}

更多关于Flutter文本提及插件simply_mentions的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter文本提及插件simply_mentions的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter中使用simply_mentions插件来实现文本提及功能的代码案例。这个插件允许你在文本输入字段中添加提及功能,通常用于社交应用或评论框中。

首先,你需要在你的pubspec.yaml文件中添加simply_mentions依赖:

dependencies:
  flutter:
    sdk: flutter
  simply_mentions: ^x.y.z  # 请替换为最新版本号

然后运行flutter pub get来安装依赖。

接下来,在你的Flutter应用中实现提及功能。以下是一个完整的示例代码:

import 'package:flutter/material.dart';
import 'package:simply_mentions/simply_mentions.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Mention Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MentionScreen(),
    );
  }
}

class MentionScreen extends StatefulWidget {
  @override
  _MentionScreenState createState() => _MentionScreenState();
}

class _MentionScreenState extends State<MentionScreen> {
  final List<String> users = ['Alice', 'Bob', 'Charlie', 'David'];
  final TextEditingController _controller = TextEditingController();
  List<Mention> _mentions = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Mention Example'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: MentionsTextField(
          controller: _controller,
          suggestions: users,
          onChanged: (List<Mention> mentions) {
            setState(() {
              _mentions = mentions;
            });
          },
          decoration: InputDecoration(
            border: OutlineInputBorder(),
            labelText: 'Type a message...',
          ),
          mentionDecoration: BoxDecoration(
            color: Colors.blue.withOpacity(0.1),
            borderRadius: BorderRadius.circular(8),
          ),
          mentionStyle: TextStyle(color: Colors.blue),
          suggestionBuilder: (context, suggestions) {
            return suggestions.isNotEmpty
                ? ListView.builder(
                    itemCount: suggestions.length,
                    itemBuilder: (context, index) {
                      return ListTile(
                        leading: CircleAvatar(
                          child: Text(suggestions[index][0]),
                        ),
                        title: Text(suggestions[index]),
                        onTap: () {
                          _controller.insertTextAtSelection(
                            '@' + suggestions[index] + ' ',
                          );
                        },
                      );
                    },
                  )
                : Container();
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 处理提交逻辑,例如打印提及的用户
          print('Mentions: $_mentions');
          print('Text: ${_controller.text}');
        },
        tooltip: 'Submit',
        child: Icon(Icons.send),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

代码解释

  1. 依赖导入:在pubspec.yaml文件中添加simply_mentions依赖。

  2. 主函数MyApp是应用的主入口,设置了基本的Material主题。

  3. 主屏幕MentionScreen是一个有状态的组件,用于处理文本输入和提及逻辑。

  4. 用户列表:定义一个users列表,包含可以被提及的用户名。

  5. 控制器和提及列表:使用TextEditingController来管理文本输入,_mentions列表用于存储提及的用户。

  6. 构建UI

    • 使用MentionsTextField小部件来创建文本输入字段。
    • suggestions属性设置为users列表。
    • onChanged回调用于更新提及列表。
    • mentionDecorationmentionStyle用于自定义提及项的样式。
    • suggestionBuilder用于构建建议列表的UI。
  7. 提交按钮FloatingActionButton用于提交输入内容,并打印提及的用户和完整文本。

  8. 资源释放:在dispose方法中释放TextEditingController资源。

这样,你就可以在Flutter应用中实现文本提及功能了。希望这个示例对你有帮助!

回到顶部