如何通过覆盖Draggable来访问DragTarget?

时间:2019-04-19 22:43:42

标签: flutter

我试图在Flutter中创建一个游戏,将物品拖放到目标中。一旦将物品放到目标上,它将重新放置在目标上。

这可以工作,直到将下一个项目放到原始目标上为止。由于第一项位于目标的“顶部”,因此不会再触发onAccept。

解决此问题的最佳方法是什么?

我基本上改写了这个问题/答案:How to update Draggable child when entering DragTarget in Flutter?

主要部分:

child: Stack(
              children: <Widget>[
                MyDragTarget(Offset(50, 500), draggableController, 'target 1'),
                MyDragTarget(Offset(250, 500), draggableController, 'target 2'),
                MyDraggable(
                  Offset(50, 100),
                  draggableController,
                  'ball 1',
                ),
                MyDraggable(
                  Offset(150, 100),
                  draggableController,
                  'ball 2',
                ),
                MyDraggable(
                  Offset(250, 100),
                  draggableController,
                  'ball 3',
                ),

              ],
            ),

可拖动

class MyDraggable<T> extends StatefulWidget {
  final Offset initPos;
  final MyDraggableController<T> controller;
  final T data;

  MyDraggable(this.initPos, this.controller, this.data, {Key key})
      : super(key: key);

  @override
  _MyDraggableState createState() =>
      _MyDraggableState<T>(this.initPos, this.controller, this.data);
}

class _MyDraggableState<T> extends State<MyDraggable> {
  MyDraggableController<T> controller;
  T data;
  Offset position = Offset(0.0, 0.0);

  _MyDraggableState(this.position, this.controller, this.data);



  @override
  void initState() {

    this.controller.subscribeToOnTargetCallback(onTargetCallbackHandler);

    super.initState();

    position = widget.initPos;
  }

  void onTargetCallbackHandler(T data, Offset targetPosition) {
    debugPrint("dropped inside target: " + data.toString());

    if (this.data == data) {
      debugPrint("DRAGGABLE is ACCEPTED " +
          this.data.toString() +
          " " +
          this.isOnTarget.toString());

      setState(() {
        this.position = targetPosition;
      });
    } else {
      debugPrint("DRAGGABLE is NOT ACCEPTED " +
          this.data.toString() +
          " " +
          this.isOnTarget.toString());
      if (this.position == targetPosition) {
        debugPrint(this.data.toString() + " is occupying this spot!");
      }
      setState(() {});
    }
  }

  @override
  void dispose() {
    this.controller.unSubscribeFromOnTargetCallback(onTargetCallbackHandler);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Positioned(
        left: position.dx,
        top: position.dy,
        child: Draggable<T>(
          data: this.data,
          child: CirkleWidget(this.data,0.5),
          feedback: CirkleWidget(this.data,1.2),
          childWhenDragging: new Container(),
          onDraggableCanceled: (v, f) => setState(
                () {
                  this.isOnTarget = false;
                  this.position = widget.initPos;


                },
              ),
        ));
  }
}

DragTarget

class MyDragTarget<T> extends StatefulWidget {

  final Offset inPos;
  final MyDraggableController<T> controller;
  final T data;

  MyDragTarget(this.inPos, this.controller, this.data, {Key key})
      : super(key: key);

  @override
  _MyDragTargetState createState() =>
      _MyDragTargetState(this.inPos, this.controller, this.data);
}

class _MyDragTargetState<T> extends State<MyDragTarget> {
  Offset position = Offset(0.0, 0.0);
  MyDraggableController<T> controller;
  T data;
  T currentBall;




  _MyDragTargetState(this.position, this.controller, this.data);

  @override
  void initState() {
    position = widget.inPos;
    data = widget.data;
    //this.controller.subscribeToOnTargetCallback(onTargetCallbackHandler);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    debugPrint(position.toString());

    return Positioned(
      left: position.dx-10,
      top: position.dy-10,
      child: DragTarget<T>(

        builder: (context, list, list2) {
          return Container(
            decoration: BoxDecoration(
                color: Colors.blueGrey,
                borderRadius: BorderRadius.circular(50.0)),
            height: 120,
            width: 120,

            child: Center(
              child: Text(data.toString().toUpperCase()),
            ),
          );
        },

        onWillAccept: (item){
          debugPrint("will accept");
          return true;
        },

        onAccept: (item) {
          debugPrint('TARGET accepted $item');
          //this.draggableController.onTarget(true, item);
          //debugPrint("set currentball from "+ currentBall.toString() + " to" + item.toString());
          //currentBall = item;

          this.controller.onDropped(item,this.position);
          return true;
        },
      ),
    );
  }
}

控制器

class MyDraggableController<T> {
  List<Function(T,Offset)> _targetUpdateCallbacks = new List<Function(T,Offset)>();

  //List<Function( )> _targetMoveCallbacks =  new List<Function( )>();

  MyDraggableController();

  void onDropped(T draggableData,Offset targetPosition) {
    debugPrint("dropped" + draggableData.toString());
    _targetUpdateCallbacks.forEach((f) {
      f(draggableData,targetPosition);
    });
  }

  void subscribeToOnTargetCallback(Function(T,Offset) f) {
    _targetUpdateCallbacks.add(f);
  }

  void unSubscribeFromOnTargetCallback(Function(T,Offset) f) {
    _targetUpdateCallbacks.remove(f);
  }
}

2 个答案:

答案 0 :(得分:0)

使用提供程序体系结构。

Constants.HOME_SCREEN: (BuildContext context) => ChangeNotifierProvider(
            builder: (context) => Data(), child: HomePage())

将初始化变量的getter和setter,并在setter函数内部notifyListeners()进行调用,以便侦听这些变量的Widget可以重建。

removeLastItem()方法已创建,它将从Draggable列表中删除最后一项。

removeLastItem() {
    items.removeLast();
    notifyListeners();
  }

最好的例子是Manik Gupta。 点击此链接。 https://medium.com/flutterdevs/draggable-and-drag-target-in-flutter-2513ea7c09f2

class DragTargetWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return DragTarget(onWillAccept: (data) {
      return true;
    }, onAccept: (CardItem data) {
      if (Provider.of<Data>(context).itemsList.length >= 1) {
        Provider.of<Data>(context).removeLastItem();
        Provider.of<Data>(context).changeSuccessDrop(true);
        Provider.of<Data>(context).changeAcceptedData(data);
      }

答案 1 :(得分:0)

将覆盖的Draggable小部件设为DragTarget的子级。

class _MyDragTargetState<T> extends State<MyDragTarget> {
  ...

  @override
  Widget build(BuildContext context) {
    debugPrint(position.toString());

    return Positioned(
      left: position.dx-10,
      top: position.dy-10,
      child: DragTarget<T>(

        builder: (context, list, list2) {
          return Stack(
            children: [
              Container(
                decoration: BoxDecoration(
                    color: Colors.blueGrey,
                    borderRadius: BorderRadius.circular(50.0)
                ),
                height: 120,
                width: 120,

                child: Center(
                  child: Text(data.toString().toUpperCase()
                ),
              ),

              MyDraggable(...) // Put your Draggable widgets here onwards
            ]
          );
        },
  ...
}