大家好,我正在开发一个 flutter 应用程序,我需要创建一个支持可拖动功能的自定义路径类型的 UI(如下图所示)。
在这里,用户应该能够在灰色路径(路线)中拖动目标标记(引脚),当用户停止拖动目标标记(引脚)时,我需要一个目标值,其中用户已更新目标并基于我需要显示用户如何完成他的目标的更新目标。
任何人都可以帮我实现这种功能吗?
使用以下示例图像(存储为
images/flag.png
):
您可以将
Flow
小部件与自定义 FlowDelegate
类一起使用,以您喜欢的方式绘制其子级:
class FooBar extends StatelessWidget {
final notifier = ValueNotifier(0.0);
@override
Widget build(BuildContext context) {
return Flow(
delegate: FooBarDelegate(notifier),
children: [
// child 0 - moving yellow flag
DecoratedBox(
decoration: const BoxDecoration(
border: Border.symmetric(horizontal: BorderSide(color: Colors.black54, width: 1)),
),
child: Stack(
children: [
ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: 2, sigmaY: 2),
child: Transform.translate(
offset: const Offset(2, 2),
child: Image.asset('images/flag.png',
colorBlendMode: BlendMode.modulate,
color: Colors.black,
),
),
),
Image.asset('images/flag.png',
colorBlendMode: BlendMode.modulate,
color: Colors.yellow,
),
Positioned.fromRect(
rect: const Rect.fromLTWH(9, 24, 58, 40),
child: GestureDetector(
onPanUpdate: (d) => notifier.value += d.delta.dy,
child: const Center(child: Text('drag me')),
),
),
],
),
),
// children 1..N - static flags
for (int i = 0; i < 6; i++)
IgnorePointer(
child: Image.asset('images/flag.png',
color: HSVColor.fromAHSV(1, 360 * i / 6, 1, 0.66).toColor(),
colorBlendMode: BlendMode.modulate,
),
),
],
);
}
}
class FooBarDelegate extends FlowDelegate {
FooBarDelegate(this.notifier) : super(repaint: notifier);
final ValueNotifier<double> notifier;
final flagAnchor = const Offset(68, 95);
@override
void paintChildren(FlowPaintingContext context) {
final r = Offset.zero & context.size;
final flagSize = context.getChildSize(0)!;
final h = r.height - flagSize.height;
final t0 = notifier.value / h;
print('t0: ${t0.toStringAsFixed(2)}');
final p0 = Alignment.topRight.inscribe(flagSize, r).topLeft + flagAnchor;
final p1 = Alignment.bottomLeft.inscribe(flagSize, r).topLeft + flagAnchor;
final rect = Rect.fromPoints(p0, p1);
final numFlags = context.childCount - 1;
final ts = List.generate(numFlags, (i) => (i + 0.5) / numFlags);
final index = ts.lowerBound(t0, (p0, p1) => p0.compareTo(p1));
if (index == 0) _paintHandle(context, rect, t0);
ts.forEachIndexed((i, t) {
_paintStaticFlag(context, rect, t, i + 1);
if (index == i + 1) _paintHandle(context, rect, t0);
});
}
_paintStaticFlag(FlowPaintingContext context, Rect rect, double t, int child) {
final transform = composeMatrixFromOffsets(
anchor: flagAnchor,
translate: Offset.lerp(rect.topRight, rect.bottomLeft, t)!,
rotation: -pi / 32,
);
context.paintChild(child, transform: transform);
}
_paintHandle(FlowPaintingContext context, Rect rect, double t) {
final transform = composeMatrixFromOffsets(
anchor: flagAnchor,
translate: Offset.lerp(rect.topRight, rect.bottomLeft, t)!,
rotation: pi / 16,
);
context.paintChild(0, transform: transform);
}
@override
BoxConstraints getConstraintsForChild(int i, BoxConstraints constraints) {
return constraints.loosen();
}
@override
bool shouldRepaint(covariant FlowDelegate oldDelegate) => false;
}
Matrix4 composeMatrixFromOffsets({
double scale = 1,
double rotation = 0,
Offset translate = Offset.zero,
Offset anchor = Offset.zero,
}) {
final double c = cos(rotation) * scale;
final double s = sin(rotation) * scale;
final double dx = translate.dx - c * anchor.dx + s * anchor.dy;
final double dy = translate.dy - s * anchor.dx - c * anchor.dy;
return Matrix4(c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, dx, dy, 0, 1);
}