You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中Draggable组件无法拖拽问题求助

Flutter Draggable组件无响应问题解决

问题现象

  • 实现的两个雷达组件无法拖拽,无任何交互反馈,控制台无输出或报错
  • Android、Windows平台测试均无法触发手势事件,尝试HitTestBehavior.opaque和Positioned组件未解决

问题根源

  1. CustomPaint无有效占位:CustomPaint未设置size,组件实际尺寸为0×0,手势检测无法命中目标区域
  2. 组件定位不匹配:Stack中的RadarWidget未定位,默认堆叠在左上角,与绘制的图形位置脱节
  3. 拖拽状态未同步:未在拖拽结束时更新RadarPoint的位置,导致拖拽后组件位置不变

修复方案

  1. 给CustomPaint指定明确尺寸,确保组件有可交互的区域
  2. 使用Positioned在Stack中定位每个RadarWidget,使其与绘制图形的中心对齐
  3. 在onDragEnd回调中更新RadarPoint的position,同步拖拽后的位置

修改后的完整代码

import 'dart:math' as math;

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

class RadarPoint {
  Offset position;
  int stateValue;
  double orientation;

  RadarPoint({
    required this.position,
    required this.stateValue,
    required this.orientation,
  });
}

final List<RadarPoint> points = [
  RadarPoint(position: const Offset(300, 60), orientation: 0, stateValue: 0),
  RadarPoint(position: const Offset(300, 220), orientation: 45, stateValue: 1),
];

const double maxAngle = 150;
const double diameter = 200;
const List<Color> distanceColors = [
  Colors.red,
  Colors.orange,
  Colors.yellow,
  Colors.green,
];

class RadarPainter extends CustomPainter {
  RadarPoint point;
  double diameter;
  List<Color> distanceColors;
  double maxAngle;

  RadarPainter({
    required this.point,
    required this.diameter,
    required this.distanceColors,
    required this.maxAngle,
  });

  double degreesToRads(double deg) => deg * math.pi / 180.0;
  double currentDiameter(int i) => diameter / distanceColors.length * (i + 1);

  @override
  void paint(Canvas canvas, Size size) {
    // 将绘制原点移到组件中心,避免偏移
    canvas.translate(size.width / 2, size.height / 2);
    
    for (var i = distanceColors.length - 1; i > -1; i--) {
      if (i >= point.stateValue) {
        canvas.drawArc(
          Rect.fromCenter(
            center: Offset.zero,
            height: currentDiameter(i),
            width: currentDiameter(i),
          ),
          degreesToRads(point.orientation),
          degreesToRads(maxAngle),
          true,
          Paint()..color = distanceColors[i].withOpacity(0.5),
        );
      }
    }
    canvas.drawCircle(Offset.zero, 5, Paint()..color = Colors.black);
  }

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

class RadarWidget extends StatefulWidget {
  final RadarPoint point;

  const RadarWidget({super.key, required this.point});

  @override
  State<RadarWidget> createState() => _RadarWidgetState();
}

class _RadarWidgetState extends State<RadarWidget> {
  late RadarPoint point;

  @override
  void initState() {
    super.initState();
    point = widget.point;
  }

  Widget _feedback() {
    return SizedBox(
      width: diameter,
      height: diameter,
      child: CustomPaint(
        painter: RadarPainter(
          point: point,
          diameter: diameter * 1.2,
          distanceColors: distanceColors,
          maxAngle: maxAngle,
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Positioned(
      left: point.position.dx - diameter / 2,
      top: point.position.dy - diameter / 2,
      child: Draggable<RadarPoint>(
        data: point,
        feedback: _feedback(),
        onDragStarted: () {
          print('onDragStarted');
        },
        onDragUpdate: (details) {
          print('onDragUpdate');
        },
        onDragEnd: (details) {
          print('onDragEnd');
          setState(() {
            // 更新位置,基于拖拽结束时的全局坐标
            point.position = details.offset - Offset(diameter/2, diameter/2);
          });
        },
        onDraggableCanceled: (velocity, offset) {
          print('onDraggableCanceled');
        },
        hitTestBehavior: HitTestBehavior.opaque,
        child: SizedBox(
          width: diameter,
          height: diameter,
          child: CustomPaint(
            painter: RadarPainter(
              point: point,
              diameter: diameter,
              distanceColors: distanceColors,
              maxAngle: maxAngle,
            ),
          ),
        ),
      ),
    );
  }
}

class App extends StatelessWidget {
  App({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Stack(
          children: [
            RadarWidget(point: points[0]),
            RadarWidget(point: points[1]),
          ],
        ),
      ),
    );
  }
}

Future<void> main() async {
  debugPrintGestureArenaDiagnostics = false;
  runApp(App());
}

内容的提问来源于stack exchange,提问作者Mauricio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 15:27:17