Flutter: DragTarget показывает неверные данные при наведении на него нового Draggable
Я новичок в разработке и начал с веб-разработки, хотя сейчас работаю с Flutter около 2 месяцев, а это значит, что я все еще учусь тоннам - так что, пожалуйста, терпите меня.
В настоящее время я работаю над периодической таблицей с перетаскиванием. У меня уже есть рабочая версия, в которой игрок может бросить элемент в правильное положение (только один DragTarget принимает Draggable). Тем не менее, теперь я хочу сделать расширенную версию, где каждый DragTarget принимает каждый Draggable и показывают некоторые данные о упали элемент.
Моя проблема: я могу отбросить DraggableElementTile на каждый «пустой» DragTarget (как я хочу), но когда я нахожу курсор на один из DragTarget, который уже «имеет данные», он меняет текст на тот, который был добавлен последним ( на другой DragTarget). Таким образом, данные Draggable не «привязаны» к DragTarget, но я не могу понять, как это решить.
Я знаю, что далее в этом коде данные следующего элемента в строке отображаются в DragTarget при onAccept. С моим полным кодом этого не происходит, может я что-то тут удалил. Или это указывает кому-то на решение?
В качестве побочного примечания: в конце концов, будет проверяться, имеет ли элемент правильную позицию в таблице, поэтому DragTarget должен нести информацию о правильной настройке (как в моей первоначальной версии).
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),
),
],
),
);
}
}
Благодарен за любые полезные идеи.
edit: я думаю, что мне нужно сохранить данные, которые я хочу показать в новом списке или около того, но все равно упираюсь в стену, когда я пытаюсь реализовать его.
1 ответ
Мне удалось заставить его работать, создав глубокую копию (названную elementDataCopy) исходного elementData, используя модель в
getElements()
. Затем я мог бы перезаписать данные данными отброшенного элемента из
Draggable
в
onAccept
из
DragTarget
, что приводит к ожидаемому поведению:
import 'dart:math';
import 'package:flutter/cupertino.dart';
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 ElementModel {
ElementModel({
this.key,
this.atomicNumber,
this.element,
this.symbol,
this.group,
this.period,
this.droppedKey,
this.accepting,
this.successfulDrop,
this.correctDrop,
this.answer,
});
String key;
int atomicNumber;
String element;
String symbol;
int group;
int period;
String droppedKey = '';
bool accepting = false;
bool successfulDrop = false;
bool correctDrop = false;
String answer = '';
}
class _MyHomePageState extends State<MyHomePage> {
List elementData = [
{
"key": "1x1",
"atomicNumber": 1,
"element": "Wasserstoff",
"symbol": "H",
"group": 1,
"period": 1
},
{
"key": "1x2",
"atomicNumber": 3,
"element": "Lithium",
"symbol": "Li",
"group": 1,
"period": 2
},
{
"key": "1x3",
"atomicNumber": 11,
"element": "Natrium",
"symbol": "Na",
"group": 1,
"period": 3
},
{
"key": "1x4",
"atomicNumber": 19,
"element": "Kalium",
"symbol": "K",
"group": 1,
"period": 4
}
];
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;
var droppedItem;
List droppedItems = [];
int droppedItemIndex;
List shuffledElementsCopy;
List elementDataCopy;
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;
}
elementDataCopy = elementData
.map((element) => ElementModel(
key: element['key'],
atomicNumber: element['atomicNumber'],
element: element['element'],
symbol: element['symbol'],
group: element['group'],
period: element['period'],
accepting: element['accepting'],
successfulDrop: element['successfulDrop'],
correctDrop: element['correctDrop'],
droppedKey: element['droppedKey'],
answer: element['answer'],
))
.toList();
_elements = [];
for (var c = 0; c < tableCols; c++) {
for (var r = 0; r < tableRows; r++) {
key = '${c + 1}x${r + 1}';
index = elementDataCopy.indexWhere((e) => e.key.contains(key));
if (!index.isNegative) {
tmpElement = elementDataCopy[index];
_elements.add(elementDragTarget(tmpElement));
} else {}
}
}
return _elements;
}
@override
void initState() {
shuffleElements();
shuffledElementsCopy = List.from(shuffledElements);
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,
),
childWhenDragging: Container(
margin: EdgeInsets.all(8),
height: 100,
width: 80,
color: Colors.blueGrey,
),
),
],
),
);
}
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;
shuffledElementsCopy[j]['answer'] = 'correct';
} else {
tmpElement.correctDrop = false;
tmpElement.accepting = false;
shuffledElementsCopy[j]['answer'] = 'wrong';
}
tmpElement.droppedKey = shuffledElements[j]['key'] + 'dropped';
shuffledElementsCopy[j]['droppedKey'] = tmpElement.droppedKey;
droppedItems.add(shuffledElements[j]);
droppedItemIndex = droppedItems.indexWhere(
(e) => e['droppedKey'] == shuffledElements[j]['key'] + 'dropped');
droppedItem = droppedItems[droppedItemIndex];
tmpElement.symbol = droppedItem['symbol'];
tmpElement.atomicNumber = droppedItem['atomicNumber'];
tmpElement.element = droppedItem['element'];
tmpElement.group = droppedItem['group'];
tmpElement.period = droppedItem['period'];
tmpElement.answer = droppedItem['answer'];
});
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(tmpElement.atomicNumber.toString()),
Text(tmpElement.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),
),
],
),
);
}
}