You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter的TextField组件如何实现多样式文本输入功能?

Flutter TextField实现多样式文本输入方案

完全可以实现选中文本自定义样式的需求,核心依赖TextField底层的富文本渲染能力,无需完全自定义输入控件。

实现核心思路

  • 自定义TextEditingController,重写buildTextSpan方法,这是局部样式生效的核心原生能力:默认控制器只会套用全局style,重写后可以根据文本区间分配不同的TextStyle。
  • 自定义数据结构存储文本区间和对应样式的映射关系,比如定义StyleRange类,记录start、end、style三个属性,所有设置过样式的文本段都存在列表中统一管理。
  • 选中改样式逻辑:通过TextField的onSelectionChanged回调可以实时拿到用户当前选中的文本范围,用户触发样式修改操作(比如点击粗体按钮、选择字体颜色)时,把对应范围的样式写入样式映射列表,再调用控制器的notifyListeners刷新输入框渲染即可。

简化实现代码示例

// 自定义样式区间模型
class StyleRange {
  final int start;
  final int end;
  final TextStyle style;
  StyleRange({required this.start, required this.end, required this.style});
}

// 自定义富文本编辑控制器
class RichTextEditingController extends TextEditingController {
  List<StyleRange> styleRanges = [];

  // 给选中范围设置样式
  void setStyleForSelection(TextSelection selection, TextStyle style) {
    if (selection.start == selection.end) return; // 没有选中文本直接返回
    styleRanges.add(StyleRange(
      start: selection.start,
      end: selection.end,
      style: style,
    ));
    notifyListeners();
  }

  @override
  TextSpan buildTextSpan({required BuildContext context, TextStyle? style, required bool withComposing}) {
    List<TextSpan> children = [];
    int textLength = text.length;
    int currentPos = 0;

    // 按样式区间拼接TextSpan
    for (var range in styleRanges) {
      if (range.start > currentPos) {
        // 无自定义样式的普通文本
        children.add(TextSpan(
          text: text.substring(currentPos, range.start),
          style: style,
        ));
      }
      // 带自定义样式的文本
      children.add(TextSpan(
        text: text.substring(range.start, range.end),
        style: style?.merge(range.style) ?? range.style,
      ));
      currentPos = range.end;
    }

    // 拼接剩余无样式文本
    if (currentPos < textLength) {
      children.add(TextSpan(
        text: text.substring(currentPos),
        style: style,
      ));
    }

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

// 页面中使用示例
class RichTextFieldPage extends StatefulWidget {
  const RichTextFieldPage({super.key});

  @override
  State<RichTextFieldPage> createState() => _RichTextFieldPageState();
}

class _RichTextFieldPageState extends State<RichTextFieldPage> {
  final RichTextEditingController _controller = RichTextEditingController();
  TextSelection _currentSelection = const TextSelection.collapsed(offset: 0);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 样式操作栏
        Row(
          children: [
            TextButton(
              onPressed: () {
                // 给选中文字设置粗体
                _controller.setStyleForSelection(_currentSelection, const TextStyle(fontWeight: FontWeight.bold));
              },
              child: const Text("加粗"),
            ),
            TextButton(
              onPressed: () {
                // 给选中文字设置红色
                _controller.setStyleForSelection(_currentSelection, const TextStyle(color: Colors.red));
              },
              child: const Text("变红"),
            )
          ],
        ),
        TextField(
          controller: _controller,
          maxLines: null,
          onSelectionChanged: (selection) {
            _currentSelection = selection;
          },
        )
      ],
    );
  }
}

注意事项

  • 上述是简化实现版本,实际使用时需要处理文本插入、删除时样式区间的位置同步问题,比如用户在已设置样式的文本中间插入内容时,要对应调整后续样式区间的start、end偏移量。
  • 可根据业务需要扩展StyleRange的属性,支持链接、列表等更复杂的富文本样式,也可自行处理样式重叠的优先级逻辑。

内容的提问来源于stack exchange,提问作者Ammar Mohamed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 00:57:03