flutter-plugins icon indicating copy to clipboard operation
flutter-plugins copied to clipboard

1 dropping file could reach on 2 DropTarget in IndexedStack

Open shawnclovie opened this issue 1 year ago • 2 comments

My app contains 2 bottom tab and use IndexedStack hold content widget for them.

class MyApp extends StatefulWidget {...}
class _MyAppState extends State<MyApp> {
  var selectedIndex = 0;
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: IndexedStack(index: selectedTabIndex, children: [DropView(1), DropView(2)]),
        bottomNavigationBar: BottomNavigationBar(
          currentIndex: selectedTabIndex,
          items: [BottomNavigationBarItem(label: "1"), BottomNavigationBarItem(label: "2")],
          onTap: (index) => setState(() => selectedTabIndex = index),
        ),
    );
  }
}

class DropView extends StatelessWidget {
  final int index;
  DropView(this.index);
  Widget build(BuildContext context) {
    return DropTarget(
      onDragEntered: (details) => print("$index drag enter"),
      onDragDone: (details) => print("$index drag done"),
      child: Container(child: Text("[$index] drop file here")),
    );
  }
}

Reproduce Steps

Steps to reproduce the behavior:

  1. Run the app on my mac
  2. Drag a file or folder into app
  3. Either tab 1 or tab 2 selected, both "1 drag enter" and "2 drag enter" would printed in debug console, even onDragDone would be same issue.

Version (please complete the following information):

  • Flutter Version: 3.19.3
  • OS: macOS
  • plugin: desktop_drop: 0.4.4

shawnclovie avatar Apr 22 '24 11:04 shawnclovie

related to #2

You can disable the DropTarget when it became invisble.

More description in here: https://github.com/MixinNetwork/flutter-plugins/issues/2#issuecomment-903203464

boyan01 avatar Apr 23 '24 09:04 boyan01

If I should set enable as true, the outer widget should pass "which page is displaying" to each children, and them should pass the state into any child of them. But they cannot known which child need it, most these state would not used. @boyan01

shawnclovie avatar Apr 23 '24 09:04 shawnclovie

I couldn't find a pretty solution for cases where the IndexedStack and DropTarget are in separate widgets. So I decided to wrap all IndexedStack children in an inherited widget that knows whether it is visible or not. Then the DropTarget is enabled, based on that information. Would be nice if flutter had a built in way for similar widgets, that can silently hide their children.

Rough example:

// usae in IndexedStack
IndexedStack(
  index: selectedIndex,
  children: [
    for (var (i, child) in stackChildren.indexed)
      IndexedStackIsVisible(
        isVisible: i == selectedIndex,
        child: child,
      )
  )
)

// usage in DropTarget
// ModalRoute.of(context)!.isCurrent is to avoid having conflicts with dialogs
DropTarget(
  enable: ModalRoute.of(context)!.isCurrent && IndexedStackIsVisible.of(context) != false,
  // ...
)

// IndexedStackIsVisible definition
class IndexedStackIsVisible extends InheritedWidget {
  final bool isVisible;

  const IndexedStackIsVisible({
    super.key,
    required this.isVisible,
    required super.child,
  });

  static bool? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<IndexedStackIsVisible>()?.isVisible;
  }

  @override
  bool updateShouldNotify(IndexedStackIsVisible oldWidget) {
    return oldWidget.isVisible != isVisible;
  }
}

ArthurHeitmann avatar Sep 03 '24 20:09 ArthurHeitmann

This should be a workaround.

shawnclovie avatar Sep 04 '24 10:09 shawnclovie