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

Flutter中如何修改文本框选中文字颜色并保留历史样式

Flutter实现选中文本改色并保留样式的步骤

要实现选中部分文本修改颜色且保留历史样式的功能,核心是用富文本片段管理替代单一文本字符串,以下是具体实现步骤:

1. 定义富文本片段数据结构

创建一个类来存储每一段文本及其对应的样式,方便后续拆分、合并片段:

class StyledTextSegment {
  final String text;
  final TextStyle? style;

  StyledTextSegment(this.text, {this.style});
}

2. 维护富文本与选中状态

在页面的State类中,声明核心状态变量:

  • List<StyledTextSegment> _textSegments:存储所有带样式的文本片段
  • TextSelection _currentSelection:实时记录用户选中的文本范围
List<StyledTextSegment> _textSegments = [StyledTextSegment('请输入或选中文本')];
TextSelection _currentSelection = TextSelection.collapsed(offset: 0);

3. 监听文本选中事件

使用EditableText组件(原生支持富文本编辑的底层组件),通过onSelectionChanged回调实时更新选中状态:

EditableText(
  // 其他配置项...
  onSelectionChanged: (selection, cause) {
    setState(() {
      _currentSelection = selection;
    });
  },
)

4. 实现选中文本改色逻辑

点击改色按钮时,根据选中范围拆分、修改对应片段的样式,核心逻辑是:

  • 定位选中范围涉及的所有文本片段
  • 对每个交集片段,拆分为「选中前(原样式)、选中部分(新颜色)、选中后(原样式)」三个部分(如果需要)
  • 更新片段列表并刷新UI

示例代码:

void _applyColorToSelection(Color targetColor) {
  if (_currentSelection.isCollapsed) return; // 无选中内容直接返回

  final int start = _currentSelection.start;
  final int end = _currentSelection.end;

  // 计算每个文本片段对应的全局索引范围
  List<({StyledTextSegment segment, int startIdx, int endIdx})> segmentRanges = [];
  int currentOffset = 0;
  for (final seg in _textSegments) {
    final segEnd = currentOffset + seg.text.length;
    segmentRanges.add((
      segment: seg,
      startIdx: currentOffset,
      endIdx: segEnd,
    ));
    currentOffset = segEnd;
  }

  final List<StyledTextSegment> newSegments = [];
  for (final range in segmentRanges) {
    if (range.endIdx <= start) {
      // 片段在选中范围前,直接保留
      newSegments.add(range.segment);
    } else if (range.startIdx >= end) {
      // 片段在选中范围后,直接保留
      newSegments.add(range.segment);
    } else {
      // 处理与选中范围重叠的片段
      final segSelStart = max(range.startIdx, start);
      final segSelEnd = min(range.endIdx, end);

      // 添加选中前的部分(如果存在)
      if (segSelStart > range.startIdx) {
        final preText = range.segment.text.substring(0, segSelStart - range.startIdx);
        newSegments.add(StyledTextSegment(preText, style: range.segment.style));
      }

      // 添加选中的部分(应用新颜色)
      final selectedText = range.segment.text.substring(
        segSelStart - range.startIdx,
        segSelEnd - range.startIdx,
      );
      newSegments.add(StyledTextSegment(selectedText, style: TextStyle(color: targetColor)));

      // 添加选中后的部分(如果存在)
      if (segSelEnd < range.endIdx) {
        final postText = range.segment.text.substring(segSelEnd - range.startIdx);
        newSegments.add(StyledTextSegment(postText, style: range.segment.style));
      }
    }
  }

  setState(() {
    _textSegments = newSegments;
    _currentSelection = TextSelection.collapsed(offset: end); // 重置选中状态
  });
}

5. 构建可编辑的富文本UI

将_textSegments转换为TextSpan,传给EditableText的textSpan参数,同时同步文本内容和选中状态:

@override
Widget build(BuildContext context) {
  // 转换片段为TextSpan
  TextSpan _buildTextSpan() {
    return TextSpan(
      style: const TextStyle(color: Colors.black, fontSize: 16), // 默认样式
      children: _textSegments.map((seg) => TextSpan(text: seg.text, style: seg.style)).toList(),
    );
  }

  return Scaffold(
    appBar: AppBar(title: const Text('富文本颜色编辑')),
    body: Column(
      children: [
        Expanded(
          child: EditableText(
            controller: TextEditingController(
              text: _textSegments.map((seg) => seg.text).join(),
            )..selection = _currentSelection,
            focusNode: FocusNode(),
            style: const TextStyle(color: Colors.black, fontSize: 16),
            cursorColor: Colors.blue,
            backgroundCursorColor: Colors.grey,
            onSelectionChanged: (selection, cause) => setState(() => _currentSelection = selection),
            textSpan: _buildTextSpan(),
            onChanged: (newText) {
              // 简化处理文本输入:将新文本转为默认样式的单片段
              setState(() => _textSegments = [StyledTextSegment(newText)]);
            },
          ),
        ),
        ElevatedButton(
          onPressed: () => _applyColorToSelection(Colors.red),
          child: const Text('选中文字变红'),
        ),
      ],
    ),
  );
}

6. 优化与扩展

  • 可以添加更多样式按钮(如加粗、斜体),只需修改_applyColorToSelection逻辑,替换样式参数即可
  • 文本输入的处理可以更精细(比如在光标位置插入新文本时保留前后样式),避免直接重置为单片段
  • 支持撤销/重做功能,通过维护_textSegments的历史版本实现

内容的提问来源于stack exchange,提问作者Mohammad Hosseini

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 03:09:59