【问题标题】:Flutter: DragTarget shows wrong data when hovering over it with a new DraggableFlutter:使用新的 Draggable 将鼠标悬停在 DragTarget 上时,它会显示错误的数据
【发布时间】:2021-02-03 20:08:34
【问题描述】:

我对开发还很陌生,刚开始是 web 开发,虽然现在使用 Flutter 大约 2 个月了,这意味着我还在学习大量 - 所以请多多包涵。

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

我的问题是: 我可以将 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),
          ),
        ],
      ),
    );
  }
}

感谢任何有用的想法。

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

【问题讨论】:

    标签: flutter dart draggable dragtarget


    【解决方案1】:

    我设法通过使用getElements() 中的模型创建初始 elementData 的深层副本(称为 elementDataCopy)使其工作。然后我可以使用来自onAcceptDraggable 中的Draggable 的删除元素的数据覆盖数据,从而导致预期的行为:

    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),
              ),
            ],
          ),
        );
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多