Skip to content

Instantly share code, notes, and snippets.

@dsyrstad
Created September 3, 2020 21:07
Show Gist options
  • Save dsyrstad/14274fe5bb2c2da4572b31316ca5c1bd to your computer and use it in GitHub Desktop.
Save dsyrstad/14274fe5bb2c2da4572b31316ca5c1bd to your computer and use it in GitHub Desktop.
Flutter Path.addPath() bug
import 'dart:ui';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'AddPath Bug Demo',
theme: ThemeData(
primarySwatch: Colors.green,
),
home: BugPainter(),
);
}
}
enum DrawingMethod { AddPath, AddPathWithMoveTo, AddPathWithNoAA, DirectToCanvas }
class BugPainter extends StatefulWidget {
@override
_BugPainterState createState() => _BugPainterState();
}
class _BugPainterState extends State<BugPainter> with TickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
DrawingMethod _drawingMethod = DrawingMethod.AddPath;
@override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
duration: Duration(seconds: 1),
);
final valueTween = Tween<double>(begin: 0, end: 1);
animation = valueTween.animate(controller)
..addListener(() {
setState(() {});
})
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.repeat();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
controller.forward();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 120),
),
for (final type in DrawingMethod.values)
RadioListTile<DrawingMethod>(
title: Text(type.toString()),
value: type,
groupValue: _drawingMethod,
onChanged: (value) {
setState(() {
_drawingMethod = value;
});
},
),
Expanded(
child: AnimatedBuilder(
animation: animation,
builder: (context, snapshot) {
return CustomPaint(
painter: ShapePainter(animation.value, _drawingMethod),
child: Container(),
);
},
),
),
],
),
),
);
}
}
class ShapePainter extends CustomPainter {
final double _value;
final DrawingMethod _drawingMethod;
final List<PathContent> _paths = List();
ShapePainter(this._value, this._drawingMethod) {
for (int i = 0; i < 15; i++) {
_paths.add(PathContent(_drawingMethod));
}
}
@override
void paint(Canvas canvas, Size size) {
double x = size.width / 2;
double y = (size.height - size.width) / 2;
for (final path in _paths) {
final transform = Matrix4.identity()
..translate(x, y)
..scale(1 - _value / 10);
path.draw(canvas, transform);
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
class PathContent {
final Path _path = Path();
final DrawingMethod _drawingMethod;
final List<Path> children = List();
final Paint _paint = Paint()
..color = Colors.teal
// fill makes a huge difference here. If you change it to stroke, there's no problem.
..style = PaintingStyle.fill;
final Paint _paintNoAA = Paint()
..color = Colors.teal
..style = PaintingStyle.fill
..isAntiAlias = false;
PathContent(this._drawingMethod) {
final rect = Rect.fromLTWH(0, 0, 40, 40);
for (var i = 0; i < 10; i++) {
children.add(Path()..addRect(rect));
}
}
void draw(Canvas canvas, Matrix4 transform) {
switch (_drawingMethod) {
case DrawingMethod.AddPath:
drawUsingAddPath(canvas, transform);
break;
case DrawingMethod.DirectToCanvas:
drawDirectToCanvas(canvas, transform);
break;
case DrawingMethod.AddPathWithMoveTo:
drawUsingAddPathWithMoveTo(canvas, transform);
break;
case DrawingMethod.AddPathWithNoAA:
drawUsingAddPathWithNoAntiAliasing(canvas, transform);
break;
}
}
void drawUsingAddPath(Canvas canvas, Matrix4 transform) {
_path.reset();
for (final child in children) {
_path.addPath(child, Offset.zero, matrix4: transform.storage);
}
canvas.drawPath(_path, _paint);
}
void drawUsingAddPathWithMoveTo(Canvas canvas, Matrix4 transform) {
_path.reset();
for (final child in children) {
_path.moveTo(0, 0); // Speeds it up somewhat
_path.addPath(child, Offset.zero, matrix4: transform.storage);
}
canvas.drawPath(_path, _paint);
}
void drawUsingAddPathWithNoAntiAliasing(Canvas canvas, Matrix4 transform) {
_path.reset();
for (final child in children) {
_path.addPath(child, Offset.zero, matrix4: transform.storage);
}
canvas.drawPath(_path, _paintNoAA);
}
void drawDirectToCanvas(Canvas canvas, Matrix4 transform) {
canvas.save();
canvas.transform(transform.storage);
for (final child in children) {
canvas.drawPath(child, _paint);
}
canvas.restore();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment