You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter实现Material组件拖拽至容器并计分的方案咨询

解决方案

一、修复字母显示截断问题

现有Emoji组件中,固定高度的容器与大字号文本冲突导致截断,调整如下:

class Emoji extends StatelessWidget {
  const Emoji({Key? key, required this.content}) : super(key: key);

  final String content;

  @override
  Widget build(BuildContext context) {
    return Material(
      color: Colors.transparent,
      child: Container(
        alignment: Alignment.center,
        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
        decoration: content.isNotEmpty 
            ? BoxDecoration(
                border: Border.all(color: Colors.grey),
                borderRadius: BorderRadius.circular(8),
              )
            : null,
        child: Text(
          content,
          style: const TextStyle(color: Colors.black, fontSize: 32),
          overflow: TextOverflow.visible,
        ),
      ),
    );
  }
}

二、简化代码结构,收拢全局变量

将分散的全局变量移入State类,避免状态混乱:

class _DragDropState extends State<DragDrop> {
  final FlutterTts flutterTts = FlutterTts();
  final List<String> words = ["h", "c", "d", "w", "c", "f"];
  String question = "Listen to this letter sound and then drag all the letter whose sound you hear into the bucket.";
  bool changeButton = false;
  final Map<String, bool> score = {};
  String word_1 = "";
  final Map<String, Color> choices = {
    'h': Colors.green,
    'c': Colors.blue,
    'd': Colors.orange,
    'w': Colors.purple,
    'f': Colors.teal,
  };
  int seed = 0;

  Future<void> speak(String word) async {
    await flutterTts.setLanguage("en-US");
    await flutterTts.setPitch(1);
    await flutterTts.setVolume(1.0);
    await flutterTts.speak(word);
  }

  Future<void> moveToQ2() async {
    setState(() {
      changeButton = true;
    });
    await Navigator.pushNamed(context, MyRoutes.speakQ);
  }

  // 原build方法保留,调用内部方法替代全局方法
}

三、完善拖拽计分逻辑

给DragTarget添加onAccept回调,完成计分状态更新:

Widget _buildDragTarget(String targetLetter) {
  return DragTarget<String>(
    builder: (BuildContext context, List<String?> incoming, List rejected) {
      if (score[targetLetter] == true) {
        return Container(
          color: Colors.white,
          child: const Text('Correct!', style: TextStyle(fontSize: 18)),
          alignment: Alignment.center,
          height: 80,
          width: 200,
          decoration: BoxDecoration(border: Border.all(color: Colors.green, width: 2)),
        );
      } else {
        return Container(
          color: choices[targetLetter],
          height: 80,
          width: 200,
          alignment: Alignment.center,
          child: Text(targetLetter, style: TextStyle(fontSize: 24, color: Colors.white)),
        );
      }
    },
    onWillAccept: (data) => data == targetLetter,
    onAccept: (data) {
      setState(() {
        score[data] = true;
        // 可在此扩展累计分数逻辑,比如添加scoreCount变量统计正确数量
      });
    },
    onLeave: (data) {},
  );
}

四、扩展支持图片和Material按钮拖拽

1. 可拖拽图片组件

class DraggableImage extends StatelessWidget {
  const DraggableImage({Key? key, required this.imagePath, required this.data}) : super(key: key);

  final String imagePath;
  final String data;

  @override
  Widget build(BuildContext context) {
    return Draggable<String>(
      data: data,
      child: (score[data] == true) 
          ? const Icon(Icons.check, size: 40, color: Colors.green)
          : Image.asset(
              imagePath,
              width: 60,
              height: 60,
              fit: BoxFit.cover,
            ),
      feedback: Image.asset(
        imagePath,
        width: 60,
        height: 60,
        fit: BoxFit.cover,
      ),
      childWhenDragging: const SizedBox(width: 60, height: 60),
    );
  }
}

2. 可拖拽Material按钮组件

class DraggableButton extends StatelessWidget {
  const DraggableButton({Key? key, required this.label, required this.data}) : super(key: key);

  final String label;
  final String data;

  @override
  Widget build(BuildContext context) {
    return Draggable<String>(
      data: data,
      child: (score[data] == true) 
          ? const Icon(Icons.check, size: 40, color: Colors.green)
          : ElevatedButton(
              onPressed: () {},
              child: Text(label),
            ),
      feedback: ElevatedButton(
        onPressed: () {},
        child: Text(label),
      ),
      childWhenDragging: const SizedBox(width: 100, height: 40),
    );
  }
}

3. 页面中集成多类型拖拽组件

在原字母拖拽的Row中扩展:

Row(
  mainAxisAlignment: MainAxisAlignment.spaceAround,
  crossAxisAlignment: CrossAxisAlignment.end,
  children: [
    // 字母拖拽
    ...words.map((letter) {
      return Draggable<String>(
        data: letter,
        child: Emoji(content: score[letter] == true ? '✅' : letter),
        feedback: Emoji(content: letter),
        childWhenDragging: const Emoji(content: ''),
      );
    }).toList(),
    // 图片拖拽示例
    DraggableImage(imagePath: 'assets/images/apple.png', data: 'apple'),
    // 按钮拖拽示例
    DraggableButton(label: 'Submit', data: 'submit'),
  ],
),

五、其他优化

  • 移除未使用的AudioPlayer实例
  • 给拖拽目标容器添加边框,增强视觉引导
  • 调整布局间距,避免滚动视图内元素溢出

内容的提问来源于stack exchange,提问作者Daniyal Haider

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 09:54:33