简体   繁体   中英

Flutter layout issue: stack z-order not working

The z-order of stack children layout is not as my expectation.

I have 4 widgets in a stack with keys: 0,1,2,3. I try to change their z-order and rebuild the stack widget. Here is my code:

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

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

class TestBringToFrontState extends State<TestBringToFrontPage> {
  List<Widget> _widgets = [];

  @override
  Widget build(BuildContext context) {
    return Stack(children: <Widget>[
      ..._widgets,
      Positioned(
          bottom: 0,
          right: 0,
          child: RaisedButton(
            child: Text(
              "click",
              style: TextStyle(color: Colors.white),
            ),
            onPressed: () {
              setState(() {
                _swap(0, 2);
                _swap(1, 3);
                print("$_widgets");
              });
            },
          )),
    ]);
  }

  @override
  void initState() {
    super.initState();

    const double start = 100;
    const double size = 100;
    _widgets = <Widget>[
      _buildWidget(key: const Key("0"), color: Colors.blue, offset: const Offset(start, start), size: const Size(size, size)),
      _buildWidget(key: const Key("1"), color: Colors.green, offset: const Offset(start + size / 2, start), size: const Size(size, size)),
      _buildWidget(key: const Key("2"), color: Colors.yellow, offset: const Offset(start, start + size / 2), size: const Size(size, size)),
      _buildWidget(key: const Key("3"), color: Colors.red, offset: const Offset(start + size / 2, start + size / 2), size: const Size(size, size)),
    ];
  }

  Widget _buildWidget({Key key, Color color, Offset offset, Size size}) {
    final label = (key as ValueKey<String>)?.value;
    return Positioned(
        key: key,
        left: 0,
        top: 0,
        child: Container(
          transform: Matrix4.identity()..translate(offset.dx, offset.dy, 0),
          width: size.width,
          height: size.height,
          decoration: BoxDecoration(border: Border.all(color: Colors.black, width: 1.0), color: color),
          child: Text(
            label,
            textAlign: TextAlign.left,
            style: TextStyle(
              color: Colors.black,
              fontWeight: FontWeight.w700,
              decoration: TextDecoration.none,
              fontSize: 15.0,
            ),
          ),
        ));
  }

  void _swap(int x, int y) {
    final w = _widgets[x];
    _widgets[x] = _widgets[y];
    _widgets[y] = w;
  }
}

At starting, stack children are laid out in z-order (from bottom to top): 0,1,2,3. Screen shows: Starting screen state image

By clicking to the button, I expect the stack children are laid out in new z-order: 2,3,0,1. And screen should show: Expected screen state image

But not as expected, screen shows: Actual screen state image.

Console log when clicking the button: "[Positioned-[<'2'>](left: 0.0, top: 0.0), Positioned-[<'3'>](left: 0.0, top: 0.0), Positioned-[<'0'>](left: 0.0, top: 0.0), Positioned-[<'1'>](left: 0.0, top: 0.0)]"

In Flutter inspectors window, the stack children are in correct order: 2,3,0,1. But stack renders wrong.

Do you know where am I wrong or is it flutter layout issue? Thank you in advance,

After playing around with your code for a little while, I've managed to figure out that the Key on each block is holding some state that messes their ordering up during setState . Removing the keys gives me the behaviour you're expecting. I don't know why this would be happening, but at least now you have a fix if the keys aren't needed for whatever your goal is.

Whether they are or not, I would file a bug report if I were you, because I'm pretty sure this behaviour isn't intended.

Here's the code I used that works (I cleaned up a little bit of it so it doesn't override initState ):

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: TestBringToFrontPage(),
    );
  }
}

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

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

class TestBringToFrontState extends State<TestBringToFrontPage> {
  static const start = 50.0;
  static const size = 250.0;

  final _widgets = <Widget>[
      _buildWidget(label: "0", color: Colors.blue, offset: const Offset(start, start), size: const Size(size, size)),
      _buildWidget(label: "1", color: Colors.green, offset: const Offset(start + size / 2, start), size: const Size(size, size)),
      _buildWidget(label: "2", color: Colors.yellow, offset: const Offset(start, start + size / 2), size: const Size(size, size)),
      _buildWidget(label: "3", color: Colors.red, offset: const Offset(start + size / 2, start + size / 2), size: const Size(size, size)),
    ];

  @override
  Widget build(BuildContext context) {
    return Stack(children: <Widget>[
      ..._widgets,
      Positioned(
          bottom: 0,
          right: 0,
          child: RaisedButton(
            child: Text(
              "click",
              style: TextStyle(color: Colors.white),
            ),
            onPressed: () {
              setState(() {
                _swap(0, 2);
                _swap(1, 3);
                print("$_widgets");
              });
            },
          )),
    ]);
  }

  static Widget _buildWidget({String label, Color color, Offset offset, Size size}) {
    return Positioned(
        left: 0,
        top: 0,
        child: Container(
          transform: Matrix4.identity()..translate(offset.dx, offset.dy, 0),
          width: size.width,
          height: size.height,
          decoration: BoxDecoration(border: Border.all(color: Colors.black, width: 1.0), color: color),
          child: Text(
            label,
            textAlign: TextAlign.left,
            style: TextStyle(
              color: Colors.black,
              fontWeight: FontWeight.w700,
              decoration: TextDecoration.none,
              fontSize: 30.0,
            ),
          ),
        ));
  }

  void _swap(int x, int y) {
    final w = _widgets[x];
    _widgets[x] = _widgets[y];
    _widgets[y] = w;
  }
}

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