zon7
zon7

Reputation: 539

Flutter Stack change depth

I have a Stack with 3 Positioned widgets with a GestureDetector as a child. I'm able to drag them, but I also want to bring the clicked one to the front as soon as you click it. I've tried lifting up a call to change state in the parent widget, but that does also exchange the afected widgets position.

Here is the full sample code (it's the body in a Scaffold. Any help will be appreciated

class DragBox extends StatefulWidget {
  final Offset startPosition;
  final Color color;
  final String label;
  final Function bringToTop;

  DragBox({
    this.startPosition, 
    this.color: const Color.fromRGBO(255, 255, 255, 1.0),
    this.label,
    this.bringToTop
  });

  @override
  DragBoxState createState() => DragBoxState();
}

class DragBoxState extends State<DragBox> {
  Offset position;
  Offset _startPos;

  @override
  void initState() {
    position = widget.startPosition;

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Positioned(
        left: position.dx,
        top: position.dy,
        child: GestureDetector(
            onScaleStart: (details) {
              //Store start conditions
              _startPos = details.focalPoint;

              widget.bringToTop(widget);
            },
            onScaleUpdate: (scaleDetails) {
              setState(() {
                position += scaleDetails.focalPoint - _startPos;
                _startPos = scaleDetails.focalPoint;
              });
            },
            child: _buildPart()
            ));
  }

  Widget _buildPart() {
    return Container(
      width: 100.0,
      height: 100.0,
      decoration: BoxDecoration(
          border: Border.all(color: Color.fromARGB(255, 0, 0, 0), width: 1.0),
          color: widget.color),
      child: Text(
        widget.label,
        style: TextStyle(
          color: Color.fromRGBO(255, 255, 255, 1.0),
          //decoration: TextDecoration.none,
          fontSize: 15.0,
        ),
      ),
    );
  }
}

class WorkTable extends StatefulWidget {
  @override
  WorkTableState createState() => WorkTableState();
}

class WorkTableState extends State<WorkTable> {
  List<DragBox> dragParts = [];

  @override
  void initState() {
    dragParts = [];

    //dragParts = [];
    dragParts.add(DragBox(
      startPosition: Offset(0.0, 0.0),
      color: Colors.red,
      label: "Box1",
      bringToTop: this.bringToTop,
    ));

    dragParts.add(DragBox(
      startPosition: Offset(50.0, 50.0),
      color: Colors.red,
      label: "Box2",
      bringToTop: this.bringToTop,
    ));

    dragParts.add(DragBox(
      startPosition: Offset(100.0, 100.0),
      color: Colors.blue,
      label: "Box3",
      bringToTop: this.bringToTop,
    ));


    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    for(DragBox d in dragParts){
      print(d.label);
    }
    return Stack(
      children: dragParts,
    );
  }

  void bringToTop(Widget widget) {
    setState(() {
      dragParts.remove(widget);
      dragParts.add(widget);
    });
  }
}

The strange part happens here

  void bringToTop(Widget widget) {
    setState(() {
      dragParts.remove(widget);
      dragParts.add(widget);
    });
  }

Instead of changing only depth, positions are also exchanged

Upvotes: 8

Views: 4648

Answers (2)

xu yun
xu yun

Reputation: 1

https://i.sstatic.net/TeeQZ.gif

My knowledge column: https://zhuanlan.zhihu.com/p/46982762

Example:

class TestBringToFrontPage extends StatefulWidget {
  TestBringToFrontPage({Key key}) : super(key: key);

  @override
  State<TestBringToFrontPage> createState() {
    return TestBringToFrontState();
  }
}

class BoxData {
  final double width;
  final double height;
  final Offset position;
  final String key;
  final Color color;
  final TextAlign align;
  BoxData(this.key, this.color,this.align, this.width, this.height, this.position);
}

class TestBringToFrontState extends State<TestBringToFrontPage> {

  List<Widget> widgetArrays = [];

  var boxDataArrays = [
    BoxData("Flutter版的bringToFront", Colors.blueGrey,TextAlign.center, 200.0, 30.0, Offset(100.0, 200.0)),
    BoxData("AAA", Colors.blue,TextAlign.left, 100.0, 100.0, Offset(100.0, 300.0)),
    BoxData("BBB", Colors.orange,TextAlign.right, 100.0, 100.0, Offset(150.0, 250.0)),
  ];

  @override
  void initState() {
    super.initState();
    widgetArrays = [];
    for (int i = 0; i < boxDataArrays.length; i++) {
      var boxData = boxDataArrays[i];
      widgetArrays.add(Positioned(
          key: Key(boxData.key),
          left: boxData.position.dx,
          top: boxData.position.dy,
          child: GestureDetector(
            child: _buildBox(
                boxData.color,
                boxData.key,
                boxData.align,
                boxData.width,
                boxData.height
            ),
            onTap: () {
              bringToFront(Key(boxData.key));
            },
          )));
    }
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: widgetArrays,
    );
  }

  void bringToFront(Key key) {
    setState(() {
      for (var i = 0; i < widgetArrays.length; i++) {
        Widget widget = widgetArrays[i];
        if (key == widget.key) {
          widgetArrays.remove(widget);
          widgetArrays.add(widget);
          break;
        }
      }
    });
  }

  Widget _buildBox(
      Color color, String label, TextAlign align, double width, double height) {
    return Container(
      width: width,
      height: height,
      decoration: BoxDecoration(
          border: Border.all(color: Color.fromARGB(255, 0, 0, 0), width: 1.0),
          color: color),
      child: Text(
        label,
        textAlign: align,
        style: TextStyle(
          color: Color.fromRGBO(255, 255, 255, 1.0),
          fontWeight: FontWeight.w700,
          //decoration: TextDecoration.none,
          fontSize: 15.0,
        ),
      ),
    );
  }
}

Upvotes: 0

boformer
boformer

Reputation: 30103

Adding a GlobalKey (or some other key) to your widgets should fix the problem:

dragParts.add(DragBox(
  key: GlobalKey(),
  startPosition: Offset(0.0, 0.0),
  color: Colors.red,
  label: "Box1",
  bringToTop: this.bringToTop,
));

dragParts.add(DragBox(
  key: GlobalKey(),
  startPosition: Offset(50.0, 50.0),
  color: Colors.red,
  label: "Box2",
  bringToTop: this.bringToTop,
));

dragParts.add(DragBox(
  key: GlobalKey(),
  startPosition: Offset(100.0, 100.0),
  color: Colors.blue,
  label: "Box3",
  bringToTop: this.bringToTop,
));

Upvotes: 7

Related Questions