[desktop_drop] The function of `onDrag*` would execute multiple times when current widget is not root route
When using Navigator.of(context).push(...) to another route, desktop_drop would execute multiple times. Here is my test code
import 'package:flutter/material.dart';
import 'package:desktop_drop/desktop_drop.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
useMaterial3: true,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home Page"),
),
body: MyDragWidget(
title: "home",
child: Center(
child: TextButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const MySecondPage()),
);
},
child: Text("to second page"),
),
),
),
);
}
}
class MySecondPage extends StatelessWidget {
const MySecondPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Page"),
),
body: MyDragWidget(
title: "second",
child: Center(
child: TextButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const MyThirdPage()),
);
},
child: Text("to third page"),
),
),
),
);
}
}
class MyThirdPage extends StatelessWidget {
const MyThirdPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Third Page"),
),
body: MyDragWidget(
title: "third",
child: Center(
child: Text("third page"),
),
),
);
}
}
class MyDragWidget extends StatefulWidget {
const MyDragWidget({super.key, required this.title, required this.child});
final String title;
final Widget child;
@override
State<MyDragWidget> createState() => _MyDragWidgetState();
}
class _MyDragWidgetState extends State<MyDragWidget> {
bool _dragging = false;
@override
Widget build(BuildContext context) {
return DropTarget(
onDragDone: (detail) async {
print("${widget.title} done");
},
onDragEntered: (detail) {
setState(() {
_dragging = true;
});
print("${widget.title} entered");
},
onDragExited: (detail) {
setState(() {
_dragging = false;
});
print("${widget.title} exited");
},
child: _dragging
? Container(
width: double.infinity,
height: double.infinity,
color: Colors.black.withOpacity(0.2),
child: Center(
child: Text("Drag Files"),
),
)
: widget.child,
);
}
}
Reproduce Steps
When in MyHomePage,the output is
flutter: home entered
flutter: home exited
flutter: home done
When in MySecondPage,the output is
flutter: home entered
flutter: second entered
flutter: home exited
flutter: home done
flutter: second exited
flutter: second done
When in MyThirdPage,the output is
flutter: home entered
flutter: second entered
flutter: third entered
flutter: home exited
flutter: home done
flutter: second exited
flutter: second done
flutter: third exited
flutter: third done
Expected behavior
The function of onDrag* only execute once whatever current widget is root route or not.
Version (please complete the following information):
- Flutter Version: 3.27.3
- OS: MacOS
- plugin: desktop_drop: ^0.5.0
i facing the same issue, and the way i avoid this is create a enable variable, then disable that variable when you push a new route
I'm also facing this very issue.
here is how i handle this issue:
in my class define a drop state variable:
bool _dropEnable = true; ... some code ...
Widget build(BuildContext context) {
return Scaffold( body: DropTarget( enable: _dropEnable, ... some UI code... } .. then some UI call this function to show new screen void showAddScreen() async{ setState(() => _dropEnable = false); .. await Navigator.push ... setState(() => _dropEnable = true); }
and this works perfectly fine to me
I was able to workaround it by creating the following component that uses the visibility_detector package:
import 'package:desktop_drop/desktop_drop.dart';
import 'package:flutter/material.dart';
import 'package:visibility_detector/visibility_detector.dart';
// This class fixes the following issue of desktop_drop's package:
// - https://github.com/MixinNetwork/flutter-plugins/issues/389
class FixedDropTarget extends StatefulWidget {
const FixedDropTarget({
super.key,
required this.child,
this.onDragEntered,
this.onDragExited,
this.onDragDone,
this.onDragUpdated,
this.enable = true,
});
final Widget child;
final OnDragCallback<DropEventDetails>? onDragEntered;
final OnDragCallback<DropEventDetails>? onDragExited;
final OnDragCallback<DropEventDetails>? onDragUpdated;
final OnDragDoneCallback? onDragDone;
final bool enable;
@override
State<FixedDropTarget> createState() => _FixedDropTargetState();
}
class _FixedDropTargetState extends State<FixedDropTarget> {
final _visibilityDetectorKey = UniqueKey();
bool _isVisible = false;
@override
Widget build(BuildContext context) {
return VisibilityDetector(
key: _visibilityDetectorKey,
child: DropTarget(
onDragEntered: widget.onDragEntered,
onDragExited: widget.onDragExited,
onDragDone: widget.onDragDone,
onDragUpdated: widget.onDragUpdated,
enable: widget.enable && _isVisible,
child: widget.child,
),
onVisibilityChanged: (info) {
final newIsVisible = info.visibleFraction > 0;
if (newIsVisible != _isVisible) {
setState(() {
_isVisible = newIsVisible;
});
}
},
);
}
}