1

我对开发还很陌生,从 web 开发开始,虽然现在和 Flutter 一起工作了大约 2 个月,这意味着我仍然学到很多东西——所以请多多包涵。

我目前正在研究拖放周期表。我已经有了一个工作版本,玩家可以在正确的位置放置一个元素(只有一个 DragTarget 接受 Draggable)。但是,我现在想制作一个高级版本,其中每个 DragTarget 接受每个 Draggable 并显示已放置元素的一些信息

我的问题是: 我可以将 DraggableElementTile 放在每个“空” DragTarget 上(如我所愿),但是当我将鼠标悬停在已经“有数据”的一个 DragTargets 上时,它会将文本更改为最后添加的文本(到不同的 DragTarget)。所以 Draggable 的数据没有“绑定”到 DragTarget,但我不知道如何解决它。

我进一步知道,在这段代码中,行中的下一个元素的数据显示在 onAccept 时的 DragTarget 中。我的完整代码不会发生这种情况,也许我在这里删除了一些东西。或者它指向某人的解决方案?

附带说明:最终,将检查元素是否在表格中的位置正确,因此 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),
          ),
        ],
      ),
    );
  }
}

感谢任何有用的想法。

编辑:我想我需要将要显示的数据保存在一个新列表中,但是当我尝试实现它时仍然碰壁。

4

1 回答 1

0

我设法通过使用getElements(). Draggable然后,我可以使用inonAccept中删除的元素的数据覆盖数据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),
          ),
        ],
      ),
    );
  }
}
于 2021-02-17T08:51:01.387 回答