admin管理员组

文章数量:1278825

I want to have 2 movable widgets in a flutter's stack. I created a stack with red and blue containers, wrapped with GestureDetector and Positioned and they moved well.

when I tried to have the item that is touched on top of the other, I got a phenoment that when I try moving the red container, the blue actually moves, and in the code below, I get the first printing onPanStart:red, followed by the printing:blue is moved If i first touch the element I want to move, and only then moves it, it moves ok

note, that my original problem had a list of widgets in the stack, in this example, I narrowed it down to 2 basic elements.

why is the wrong elemnt moves?

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Draggable Containers with Z-Order',
      home: Scaffold(
        appBar: AppBar(title: Text('Draggable Containers with Z-Order')),
        body: DraggableStack(),
      ),
    );
  }
}

class DraggableStack extends StatefulWidget {
  @override
  _DraggableStackState createState() => _DraggableStackState();
}

class _DraggableStackState extends State<DraggableStack> {
  // Starting positions for the containers.
  Offset redPosition = Offset(50, 50);
  Offset bluePosition = Offset(200, 200);

  // True if red container is on top, false if blue container is on top.
  bool redOnTop = false;

  @override
  Widget build(BuildContext context) {
    // Create the red container widget.
    Widget redContainer = Positioned(
      left: redPosition.dx,
      top: redPosition.dy,
      child: GestureDetector(
        onPanStart: (_) {
          setState(() {
            redOnTop = true;
            print("onPanStart:red");
          });
        },
        onPanUpdate: (details) {
          setState(() {
            redPosition += details.delta;
            print("red is moved");
          });
        },
        child: Container(
          width: 100,
          height: 100,
          color: Colors.red,
        ),
      ),
    );

    // Create the blue container widget.
    Widget blueContainer = Positioned(
      left: bluePosition.dx,
      top: bluePosition.dy,
      child: GestureDetector(
        onPanStart: (_) {
          setState(() {
            redOnTop = false;
            print("onPanStart:blue");
          });
        },
        onPanUpdate: (details) {
          setState(() {
            bluePosition += details.delta;
            print("blue is moved");
          });
        },
        child: Container(
          width: 100,
          height: 100,
          color: Colors.blue,
        ),
      ),
    );

    // Render the Stack so that the touched container is above.
    return Stack(
      children: redOnTop
          ? [blueContainer, redContainer] // Red on top.
          : [redContainer, blueContainer], // Blue on top.
    );
  }
}

I want to have 2 movable widgets in a flutter's stack. I created a stack with red and blue containers, wrapped with GestureDetector and Positioned and they moved well.

when I tried to have the item that is touched on top of the other, I got a phenoment that when I try moving the red container, the blue actually moves, and in the code below, I get the first printing onPanStart:red, followed by the printing:blue is moved If i first touch the element I want to move, and only then moves it, it moves ok

note, that my original problem had a list of widgets in the stack, in this example, I narrowed it down to 2 basic elements.

why is the wrong elemnt moves?

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Draggable Containers with Z-Order',
      home: Scaffold(
        appBar: AppBar(title: Text('Draggable Containers with Z-Order')),
        body: DraggableStack(),
      ),
    );
  }
}

class DraggableStack extends StatefulWidget {
  @override
  _DraggableStackState createState() => _DraggableStackState();
}

class _DraggableStackState extends State<DraggableStack> {
  // Starting positions for the containers.
  Offset redPosition = Offset(50, 50);
  Offset bluePosition = Offset(200, 200);

  // True if red container is on top, false if blue container is on top.
  bool redOnTop = false;

