Flutter TextField如何实现文本带圆角的彩色背景效果
要实现自定义绘制的带padding、圆角、无行间隙的TextField文本背景效果,可通过自定义绘制层+实时计算文本行边界的方案完成,具体实现如下:
核心逻辑
- 通过
TextPainter布局输入文本,计算每一行文本的精确坐标和尺寸 - 对每行文本的边界做padding拓展,根据相邻行的位置关系自动合并圆角,消除行间隙
- 把自定义背景绘制层放在TextField下层,文本变化时实时重绘背景即可
完整实现代码
自定义背景绘制类
class HighlightBackgroundPainter extends CustomPainter { final String text; final TextStyle textStyle; final double paddingHorizontal; final double paddingVertical; final double borderRadius; final Color bgColor; final TextDirection textDirection; HighlightBackgroundPainter({ required this.text, required this.textStyle, required this.paddingHorizontal, required this.paddingVertical, required this.borderRadius, required this.bgColor, required this.textDirection, }); @override void paint(Canvas canvas, Size size) { final textPainter = TextPainter( text: TextSpan(text: text, style: textStyle), textDirection: textDirection, maxLines: null, ); textPainter.layout(maxWidth: size.width); final bgPaint = Paint()..color = bgColor; List<Rect> lineRects = []; final lineMetrics = textPainter.computeLineMetrics(); // 计算每行文本拓展padding后的边界 for (var line in lineMetrics) { lineRects.add(Rect.fromLTWH( line.left - paddingHorizontal, line.baseline - line.ascent - paddingVertical, line.width + paddingHorizontal * 2, line.height + paddingVertical * 2, )); } // 绘制带圆角的背景,处理相邻行的圆角合并 for (int i = 0; i < lineRects.length; i++) { final rect = lineRects[i]; bool hasPrevOverlap = i > 0 && (lineRects[i-1].right > rect.left && lineRects[i-1].left < rect.right); bool hasNextOverlap = i < lineRects.length -1 && (lineRects[i+1].right > rect.left && lineRects[i+1].left < rect.right); final rrect = RRect.fromRectAndCorners( rect, topLeft: hasPrevOverlap ? Radius.zero : Radius.circular(borderRadius), topRight: hasPrevOverlap ? Radius.zero : Radius.circular(borderRadius), bottomLeft: hasNextOverlap ? Radius.zero : Radius.circular(borderRadius), bottomRight: hasNextOverlap ? Radius.zero : Radius.circular(borderRadius), ); canvas.drawRRect(rrect, bgPaint); } } @override bool shouldRepaint(covariant HighlightBackgroundPainter oldDelegate) { return oldDelegate.text != text || oldDelegate.textStyle != textStyle || oldDelegate.paddingHorizontal != paddingHorizontal || oldDelegate.paddingVertical != paddingVertical || oldDelegate.borderRadius != borderRadius || oldDelegate.bgColor != bgColor || oldDelegate.textDirection != textDirection; } }
封装带高亮背景的TextField
class HighlightTextField extends StatefulWidget { const HighlightTextField({super.key}); @override State<HighlightTextField> createState() => _HighlightTextFieldState(); } class _HighlightTextFieldState extends State<HighlightTextField> { final TextEditingController _controller = TextEditingController(text: "输入测试文本\n多行换行效果测试"); final TextStyle textStyle = const TextStyle(fontSize: 16, color: Colors.white, height: 1.2); @override Widget build(BuildContext context) { return Stack( children: [ // 背景绘制层 Positioned.fill( child: CustomPaint( painter: HighlightBackgroundPainter( text: _controller.text, textStyle: textStyle, paddingHorizontal: 8, paddingVertical: 4, borderRadius: 6, bgColor: Colors.blue, textDirection: Directionality.of(context), ), ), ), // 文本输入层 TextField( controller: _controller, style: textStyle, maxLines: null, decoration: const InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 4), ), onChanged: (_) => setState(() {}), ) ], ); } }
效果说明
- 可自由调整
paddingHorizontal/paddingVertical控制背景和文本的间距 - 可自由调整
borderRadius控制圆角大小,不会出现描边方案里圆角过大的问题 - 相邻行自动合并圆角,完全消除行间隙
- 支持动态输入,文本修改后背景实时同步更新
内容的提问来源于stack exchange,提问作者nicover
相关产品推荐
相关产品推荐

