如何在Flutter中实现文字内嵌图片的效果?
Flutter实现图片填充文字效果的方案
方法一:使用ShaderMask组件(推荐)
这是实现图片填充文字最简洁高效的方式,无需额外PNG资源,直接通过着色器将图片纹理映射到文字上。
示例代码:
import 'dart:ui' as ui; import 'package:flutter/material.dart'; class ImageFillText extends StatefulWidget { const ImageFillText({super.key}); @override State<ImageFillText> createState() => _ImageFillTextState(); } class _ImageFillTextState extends State<ImageFillText> { ui.Image? _image; @override void initState() { super.initState(); _loadImage(); } Future<void> _loadImage() async { final bytes = await DefaultAssetBundle.of(context).load('assets/palm_tree.jpg'); _image = await decodeImageFromList(bytes.buffer.asUint8List()); setState(() {}); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: _image == null ? const CircularProgressIndicator() : ShaderMask( blendMode: BlendMode.srcIn, shaderCallback: (bounds) => ImageShader( _image!, TileMode.repeated, TileMode.repeated, Matrix4.identity().storage, ), child: const Text( 'Hawaii', style: TextStyle( fontSize: 120, fontWeight: FontWeight.bold, color: Colors.white, // 该颜色会被Shader覆盖,可随意设置 ), ), ), ), ); } }
代码说明:
BlendMode.srcIn会保留子组件(文字)的形状,用指定Shader填充内容ImageShader将加载的图片作为纹理,TileMode.repeated可让图片在文字内部重复填充,也可改用TileMode.clamp只显示一次- 需异步加载图片,避免阻塞UI
方法二:使用CustomPaint自定义绘制
如果需要更复杂的自定义逻辑(比如调整图片在文字中的位置、缩放比例),可以用CustomPaint手动实现:
示例代码:
import 'dart:ui' as ui; import 'package:flutter/material.dart'; class CustomImageFillText extends StatefulWidget { const CustomImageFillText({super.key}); @override State<CustomImageFillText> createState() => _CustomImageFillTextState(); } class _CustomImageFillTextState extends State<CustomImageFillText> { ui.Image? _image; final TextPainter _textPainter = TextPainter( text: const TextSpan( text: 'Hawaii', style: TextStyle( fontSize: 120, fontWeight: FontWeight.bold, ), ), textDirection: TextDirection.ltr, ); @override void initState() { super.initState(); _loadImage(); _textPainter.layout(); } Future<void> _loadImage() async { final bytes = await DefaultAssetBundle.of(context).load('assets/palm_tree.jpg'); _image = await decodeImageFromList(bytes.buffer.asUint8List()); setState(() {}); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: _image == null ? const CircularProgressIndicator() : CustomPaint( size: _textPainter.size, painter: _ImageTextPainter( image: _image!, textPainter: _textPainter, ), ), ), ); } } class _ImageTextPainter extends CustomPainter { final ui.Image image; final TextPainter textPainter; _ImageTextPainter({required this.image, required this.textPainter}); @override void paint(Canvas canvas, Size size) { // 获取文字路径作为裁剪区域 final path = Path(); textPainter.text.getPathForRange(0, textPainter.text.length, Offset.zero, path); // 保存画布状态,裁剪到文字路径 canvas.save(); canvas.clipPath(path); // 在裁剪区域内绘制图片 final paint = Paint(); canvas.drawImageRect( image, Rect.fromLTWH(0, 0, image.width.toDouble(), image.height.toDouble()), Rect.fromLTWH(0, 0, size.width, size.height), paint, ); canvas.restore(); } @override bool shouldRepaint(covariant _ImageTextPainter oldDelegate) { return image != oldDelegate.image || textPainter != oldDelegate.textPainter; } }
说明:
- 先通过
TextPainter获取文字路径,再用clipPath裁剪画布,最后在裁剪区域内绘制图片 - 灵活性更高,但代码量比
ShaderMask方案多
对比Stack叠PNG方案的不足
你之前用Stack叠加透明PNG的方式存在明显缺陷:
- 需要额外制作带透明通道的文字图片,维护成本高
- 修改文字大小、样式时需重新生成图片
- 无法动态调整图片在文字内的填充方式(重复、拉伸等)
上面两种纯代码实现的方案,支持动态修改文字内容、大小、样式,图片也可随时替换,是更优的实践。
内容的提问来源于stack exchange,提问作者teao22
相关产品推荐
相关产品推荐

