Flutter web - ReordorableListView,在字段之间悬停时带有 + 按钮

Flutter web - ReordorableListView with + button on hover between fields

我正在做一个 Flutter Web 项目,我们想要一个 ReordorableListView 设计。当用户悬停在两个元素之间时(然后在所选位置添加一个元素),我们还希望在列表的卡片之间有一个 "+"+" 按钮。

我试图通过在列表中包含一个小部件(它是透明的,悬停时变成 "+")来实现的。但是通过这样做,这个小部件也可以被我不想要的用户拖动。

这是一个简单的例子:

class Screen extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return ReorderableListView(
      onReorder: (int oldIndex, int newIndex) {},
      children: [
        const Card(
          child: Padding(
            padding: EdgeInsets.all(8.0),
            child: Text('Value 0'),
          ),
          key: Key('key0'),
        ),
        PlusButton(
          key: Key('plusButton'),
        ),
        const Card(
          child: Padding(
            padding: EdgeInsets.all(8.0),
            child: Text('Value 1'),
          ),
          key: Key('key1'),
        ),
        const Card(
          child: Padding(
            padding: EdgeInsets.all(8.0),
            child: Text('Value 2'),
          ),
          key: Key('key2'),
        ),
      ],
    );
  }
}

class PlusButton extends StatefulWidget {
  PlusButton({Key key}) : super(key: key);
  @override
  _PlusButtonState createState() => _PlusButtonState();
}

class _PlusButtonState extends State<PlusButton> {
  bool isHovered = false;
  @override
  Widget build(BuildContext context) {
    return MouseRegion(
      onEnter: (_) {
        print('enter');
        setState(() {
          isHovered = true;
        });
      },
      onExit: (_) {
        print('exit');
        setState(() {
          isHovered = false;
        });
      },
      child: Container(
        height: 20,
        width: 50,
        color: Colors.red, // Should be transparent, red to see it in the example
        child: isHovered
            ? IconButton(
                iconSize: 10,
                icon: const Icon(Icons.add),
                onPressed: () {},
              )
            : null,
      ),
    );
  }
}

名单:

当按钮悬停时:

如您所见,按钮也是可拖动的:

当用户将鼠标悬停在两个元素之间时,如何显示 "+"+" 按钮?

我没有找到任何合适的方法来做我想做的事。我发现的一个解决方法是在按钮的 Container 上放置一个 GestureDetector 和一个非空(但为空)onLongPress 以阻止小部件的拖动。

这是更新后的代码:

class Screen extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return ReorderableListView(
      onReorder: (int oldIndex, int newIndex) {},
      children: [
        const Card(
          child: Padding(
            padding: EdgeInsets.all(8.0),
            child: Text('Value 0'),
          ),
          key: Key('key0'),
        ),
        PlusButton(
          key: Key('plusButton'),
        ),
        const Card(
          child: Padding(
            padding: EdgeInsets.all(8.0),
            child: Text('Value 1'),
          ),
          key: Key('key1'),
        ),
        const Card(
          child: Padding(
            padding: EdgeInsets.all(8.0),
            child: Text('Value 2'),
          ),
          key: Key('key2'),
        ),
      ],
    );
  }
}

class PlusButton extends StatefulWidget {
  PlusButton({Key key}) : super(key: key);
  @override
  _PlusButtonState createState() => _PlusButtonState();
}

class _PlusButtonState extends State<PlusButton> {
  bool isHovered = false;
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onLongPress: () {}, // To prevent the drag
      child: MouseRegion(
        onEnter: (_) {
          print('enter');
          setState(() {
            isHovered = true;
          });
        },
        onExit: (_) {
          print('exit');
          setState(() {
            isHovered = false;
          });
        },
        child: Container(
          height: 20,
          width: 50,
          color: Colors.red, // Should be transparent, red to see it in the example
          child: isHovered
              ? IconButton(
                  iconSize: 10,
                  icon: const Icon(Icons.add),
                  onPressed: () {},
                )
              : null,
        ),
      ),
    );
  }
}

也许你可以看看 ReorderableListView,里面有一个 buildDefaultDragHandle 参数。通过将其设置为 false,该小部件将不再自动使项目可拖动,但它允许您使用 ReorderableDragStartListenerReorderableDelayedDragStartListener(需要长按触发拖动)包裹您的项目。

Off-topic:我也有一个类似的feature-request,但它是为了让“加号按钮”悬停在ListView的顶部,而不是在项目之间。

Flutter 不擅长通过其原始能力创建非常规布局,尝试将 SizedBox.shrink 与 OverflowBox 结合使用,它在图形上有效但 HitTest 不起作用,因为溢出部分不计算在内。