Android开发中,遇到聊天背景,复杂的布局背景这类的需求,一般都是通过9Patch图去实现的,简单好用,效果最能达到UI的要求,尤其是带有阴影的背景,9Patch简直是程序员之光。
遗憾的是,这么好用的东西,只能在Android的开发中使用,放到Flutter中就不灵光了。那么,Flutter中想要实现这样的效果,应该怎么做呢?
研究了一番,其实,Flutter中也提供了类似与9Patch图的实现效果,只是这个效果要结合Container的padding和decoration这两个属性了。
先来看一下我们要实现的最终效果:
需要用到的切图:
那么具体应该怎么实现呢?
在Flutter中实现这样的效果,需要将整体放入一个Container中,这是前提,然后分两个维度去布局文字和Container四周的边距,以及切图拉伸的点位和宽高。
第一个维度:文字(也可以是其他内容)和Container四周的边距(即padding),这个比较简单,设置Container的padding属性即可,不过这里设置的值有些讲究,如下图:
这里如果要想文字在气泡的中间显示,那么左侧还要忽略多出来的这个箭头的宽度,所以设置padding的时候应该是这样的:
padding: const EdgeInsets.fromLTRB(20, 12, 16, 12),
第二个维度:气泡的拉伸,这里需要明确的是,气泡左侧箭头及以上部分是不能拉伸的,否则,这个箭头就会变形,所以,拉伸的时候只能从箭头下面开始拉伸(这里对应的是LTRB或者LTWH的T,关于LTRB和LTWH的区别可自行查询,这里不再赘述),如下图,
粉色的方框便是拉伸的区域,这个拉伸区域可以通过如下代码设置:
centerSlice: Rect.fromLTWH(20, 25, ?, ?)
或者
centerSlice: Rect.fromLTRB(20, 25, ?, ?)
左侧和上面比较好确定,避开左侧三角箭头即可,我这里大概试了几下,确定了左:20,上:25,那么如何确定右侧和底部的数值呢?
刚开始的时候我尝试将这个拉伸区域拉开一点点,比如拉开宽高为1,代码如下:
centerSlice: Rect.fromLTWH(20, 25, 1, 1),
或者
centerSlice: Rect.fromLTRB(20, 25, 21, 26),
当我敲了很长一段文字后,确实好用,按照预想的展示出来了,大功告成。
当我输入了一个“123”之后,发现不好用了,连图带字全都不显示了,控制台出现了一串报错:
The following assertion was thrown during paint():
Assertion failed: file:///D:/Work/Flutter/flutter_sdk/flutter/packages/flutter/lib/src/painting/decoration_image.dart:535:12
sourceSize == inputSize
"centerSlice was used with a BoxFit that does not guarantee that the image is fully visible."
点开日志指向的文件:里面有段注释写到:
意思是"我们无法在应用9Patch拉伸的同时绘制图像的子集。",为甚么文字多的时候没问题,文字少的时候就不行了?根据注释,我猜测是文字多的时候渲染的比较慢,给了9Patch图充分的拉伸时间,文字少的时候渲染快,这时候9Patch还没有完成拉伸或者正在拉伸,我设置的拉伸宽高只有1,这就导致了文字渲染不进去,或者9Patch不完全可见,验证了错误的日志:
centerSlice was used with a BoxFit that does not guarantee that the image is fully visible.
翻译过来就是:centerSlice 与不保证图像完全可见的 BoxFit 一起使用。
最终,我把拉伸的宽高改大一点比如5或者10之类的,也不能太大,否则气泡切图会变形,够用即可。完整代码如下:
Container(
padding: const EdgeInsets.fromLTRB(20, 12, 16, 12),
margin: const EdgeInsets.only(left: 10),
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/....../qipao.png'),
centerSlice: Rect.fromLTWH(20, 25, 10, 10),
),
),
child: Text("测试文字"),
),
其中的
centerSlice: Rect.fromLTWH(20, 25, 10, 10),
也可以改为
centerSlice: Rect.fromLTRB(20, 25, 30, 35),
至此,Flutter中使用9Patch类似效果的实现方法已经写完了,需要注意的是不要把padding和拉伸这两个维度弄混了,这两个其实是没有必然联系的,我刚开的时候调这些参数的时候也是很乱的,理清这两个维度的关系,这个效果就很容易实现了。