【问题标题】:Flutter 2: Create Gradient Icon without ShaderMaskFlutter 2:在没有 ShaderMask 的情况下创建渐变图标
【发布时间】:2021-10-01 08:55:24
【问题描述】:

我正在寻找一种不使用 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()

【问题讨论】:

  • 即使与RepaintBoundary 一起使用也会有问题吗?
  • 在官方文档中它说“其他可能触发 saveLayer() 并且可能代价高昂的小部件:ShaderMask、ColorFilter、Chip—如果 disabledColorAlpha != 0xff, Text—可能会导致调用 saveLayer()如果存在溢出着色器,则调用 saveLayer()”。所以是的。
  • 我的意思是,如果您尝试使用 RepaintBoundary 小部件包装所有内容 - 我已经知道 saveLayoer 可能很昂贵
  • 嗯,我没用过所以不知道怎么画
  • return RepaintBoundary(child: ShaderMask(...

标签: flutter performance icons gradient


【解决方案1】:
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;
  }
}

【讨论】:

  • 顺便说一句,对于简单的情况(比如你的)TextPainter api 可能比Paragraph 更方便...
  • @pskink 感谢您的评论,我已将其更新为 TextPainter
  • ...如果你有Size,那么不要冗长Rect.fromLTWH(0.0, 0.0, size.width, size.height),只需使用漂亮的快捷方式:Offset.zero &amp; size;-)
  • 哦,是的,聪明的 AND 运算符!
猜你喜欢
  • 2017-02-27
  • 1970-01-01
  • 2014-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-15
  • 2020-05-11
相关资源
最近更新 更多