  @override
  Widget build(BuildContext context) {
    // Create the red container widget.
    Widget redContainer = Positioned(
      left: redPosition.dx,
      top: redPosition.dy,
      child: GestureDetector(
        onPanStart: (_) {
          setState(() {
            redOnTop = true;
            print("onPanStart:red");
          });
        },
        onPanUpdate: (details) {
          setState(() {
            redPosition += details.delta;
            print("red is moved");
          });
        },
        child: Container(
          width: 100,
          height: 100,
          color: Colors.red,
        ),
      ),
    );

    // Create the blue container widget.
    Widget blueContainer = Positioned(
      left: bluePosition.dx,
      top: bluePosition.dy,
      child: GestureDetector(
        onPanStart: (_) {
          setState(() {
            redOnTop = false;
            print("onPanStart:blue");
          });
        },
        onPanUpdate: (details) {
          setState(() {
            bluePosition += details.delta;
            print("blue is moved");
          });
        },
        child: Container(
          width: 100,
          height: 100,
          color: Colors.blue,
        ),
      ),
    );

    // Render the Stack so that the touched container is above.
    return Stack(
      children: redOnTop
          ? [blueContainer, redContainer] // Red on top.
          : [redContainer, blueContainer], // Blue on top.
    );
  }
}
Share Improve this question asked Feb 23 at 21:35 Ravid GabbayRavid Gabbay 234 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 0

Your problem is the stack, when you tap on a GestureDetector the stack rebuilds and that's why it's having conflicts

    // Render the Stack so that the touched container is above.
    return Stack(
      children: redOnTop
          ? [blueContainer, redContainer] // Red on top.
          : [redContainer, blueContainer], // Blue on top.
    );

If you try setting this logic in each, you will see that when you try and drag it, they don't move:

        onPanUpdate: (details) {
          if(!redOnTop){
            setState(() {
              bluePosition += details.delta;
              print("blue is moved");
            });
          }
        },

You may have to use something different than the stack with the variable, to avoid rebuilds that conflict with you inner components.

Try this package to change the z-index dinamically https://pub.dev/packages/indexed

I have found the solution. Adding key:Valuekey() to the Positioned elements solved the problem. the key should be such that it is fixed for the container, and not based on the index in the stack (which changes all the time) it seems that without the key, the rebuild of the stack causes the gesturedetection to be linked with the index in the stack, and not with the correct element

    import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Draggable Containers with Z-Order',
      home: Scaffold(
        appBar: AppBar(title: Text('Draggable Containers with Z-Order')),
        body: DraggableStack(),
      ),
    );
  }
}

class DraggableStack extends StatefulWidget {
  @override
  _DraggableStackState createState() => _DraggableStackState();
}

class _DraggableStackState extends State<DraggableStack> {
  // Starting positions for the containers.
  Offset redPosition = Offset(50, 50);
  Offset bluePosition = Offset(200, 200);

  // True if red container is on top, false if blue container is on top.
  bool redOnTop = false;

  @override
  Widget build(BuildContext context) {
    // Create the red container widget.
    Widget redContainer = Positioned(
      key: ValueKey(1),
      left: redPosition.dx,
      top: redPosition.dy,
      child: GestureDetector(
        onPanStart: (_) {
          setState(() {
            redOnTop = true;
            print("onPanStart:red");
          });
        },
        onPanUpdate: (details) {
          setState(() {
            redPosition += details.delta;
            print("red is moved");
          });
        },
        child: Container(
          width: 100,
          height: 100,
          color: Colors.red,
        ),
      ),
    );

    // Create the blue container widget.
    Widget blueContainer = Positioned(
      key: ValueKey(2),
      left: bluePosition.dx,
      top: bluePosition.dy,
      child: GestureDetector(
        onPanStart: (_) {
          setState(() {
            redOnTop = false;
            print("onPanStart:blue");
          });
        },
        onPanUpdate: (details) {
          setState(() {
            bluePosition += details.delta;
            print("blue is moved");
          });
        },
        child: Container(
          width: 100,
          height: 100,
          color: Colors.blue,
        ),
      ),
    );

    // Render the Stack so that the touched container is above.
    return Stack(
      children: redOnTop
          ? [blueContainer, redContainer] // Red on top.
          : [redContainer, blueContainer], // Blue on top.
    );
  }
}


 

本文标签: flutterGestureDetector causes move of the wrong item in stackStack Overflow