import 'package:flutter/material.dart'; class FlowSegment { final Offset from; final Offset to; final Color color; final double powerW; final bool dashed; const FlowSegment({ required this.from, required this.to, required this.color, required this.powerW, this.dashed = false, }); } class FlowPainter extends CustomPainter { final List segments; final double animValue; final Color lineColor; const FlowPainter({ required this.segments, required this.animValue, required this.lineColor, }); @override void paint(Canvas canvas, Size size) { final linePaint = Paint() ..color = lineColor ..strokeWidth = 1.5 ..style = PaintingStyle.stroke ..strokeCap = StrokeCap.round; for (final seg in segments) { final src = Offset(seg.from.dx * size.width, seg.from.dy * size.height); final dst = Offset(seg.to.dx * size.width, seg.to.dy * size.height); if (seg.dashed) { _drawDashed(canvas, src, dst, linePaint); } else { canvas.drawLine(src, dst, linePaint); } if (seg.powerW > 30) { final n = (seg.powerW / 600).clamp(1, 5).round(); final r = seg.powerW > 1500 ? 3.5 : 2.5; final particlePaint = Paint() ..color = seg.color ..style = PaintingStyle.fill; for (int i = 0; i < n; i++) { final t = (animValue + i / n) % 1.0; final pos = Offset.lerp(src, dst, t)!; canvas.drawCircle(pos, r, particlePaint); } } } } void _drawDashed(Canvas canvas, Offset src, Offset dst, Paint paint) { final dir = dst - src; final length = dir.distance; if (length == 0) return; final unit = dir / length; const dashLen = 5.0; const gapLen = 4.0; double pos = 0; while (pos < length) { final end = (pos + dashLen).clamp(0.0, length); canvas.drawLine(src + unit * pos, src + unit * end, paint); pos += dashLen + gapLen; } } @override bool shouldRepaint(FlowPainter old) => old.animValue != animValue || old.lineColor != lineColor || old.segments.length != segments.length; }