Flutter中的弧形渐变?

14
我有一个Paint对象,想要用它来绘制弧形渐变,使用canvas.drawArc方法。但是根据我的研究,唯一的方法是使用Shader,而要从Gradient对象获取Shader,需要使用Gradient.createShader(Rect rect)方法,该方法需要一个矩形作为参数。我的问题是,是否有办法为弧形创建Shader,而不是矩形?以下是参考代码:
Paint paint = new Paint()
  ..color = bgColor
  ..strokeCap = StrokeCap.round
  ..strokeWidth = 3.0
  ..style = PaintingStyle.stroke
  ..shader = new Gradient.radial(size.width / 2.0, size.height / 2.0, size.height / 3.0, Colors.transparent, timerColor, TileMode.mirror).createShader(/* I don't have a rect object */);


canvas.drawArc(..., paint);
2个回答

24

你需要的矩形其实是一个正方形,可以将正在绘制的圆放入其中。弧线只是沿着该圆上切出的一片扇形。使用 Rect.fromCircle 根据圆心和半径创建这个正方形。在创建渐变和绘制弧线时使用此正方形。

以下是示例:

import 'dart:math';

import 'package:flutter/material.dart';

class X1Painter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // create a bounding square, based on the centre and radius of the arc
    Rect rect = new Rect.fromCircle(
      center: new Offset(165.0, 55.0),
      radius: 180.0,
    );

    // a fancy rainbow gradient
    final Gradient gradient = new RadialGradient(
      colors: <Color>[
        Colors.green.withOpacity(1.0),
        Colors.green.withOpacity(0.3),
        Colors.yellow.withOpacity(0.2),
        Colors.red.withOpacity(0.1),
        Colors.red.withOpacity(0.0),
      ],
      stops: [
        0.0,
        0.5,
        0.7,
        0.9,
        1.0,
      ],
    );

    // create the Shader from the gradient and the bounding square
    final Paint paint = new Paint()..shader = gradient.createShader(rect);

    // and draw an arc
    canvas.drawArc(rect, pi / 4, pi * 3 / 4, true, paint);
  }

  @override
  bool shouldRepaint(X1Painter oldDelegate) {
    return true;
  }
}

class X1Demo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(title: const Text('Arcs etc')),
      body: new CustomPaint(
        painter: new X1Painter(),
      ),
    );
  }
}

void main() {
  runApp(
    new MaterialApp(
      theme: new ThemeData.dark(),
      home: new X1Demo(),
    ),
  );
}

有没有办法做到同样的事情,但使用 PaintingStyle.stroke 进行绘制?将 Paint.style 设置为该值会完全停止弧线的绘制。 - PavelF
我不太确定你的意思。使用“PaintingStyle.stroke”只会得到用渐变绘制的轮廓。如果您想要在轮廓中填充颜色并使用渐变填充它,需要两次绘制弧线。第一次如上所示;第二次需要新的 Paint 设置样式、strokeWidth 和颜色。 - Richard Heap
好的,抱歉;我没有画足够大的弧线,它在那里,只是我看不见。 - PavelF

16

我的SweepGradient版本。

完整的例子:

import 'dart:math' as math;

import 'package:flutter/material.dart';

class GradientArcPainterDemo extends StatefulWidget {
  const GradientArcPainterDemo({
    Key key,
  }) : super(key: key);

  @override
  GradientArcPainterDemoState createState() => GradientArcPainterDemoState();
}

class GradientArcPainterDemoState extends State<GradientArcPainterDemo> {
  double _progress = 0.9;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: AppBar(title: const Text('GradientArcPainter Demo')),
      body: GestureDetector(
        onTap: () {
          setState(() {
            _progress += 0.1;
          });
        },
        child: Center(
          child: SizedBox(
            width: 200.0,
            height: 200.0,
            child: CustomPaint(
              painter: GradientArcPainter(
                progress: _progress,
                startColor: Colors.blue,
                endColor: Colors.red,
                width: 8.0,
              ),
              child: Center(child: Text('$_progress')),
            ),
          ),
        ),
      ),
    );
  }
}

class GradientArcPainter extends CustomPainter {
  const GradientArcPainter({
    @required this.progress,
    @required this.startColor,
    @required this.endColor,
    @required this.width,
  })  : assert(progress != null),
        assert(startColor != null),
        assert(endColor != null),
        assert(width != null),
        super();

  final double progress;
  final Color startColor;
  final Color endColor;
  final double width;

  @override
  void paint(Canvas canvas, Size size) {
    final rect = new Rect.fromLTWH(0.0, 0.0, size.width, size.height);
    final gradient = new SweepGradient(
      startAngle: 3 * math.pi / 2,
      endAngle: 7 * math.pi / 2,
      tileMode: TileMode.repeated,
      colors: [startColor, endColor],
    );

    final paint = new Paint()
      ..shader = gradient.createShader(rect)
      ..strokeCap = StrokeCap.butt  // StrokeCap.round is not recommended.
      ..style = PaintingStyle.stroke
      ..strokeWidth = width;
    final center = new Offset(size.width / 2, size.height / 2);
    final radius = math.min(size.width / 2, size.height / 2) - (width / 2);
    final startAngle = -math.pi / 2;
    final sweepAngle = 2 * math.pi * progress;
    canvas.drawArc(new Rect.fromCircle(center: center, radius: radius),
        startAngle, sweepAngle, false, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

结果:


1
如何使渐变色谱成为完整的颜色光谱,即使进度只有部分完成?例如,如果进度仅为0.2,我实际上希望看到从红色到蓝色的整个扫描,而不仅仅是蓝色部分。 - MobileMon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接