Flutter 2:在没有 ShaderMask 的情况下创建渐变图标
Flutter 2: Create Gradient Icon without ShaderMask
我正在寻找一种不使用 ShaderMask()
来创建渐变图标的方法。
为什么不用 ShaderMask?
因为调用 saveLayer()
分配了一个屏幕外缓冲区。将内容绘制到屏幕外缓冲区可能会触发渲染目标切换,这在旧 GPU 中特别慢。 (official flutter documentation)
这里是我用 ShaderMask 制作的 GradientIcon。
import 'package:flutter/material.dart';
class GradientIcon extends StatefulWidget {
final IconData? icon;
final double? size;
final Gradient? gradient;
const GradientIcon({
@required this.icon,
@required this.size,
@required this.gradient,
Key? key
}) : super(key: key);
@override
_GradientIconState createState() => _GradientIconState();
}
class _GradientIconState extends State<GradientIcon> {
double size = 0;
static const iconSizeMultiplier = 1.2;
Shader? shaderFromGradient;
@override
void initState() {
super.initState();
if (widget.size != null)
size = widget.size! * iconSizeMultiplier;
}
@override
Widget build(BuildContext context) {
return ShaderMask(
child: Icon(
widget.icon,
size: size,
color: Colors.white,
),
shaderCallback: (Rect bounds) {
return widget.gradient!.createShader(Rect.fromLTRB(0, 0, size, size));
},
);
}
}
所以我的问题是:有没有一种方法可以在不调用 saveLayer()
的情况下渲染渐变图标,也就是不调用 ShaderMask()
?
import 'package:flutter/material.dart';
class GradientIcon extends StatelessWidget {
final IconData icon;
final Gradient gradient;
final double size;
const GradientIcon(
this.icon,
this.gradient,
{
this.size = 24,
Key? key
}
) : super(key: key);
@override
Widget build(BuildContext context) {
return RepaintBoundary(
child: CustomPaint(
size: Size(size, size),
painter: _GradientIconPainter(
icon: icon,
gradient: gradient,
iconSize: size
),
)
);
}
}
class _GradientIconPainter extends CustomPainter {
final IconData? icon;
final Gradient? gradient;
final double? iconSize;
_GradientIconPainter({
Listenable? repaint,
@required this.icon,
@required this.gradient,
@required this.iconSize
}) : super(repaint: repaint);
@override
void paint(Canvas canvas, Size size) {
final Paint _gradientShaderPaint = Paint()
..shader = gradient!.createShader(
Rect.fromLTWH(0.0, 0.0, size.width, size.height)
);
final TextPainter _textPainter = TextPainter(
textDirection: TextDirection.ltr,
text: TextSpan(
text: String.fromCharCode(icon!.codePoint),
style: TextStyle(
foreground: _gradientShaderPaint,
fontFamily: icon!.fontFamily,
fontSize: iconSize
),
)
);
_textPainter.layout(
minWidth: 0,
maxWidth: size.width,
);
final xCenter = (size.width - _textPainter.width) / 2;
final yCenter = (size.height - _textPainter.height) / 2;
final offset = Offset(xCenter, yCenter);
_textPainter.paint(canvas, offset);
}
@override
bool shouldRepaint(_GradientIconPainter oldDelegate) {
return icon != oldDelegate.icon || gradient != oldDelegate.gradient ||
iconSize != oldDelegate.iconSize;
}
}
我正在寻找一种不使用 ShaderMask()
来创建渐变图标的方法。
为什么不用 ShaderMask?
因为调用 saveLayer()
分配了一个屏幕外缓冲区。将内容绘制到屏幕外缓冲区可能会触发渲染目标切换,这在旧 GPU 中特别慢。 (official flutter documentation)
这里是我用 ShaderMask 制作的 GradientIcon。
import 'package:flutter/material.dart';
class GradientIcon extends StatefulWidget {
final IconData? icon;
final double? size;
final Gradient? gradient;
const GradientIcon({
@required this.icon,
@required this.size,
@required this.gradient,
Key? key
}) : super(key: key);
@override
_GradientIconState createState() => _GradientIconState();
}
class _GradientIconState extends State<GradientIcon> {
double size = 0;
static const iconSizeMultiplier = 1.2;
Shader? shaderFromGradient;
@override
void initState() {
super.initState();
if (widget.size != null)
size = widget.size! * iconSizeMultiplier;
}
@override
Widget build(BuildContext context) {
return ShaderMask(
child: Icon(
widget.icon,
size: size,
color: Colors.white,
),
shaderCallback: (Rect bounds) {
return widget.gradient!.createShader(Rect.fromLTRB(0, 0, size, size));
},
);
}
}
所以我的问题是:有没有一种方法可以在不调用 saveLayer()
的情况下渲染渐变图标,也就是不调用 ShaderMask()
?
import 'package:flutter/material.dart';
class GradientIcon extends StatelessWidget {
final IconData icon;
final Gradient gradient;
final double size;
const GradientIcon(
this.icon,
this.gradient,
{
this.size = 24,
Key? key
}
) : super(key: key);
@override
Widget build(BuildContext context) {
return RepaintBoundary(
child: CustomPaint(
size: Size(size, size),
painter: _GradientIconPainter(
icon: icon,
gradient: gradient,
iconSize: size
),
)
);
}
}
class _GradientIconPainter extends CustomPainter {
final IconData? icon;
final Gradient? gradient;
final double? iconSize;
_GradientIconPainter({
Listenable? repaint,
@required this.icon,
@required this.gradient,
@required this.iconSize
}) : super(repaint: repaint);
@override
void paint(Canvas canvas, Size size) {
final Paint _gradientShaderPaint = Paint()
..shader = gradient!.createShader(
Rect.fromLTWH(0.0, 0.0, size.width, size.height)
);
final TextPainter _textPainter = TextPainter(
textDirection: TextDirection.ltr,
text: TextSpan(
text: String.fromCharCode(icon!.codePoint),
style: TextStyle(
foreground: _gradientShaderPaint,
fontFamily: icon!.fontFamily,
fontSize: iconSize
),
)
);
_textPainter.layout(
minWidth: 0,
maxWidth: size.width,
);
final xCenter = (size.width - _textPainter.width) / 2;
final yCenter = (size.height - _textPainter.height) / 2;
final offset = Offset(xCenter, yCenter);
_textPainter.paint(canvas, offset);
}
@override
bool shouldRepaint(_GradientIconPainter oldDelegate) {
return icon != oldDelegate.icon || gradient != oldDelegate.gradient ||
iconSize != oldDelegate.iconSize;
}
}