I'm rather new to development, and started off with web dev, though now work with Flutter for about 2 months now, which means I still learn tons - so please, bear with me.
I'm currently working on a drag & drop periodic table. I already got a working version where the player can drop an element at the correct position (only one DragTarget accepts the Draggable). However, I now want to make an advanced version, where each DragTarget accepts each Draggable and shows some information of the dropped element.
My problem is: I can drop the DraggableElementTile on each "empty" DragTarget (as I want to), but when I hover over one of the DragTargets that already "have data", it changes the text to the one that was added last (to a different DragTarget). So the data of the Draggable is not "tied" to the DragTarget, but I cannot find out how to solve it.
I know that further, in this code, the data of the next element in line is shown in the DragTarget upon onAccept. It doesn't happen with my full code, maybe I deleted something here. Or it points someone to the solution?
As a side note: Eventually, there'll be a check, if the element has the correct position in the table or not, so the DragTarget needs to carry the information of the correct setup (as in my initial version).
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Draggable & DragTarget',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List elementData = [
{
"key": "1x1",
"atomicNumber": 1,
"element": "Wasserstoff",
"symbol": "H",
"group": 1,
"period": 1,
"accepting": false,
"successfulDrop": false,
"correctDrop": false
},
{
"key": "1x2",
"atomicNumber": 3,
"element": "Lithium",
"symbol": "Li",
"group": 1,
"period": 2,
"accepting": false,
"successfulDrop": false,
"correctDrop": false
},
{
"key": "1x3",
"atomicNumber": 11,
"element": "Natrium",
"symbol": "Na",
"group": 1,
"period": 3,
"accepting": false,
"successfulDrop": false,
"correctDrop": false
},
{
"key": "1x4",
"atomicNumber": 19,
"element": "Kalium",
"symbol": "K",
"group": 1,
"period": 4,
"accepting": false,
"successfulDrop": false,
"correctDrop": false
}
];
int j = 0;
List<Widget> _elements;
List shuffledElements;
int tableRows = 4;
int tableCols = 1;
String key;
int index;
var tmpElement;
bool accepting = false;
bool successfulDrop = false;
bool correctDrop = false;
List shuffleElements() {
var random = Random();
shuffledElements = List.from(elementData);
for (var i = shuffledElements.length - 1; i > 0; i--) {
var n = random.nextInt(i + 1);
var temp = shuffledElements[i];
shuffledElements[i] = shuffledElements[n];
shuffledElements[n] = temp;
}
return shuffledElements;
}
void nextElement() {
setState(() {
if (j < shuffledElements.length - 1) {
j++;
} else {}
});
}
List<Widget> getElements() {
if (_elements != null) {
return _elements;
}
_elements = [];
for (var j = 0; j < tableCols; j++) {
for (var i = 0; i < tableRows; i++) {
key = '${j + 1}x${i + 1}';
index = elementData.indexWhere((e) => e.containsValue(key));
if (!index.isNegative) {
tmpElement = elementData[index];
_elements.add(elementDragTarget(tmpElement));
} else {}
}
}
return _elements;
}
@override
void initState() {
shuffleElements();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white38,
appBar: AppBar(title: Text('Drag and Drop')),
body: Column(
children: [
Container(
color: Colors.white38,
height: MediaQuery.of(context).size.height * 0.7,
child: GridView.count(
crossAxisCount: tableRows,
scrollDirection: Axis.horizontal,
children: getElements(),
),
),
Draggable(
data: shuffledElements[j],
child: DraggableElementTile(
shuffledElements: shuffledElements,
j: j,
),
feedback: DraggableElementTile(
shuffledElements: shuffledElements,
j: j,
),
),
],
),
);
}
//problem: if I hover over tiles that already show data
// it changes to last data
Widget elementDragTarget(tmpElement) {
return DragTarget(
onWillAccept: (data) {
if (tmpElement['successfulDrop'] == true) {
tmpElement['accepting'] = false;
return false;
} else {
setState(() {
tmpElement['accepting'] = true;
});
return true;
}
},
onAccept: (data) {
setState(() {
tmpElement['successfulDrop'] = true;
if (shuffledElements[j]["atomicNumber"] ==
tmpElement['atomicNumber']) {
tmpElement['correctDrop'] = true;
tmpElement['accepting'] = false;
} else {
tmpElement['correctDrop'] = false;
tmpElement['accepting'] = false;
}
});
nextElement();
},
onLeave: (data) {
setState(() {
tmpElement['accepting'] = false;
});
return false;
},
builder: (context, acceptedData, rejectedData) {
return buildElementTileInGrid(tmpElement);
},
);
}
//show in grid onAccept
Container buildElementTileInGrid(tmpElement) {
accepting = tmpElement['accepting'];
successfulDrop = tmpElement['successfulDrop'];
correctDrop = tmpElement['correctDrop'];
return Container(
padding: EdgeInsets.all(4),
margin: EdgeInsets.all(4),
decoration: BoxDecoration(
border: Border.all(
width: 4,
color: accepting == true ? Colors.teal : Colors.transparent,
),
color: Colors.white38,
),
child: successfulDrop == true
? Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(shuffledElements[j]['atomicNumber'].toString()),
Text(shuffledElements[j]['symbol']),
],
)
: Container(),
);
}
}
//draggable
class DraggableElementTile extends StatelessWidget {
const DraggableElementTile({
Key key,
@required this.shuffledElements,
@required this.j,
}) : super(key: key);
final List shuffledElements;
final int j;
@override
Widget build(BuildContext context) {
return Container(
color: Colors.teal,
padding: EdgeInsets.all(12),
margin: EdgeInsets.all(8),
height: 100,
width: 80,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
shuffledElements[j]['symbol'],
style: TextStyle(fontSize: 14),
),
Text(
shuffledElements[j]['element'],
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 14),
),
],
),
);
}
}
Thankful for any helpful ideas.
edit: I think I'd need to save the data I want to show in a new list or so, but still hit a wall when I try to implement it.