Flutter RichText 支持图片显示和自定义图片效果,经典 Android 开发教程
///so that we can define letterSpacing as Image Span widthconst String imageSpanTransparentPlaceholder = "\u200B";
///transparentPlaceholder is transparent text//fontsize id define image height//size = 30.0/26.0 * fontSize///final double size = 30.0;///fontSize 26 and text height =30.0//final double fontSize = 26.0;
double dpToFontSize(double dp) {return dp / 30.0 * 26.0;}
图片文字那么必然要有图片了,那么我们就提供个 ImageProvider 来装载图片,因为做过extended image,这部分不要太熟悉了,对 image 不了解的同学可以去看看 这个 全能的Image
当然我没有忘记给大家准备网络图片缓存的 ImageProvider,以及清除它们的方法 clearExtendedTextDiskCachedImages
CachedNetworkImage(this.url,{this.scale = 1.0,this.headers,this.cache: false,this.retries = 3,this.timeLimit,this.timeRetry = const Duration(milliseconds: 100)}): assert(url != null),
assert(scale != null);
/// Clear the disk cache directory then return if it succeed./// <param name="duration">timespan to compute whether file has expired or not</param>Future<bool> clearExtendedTextDiskCachedImages({Duration duration}) async
需要注意的是,因为 ImageSpan 没法获取到 BuildContext,所以我们需要在 Extended text build 的时候,把 ImageProvider 所需要的 ImageConfiguration 准备好
void _createImageConfiguration(List<TextSpan> textSpan, BuildContext context) {textSpan.forEach((ts) {if (ts is ImageSpan) {ts.createImageConfiguration(context);} else if (ts.children != null) {_createImageConfiguration(ts.children, context);}});}
接下来就要到核心绘画文字的类里面去了 ExtendedRenderParagraph 在 Paint 方法中,在画字之前我们来处理这个图片(反正文字是透明的,而且 0 的 width,只是有个与前后文字的距离(图片的宽)),在绘画图片的时候,我把画布移动到 offset 的地方,就是整个文字开始绘画的点,方便后面计算的绘画
void paint(PaintingContext context, Offset offset) {_paintSpecialText(context, offset);_paint(context, offset);}
void _paintSpecialText(PaintingContext context, Offset offset) {final Canvas canvas = context.canvas;
canvas.save();///move to extended textcanvas.translate(offset.dx, offset.dy);
///we have move the canvas, so rect top left should be (0,0)final Rect rect = Offset(0.0, 0.0) & size;_paintSpecialTextChildren(<TextSpan>[text], canvas, rect);canvas.restore();}
在_paintSpecialTextChildren 中,循环找寻 ImageSpan. 注意使用 getOffsetForCaret 方法,我们来判断这个 TextSpan 是否已经是文本溢出了。
Offset topLeftOffset = getOffsetForCaret(TextPosition(offset: textOffset),rect,);//skip invalid or overflowif (topLeftOffset == null ||(textOffset != 0 && topLeftOffset == Offset.zero)) {return;}
textOffset 起始为 0,当跳过一个 TextSpan,我们加上该 TextSpan 的 offset,然后继续查找
textOffset += ts.toPlainText().length;
如果是一个 ImageSpan,首先因为这个\u200B 没有宽度,而宽度是我们设置的 letterSpacing,所以这个图片绘画的地方应该要向前移动 width / 2.0
if (ts is ImageSpan) {///imageSpanTransparentPlaceholder \u200B has no width, and we define image width by///use letterSpacing,so the actual top-left offset of image should be subtract letterSpacing(width)/2.0Offset imageSpanOffset = topLeftOffset - Offset(ts.width / 2.0, 0.0);
if (!ts.paint(canvas, imageSpanOffset)) {//image not readyts.resolveImage(listener: (ImageInfo imageInfo, bool synchronousCall) {if (synchronousCall)ts.paint(canvas, imageSpanOffset);else {if (owner == null || !owner.debugDoingPaint) {markNeedsPaint();}}});}}
ImageSpan 的 paint 方法,如果图片还没加载,那么我们需要 resolveImage 并且监听回调,在回调的时候,如果是一个同步的回调,那么这个时候 Canvas 应该不没有被 dispose 掉,那么我们就直接画上。否则判断 owner,并且设置 markNeedsPaint,让整个 Text 再次绘画。
上面就是怎么在文本中加入一个图片,然而产品可不是那么好对付的,产品说,那个图片给我加个圆角,加个 Border,加个加载效果,给弄成圆形的,巴拉巴拉...说累了,你就直接按照下面的图来做吧。
看到这样的需求,我的表情为
不过其实掌握了 Canvas 的一些技巧之后,这点事情难不倒我,加上 2 个回调,在绘画图片之前和之后,做你想要做的任何事情。
///you can paint your placeholder or clip///any thing you wantfinal BeforePaintImage beforePaintImage;
///you can paint border,shadow etcfinal AfterPaintImage afterPaintImage;
比如说在图片加载之后来个 loading 占位,你可以这样做
评论