FlutterでLoadingを表すアニメーションWidgetが何個がありますが(例えば、CircularProgressIndicatorとか)、自分なりのアニメーションのカスタマイズしたい場合、CustomPaintを用いて描くことも可能です。

回転円
まずはシンプルな円回転を描いてみましょう。
下記の例では、AnimationControllerの値に応じてアークを連続的に回転させるだけです。

光輪(ハロー)
円の光輪(ハロー)はPaintのmaskFilterの属性で実現できます。
  /// See also:
  ///
  ///  * [Canvas.drawShadow], which is a more efficient way to draw shadows.
  const MaskFilter.blur(
    this._style,
    this._sigma,
  ) : assert(_style != null),
      assert(_sigma != null);
上記の属性でぼやけた影を生成できます。パラメーター_sigmaがぼかしの程度を表します。
paint.maskFilter = MaskFilter.blur(BlurStyle.solid, 4);
その_sigmaをアニメーションの値にすると、光輪の動画生成できます。
  final Animatable<double> breatheTween = TweenSequence<double>(
    <TweenSequenceItem<double>>[
      TweenSequenceItem<double>(
        tween: Tween<double>(begin: 0, end: 4),
        weight: 1,
      ),
      TweenSequenceItem<double>(
        tween: Tween<double>(begin: 4, end: 0),
        weight: 1,
      ),
    ],
  ).chain(CurveTween(curve: Curves.decelerate));
  paint.maskFilter = MaskFilter.blur(BlurStyle.solid, breatheTween.evaluate(animation));
 
着色
前の章でPathのpaint属性でshader着色を紹介したことあるので、今回をこちらの知識で光輪を着色しようと思います。
    // 光輪
    paint.maskFilter = MaskFilter.blur(BlurStyle.solid, breatheTween.evaluate(animation));
    List<Color> colors = [
      Colors.red,
      Colors.orange,
      Colors.yellow,
      Colors.green,
      Colors.cyan,
      Colors.blue,
      Colors.purple,
    ];
    colors.addAll(colors.toList());
    final List<double> pos = List.generate(colors.length, (index) => index / colors.length);
    // shader
    paint.shader = ui.Gradient.sweep(Offset.zero, colors, pos, TileMode.clamp, 0, 2 * pi);
 
光輪を回転させる
最後は光輪と回転する円を結合すれば、ぐるぐる回るLoading円ができます。
    canvas.translate(size.width / 2, size.height / 2);
    final Paint paint = Paint()
      ..style = PaintingStyle.stroke
      ..color = Colors.purple
      ..strokeWidth = 1;
    // 光輪
    paint.maskFilter = MaskFilter.blur(BlurStyle.solid, breatheTween.evaluate(animation));
    List<Color> colors = [
      Colors.red,
      Colors.orange,
      Colors.yellow,
      Colors.green,
      Colors.cyan,
      Colors.blue,
      Colors.purple,
    ];
    colors.addAll(colors.toList());
    final List<double> pos = List.generate(colors.length, (index) => index / colors.length);
    // shader
    paint.shader = ui.Gradient.sweep(Offset.zero, colors, pos, TileMode.clamp, 0, 2 * pi);
    Path path = Path();
    path.addOval(Rect.fromCenter(center: Offset.zero, width: 200, height: 200));
    canvas.save();
    canvas.rotate(animation.value * 2 * pi);
    canvas.drawPath(path, paint);
    canvas.restore();
