Ad

Flutter Web - ReordorableListView With + Button On Hover Between Fields

- 1 answer

I am working on a Flutter Web project, and we would like to have a ReordorableListView design. We also would like to have a "+" button between the cards of the list when the user hovers between 2 elements (to then add an element at the chosen position).

I tried to do it by including in the list a widget (which is transparent and becomes a "+" when hovered). But by doing this, this widget is also draggable by the user which I don't want.

Here is a simple example:

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,
      ),
    );
  }
}

The list :

enter image description here

When the button is hovered:

enter image description here

As you can see, the button is also draggable:

enter image description here

How can I display a "+" button between 2 elements when the user hovers there ?

Ad

Answer

I didn't find any proper way to do what I wanted to do. A workaround I found was to put a GestureDetector on the Container of the button with a non-null (but empty) onLongPress to block the drag of the widget.

Here is the updated code:

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,
        ),
      ),
    );
  }
}
Ad
source: stackoverflow.com
Ad