Flutter:从Drawer拖拽Draggable到Scaffold主体区域的问题
解决Flutter中Drawer内Draggable拖拽到主区域的问题
问题根源在于:Drawer是一个独立的路由页面,调用Navigator.pop会直接销毁Drawer的上下文,而Draggable的初始渲染依赖该上下文,导致拖拽流程中断。
下面提供两种可行的解决方案:
方案1:延迟关闭Drawer(简单易实现)
利用Flutter的帧调度机制,在拖拽启动后的下一帧再关闭Drawer,此时Draggable的拖拽状态已经完成初始化,不再依赖Drawer的上下文。
修改代码中的onDragStarted回调:
onDragStarted: () { // 在下一帧执行关闭Drawer操作,避免中断拖拽初始化 SchedulerBinding.instance.addPostFrameCallback((_) { Navigator.pop(context); }); },
需要在文件顶部导入调度器依赖:
import 'package:flutter/scheduler.dart';
方案2:使用Overlay承载拖拽反馈(更稳定)
将拖拽的feedback组件放到全局Overlay中,完全脱离Drawer的上下文,这样关闭Drawer后,反馈仍能正常显示,拖拽流程不受影响。
修改后的完整代码
import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { String droppedItem = ""; OverlayEntry? _overlayEntry; final GlobalKey _overlayKey = GlobalKey(); Offset _dragOffset = Offset.zero; // 创建Overlay中的反馈组件 OverlayEntry _createOverlayFeedback(Widget feedback) { return OverlayEntry( builder: (context) => Positioned( left: _dragOffset.dx - 25, top: _dragOffset.dy - 25, child: feedback, ), ); } @override Widget build(BuildContext context) { return Scaffold( key: _overlayKey, appBar: AppBar( title: const Text('Drag and Drop Example'), ), body: Center( child: Container( width: 200, height: 200, color: Colors.grey, child: DragTarget<String>( onWillAcceptWithDetails: (details) { setState(() { droppedItem = details.data; }); return true; }, onLeave: (data) { _overlayEntry?.remove(); _overlayEntry = null; }, onAcceptWithDetails: (details) { _overlayEntry?.remove(); _overlayEntry = null; }, builder: (context, candidateData, rejectedData) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Icon( Icons.cloud_upload, size: 50, color: Colors.blue, ), Text( 'Drop Here : \n $droppedItem', style: const TextStyle(fontSize: 20), ), ], ); }, ), ), ), drawer: Drawer( child: ListView( children: <Widget>[ Draggable<String>( data: 'Item 1', feedback: const SizedBox.shrink(), // 原反馈不再显示 childWhenDragging: const ListTile( title: Text('Item 1'), leading: Icon(Icons.drag_handle), ), child: const ListTile( title: Text('Item 1'), leading: Icon(Icons.drag_handle), ), onDragStarted: () { // 记录初始拖拽位置 RenderBox box = context.findRenderObject() as RenderBox; _dragOffset = box.localToGlobal(Offset.zero); // 插入Overlay反馈 _overlayEntry = _createOverlayFeedback( Container( color: Colors.deepOrange, height: 50, width: 50, child: const Icon(Icons.directions_run), ), ); Overlay.of(_overlayKey.currentContext!)?.insert(_overlayEntry!); // 关闭Drawer Navigator.pop(context); }, onDragUpdate: (details) { // 更新拖拽位置 setState(() { _dragOffset = details.globalPosition; }); _overlayEntry?.markNeedsBuild(); }, onDragEnd: (details) { _overlayEntry?.remove(); _overlayEntry = null; }, ), Draggable<String>( data: 'Item 2', feedback: const SizedBox.shrink(), childWhenDragging: const ListTile( title: Text('Item 2'), leading: Icon(Icons.drag_handle), ), child: const ListTile( title: Text('Item 2'), leading: Icon(Icons.drag_handle), ), onDragStarted: () { RenderBox box = context.findRenderObject() as RenderBox; _dragOffset = box.localToGlobal(Offset.zero); _overlayEntry = _createOverlayFeedback( Container( color: Colors.deepOrange, height: 50, width: 50, child: const Icon(Icons.directions_run), ), ); Overlay.of(_overlayKey.currentContext!)?.insert(_overlayEntry!); Navigator.pop(context); }, onDragUpdate: (details) { setState(() { _dragOffset = details.globalPosition; }); _overlayEntry?.markNeedsBuild(); }, onDragEnd: (details) { _overlayEntry?.remove(); _overlayEntry = null; }, ), ], ), ), ); } }
内容的提问来源于stack exchange,提问作者Stefan van der Lee
相关产品推荐
相关产品推荐

