I am trying to create a game in Flutter where items are dragged and dropped into targets. As soon as an item is dropped on the target it will be repositioned at the target.
This works up untill the next item being dropped on the original target. The onAccept is not triggered anymore because the first item is 'on top' of the target.
What is the best way to work around this?
I basically rewrote this question/answer : How to update Draggable child when entering DragTarget in Flutter?
Part of Main:
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',
),
],
),
Draggable
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;
},
),
);
}
}
controller
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);
}
}