Home > front end >  Flutter 2: Create Gradient Icon without ShaderMask
Flutter 2: Create Gradient Icon without ShaderMask

Time:10-04

I am searching a way to create a Gradient Icon without using ShaderMask().

Why without ShaderMask ?

Because calling saveLayer() allocates an offscreen buffer. Drawing content into the offscreen buffer might trigger render target switches that are particularly slow in older GPUs. (official flutter documentation)

So here the GradientIcon I have made with the ShaderMask.

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));
      },
    );
  }
}

So my question is : Is there a way of rendering Gradient Icon without calling saveLayer(), so without calling ShaderMask() ?

CodePudding user response:

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;
  }
}
  • Related