简体   繁体   中英

Flutter - changing a Stack Order

I have a Stack where on a condition (eg user click), I want one of the lower order widgets to be pushed to the top of the stack. Using the code below as a simple example - what code do I need in a setState() method to reorder so that the first (bottom) widget becomes the last (top) widget?

new Stack(
        children: <Widget>[
          new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const 
Color.fromRGBO(200, 100, 180, 1.0)),
          new Positioned(
            left: 20.0,
            child: new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const 
Color.fromRGBO(000, 10, 130, 1.0)),
          ),
          new Positioned(
            left:40.0,
            child: new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const Color.fromRGBO(218, 165, 32, 1.0)),
          )

        ],
      );

I have edited the proposed solution and the stack does not change order. Here is the sample code in full (the print statement print to the console as expected on button press):

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

AnimationController timerController;

void main() => runApp(MaterialApp(
  home: MyApp(),
));

class MyApp extends StatefulWidget {
 @override
   MyAppState createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
  AnimationController timerController;

  @override
  Widget build(BuildContext context) {
List<Widget> stackChildren = <Widget>[
  new Icon(Icons.monetization_on,
      key: GlobalKey(),
      size: 60.0,
      color: const Color.fromRGBO(50, 50, 50, 1.0)),
  new Positioned(
    left: 20.0,
    child: new Icon(Icons.monetization_on,
        key: GlobalKey(),
        size: 60.0,
        color: const Color.fromRGBO(50, 100, 150, 1.0)),
  ),
];

void swapStackChildren() {
     setState(() {
       print("swapStackChildren");
    stackChildren = [
      new Positioned(
          left: 40.0,
          child: new Icon(Icons.monetization_on,
              key: GlobalKey(),
              size: 60.0,
              color: const Color.fromRGBO(150, 00, 200, 1.0))),
      new Icon(Icons.monetization_on,
          key: GlobalKey(),
          size: 100.0,
          color: const Color.fromRGBO(200, 200, 100, 1.0)),
    ];
  });
}

return Scaffold(
  body: Padding(
    padding: EdgeInsets.all(8.0),
    child: Column(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: <Widget>[
        Stack(children: stackChildren),
        new RaisedButton(
          child: const Text('Swop'),
          color: Theme.of(context).accentColor,
          elevation: 4.0,
          splashColor: Colors.blueGrey,
          onPressed: () {
            swapStackChildren();
          },
        ),
      ],
    ),
  ),
);
}

}

Make a variable in your widget that keeps track of the children:

List<Widget> stackChildren = <Widget>[
          new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const 
Color.fromRGBO(200, 100, 180, 1.0)),
          new Positioned(
            left: 20.0,
            child: new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const 
Color.fromRGBO(000, 10, 130, 1.0)),
          ),
          new Positioned(
            left:40.0,
            child: new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const Color.fromRGBO(218, 165, 32, 1.0)),
          )

        ];

Then in whatever function you have to trigger the order switch, you can just call the following function:

void swapStackChildren() {
    final temp = stackChildren[0];
    setState(() {
          stackChildren[0] = stackChildren[2];
          stackChildren[2] = temp;
        });
  }

Edit: As suggested by the comments, it's a better idea just to assign a new value to stackChildren instead of modifying it. So you should instead do something like this:

void swapStackChildren() {
  setState(() {
    stackChildren = [
      new Positioned(
          left: 40.0,
          child: new Icon(Icons.monetization_on,
              key: GlobalKey(),
              size: 60.0,
              color: const Color.fromRGBO(218, 165, 32, 1.0))),
      new Icon(Icons.monetization_on,
          key: GlobalKey(),
          size: 60.0,
          color: const Color.fromRGBO(200, 100, 180, 1.0)),
      new Positioned(
        left: 20.0,
        child: new Icon(Icons.monetization_on,
            key: GlobalKey(),
            size: 60.0,
            color: const Color.fromRGBO(000, 10, 130, 1.0)),
      ),
    ];
  });
}

Edit:

Here is with the full sample code:
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

AnimationController timerController;

void main() => runApp(MaterialApp(
      home: MyApp(),
    ));

class MyApp extends StatefulWidget {
  @override
  MyAppState createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
  AnimationController timerController;
  List<Widget> stackChildren = <Widget>[
      new Icon(Icons.monetization_on,
          key: GlobalKey(),
          size: 60.0,
          color: const Color.fromRGBO(50, 50, 50, 1.0)),
      new Positioned(
        left: 20.0,
        child: new Icon(Icons.monetization_on,
            key: GlobalKey(),
            size: 60.0,
            color: const Color.fromRGBO(50, 100, 150, 1.0)),
      ),
    ];

  void swapStackChildren() {
      setState(() {
        print("swapStackChildren");
        stackChildren = [
          new Positioned(
              left: 40.0,
              child: new Icon(Icons.monetization_on,
                  key: GlobalKey(),
                  size: 60.0,
                  color: const Color.fromRGBO(150, 00, 200, 1.0))),
          new Icon(Icons.monetization_on,
              key: GlobalKey(),
              size: 100.0,
              color: const Color.fromRGBO(200, 200, 100, 1.0)),
        ];
      });
    }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: EdgeInsets.all(8.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Stack(children: stackChildren),
            new RaisedButton(
              child: const Text('Swop'),
              color: Theme.of(context).accentColor,
              elevation: 4.0,
              splashColor: Colors.blueGrey,
              onPressed: () {
                swapStackChildren();
              },
            ),
          ],
        ),
      ),
    );
  }
}

I've found a less code intensive solution .You can use the Visibility widget which you can control the visibility of the child widget after the change of state .

 Visibility(
visible:visibility,//it takes bool
//here you can add the bottom widget that you want on top keeping it invisible
),

make a copy of your bottom widget and add it on top wrapped in the Visibility method as invisible and wrap your bottom widget as well .So when you want to switch you can make your bottom widget invisible and your top visible and switch accordingly

new Stack(
        children: <Widget>[
 Visibility(
visible:topvisibility
  new Positioned(
            left:40.0,
            child: new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const Color.fromRGBO(218, 165, 32, 1.0)),
          )
),

          new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const 
Color.fromRGBO(200, 100, 180, 1.0)),
          new Positioned(
            left: 20.0,
            child: new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const 
Color.fromRGBO(000, 10, 130, 1.0)),
          ),
 Visibility(
visible:bottomvisibility
          new Positioned(
            left:40.0,
            child: new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const Color.fromRGBO(218, 165, 32, 1.0)),
          )
),

        ],
      );

Then change the visibility bool accordingly in setState() method.

You can refer to this article about reordering stack items- LINK

Also don't forget to assign keys to the stack widgets if you don't want to re-render the whole widget when setState(){} is called.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM