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

如何为多行TextField每行添加匹配尺寸的Container背景?

实现多行TextField每行匹配尺寸的背景Container

核心思路

要实现每行背景与TextField行尺寸完全匹配,关键是获取TextField文本的每行布局信息——借助TextPainter模拟TextField的文本排版,计算出每行的位置、宽高,再结合Stack将背景Container与TextField叠加,背景层在下方,TextField设为透明以便透出背景。

具体实现代码

import 'package:flutter/material.dart';

class TextFieldLineBackground extends StatefulWidget {
  const TextFieldLineBackground({super.key});

  @override
  State<TextFieldLineBackground> createState() => _TextFieldLineBackgroundState();
}

class _TextFieldLineBackgroundState extends State<TextFieldLineBackground> {
  late TextEditingController _controller;
  final _textStyle = const TextStyle(fontSize: 16, color: Colors.black);
  double _textFieldWidth = 0;
  List<LineInfo> _lineInfos = [];

  @override
  void initState() {
    super.initState();
    _controller = TextEditingController()..addListener(_calculateLineInfos);
  }

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

  // 计算每行的位置和文本内容
  void _calculateLineInfos() {
    final text = _controller.text;
    if (text.isEmpty || _textFieldWidth == 0) {
      setState(() => _lineInfos = []);
      return;
    }

    final textPainter = TextPainter(
      text: TextSpan(text: text, style: _textStyle),
      textDirection: TextDirection.ltr,
      maxLines: null,
    )..layout(maxWidth: _textFieldWidth);

    final lineMetrics = textPainter.computeLineMetrics();
    final lineInfos = <LineInfo>[];

    for (var i = 0; i < lineMetrics.length; i++) {
      final line = lineMetrics[i];
      // 获取该行文本的起始和结束偏移量
      final startOffset = textPainter.getPositionForOffset(Offset(line.left, line.baseline)).offset;
      final endOffset = i == lineMetrics.length - 1
          ? text.length
          : textPainter.getPositionForOffset(Offset(lineMetrics[i+1].left, lineMetrics[i+1].baseline)).offset;
      final lineText = text.substring(startOffset, endOffset);

      lineInfos.add(LineInfo(
        rect: Rect.fromLTWH(
          line.left,
          line.baseline - line.ascent,
          line.width,
          line.height,
        ),
        text: lineText,
      ));
    }

    setState(() => _lineInfos = lineInfos);
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        // 监听TextField的可用宽度变化,重新计算行布局
        if (_textFieldWidth != constraints.maxWidth) {
          _textFieldWidth = constraints.maxWidth - 16; // 减去TextField的内边距
          _calculateLineInfos();
        }

        return Stack(
          children: [
            // 每行的背景Container
            ..._lineInfos.map((info) {
              // 根据每行文本动态设置背景样式
              Color bgColor = Colors.yellow.withOpacity(0.3);
              if (info.text.contains('重要')) {
                bgColor = Colors.red.withOpacity(0.3);
              } else if (info.text.startsWith('-')) {
                bgColor = Colors.green.withOpacity(0.3);
              }

              return Positioned.fromRect(
                rect: info.rect,
                child: Container(
                  decoration: BoxDecoration(
                    color: bgColor,
                    border: Border.all(color: Colors.grey.shade300, width: 0.5),
                  ),
                ),
              );
            }),
            // 上层透明背景的TextField
            TextField(
              controller: _controller,
              maxLines: null,
              style: _textStyle,
              decoration: const InputDecoration(
                border: OutlineInputBorder(),
                contentPadding: EdgeInsets.all(8),
                fillColor: Colors.transparent,
                filled: true,
              ),
            ),
          ],
        );
      },
    );
  }
}

// 存储每行的位置和文本信息
class LineInfo {
  final Rect rect;
  final String text;
  LineInfo({required this.rect, required this.text});
}

关键说明

  1. TextPainter的作用:完全复用TextField的文本样式和布局宽度,精准计算每行的Rect(位置、宽高),确保背景Container与文本行完美对齐。
  2. 动态样式设置:通过LineInfo存储每行文本,可根据文本内容(如包含特定关键词、前缀)自定义背景颜色、边框等样式。
  3. 适配布局变化:用LayoutBuilder监听TextField的宽度变化,确保窗口缩放或父容器尺寸改变时,背景行能实时更新位置和尺寸。

内容的提问来源于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.07 14:43:29