如何在Flutter中将小部件堆叠在彼此上方

75

我需要像这样堆叠小部件:

coins

我写了下面的代码,但是硬币是一个接着一个出现的,并带有一些默认衬距。 我如何才能得到上面的图片效果?

Row(
  children: <Widget>[
    Icon(
      Icons.monetization_on, size: 36.0, 
      color: const Color.fromRGBO(218, 165, 32, 1.0),
    ),
    Icon(
      Icons.monetization_on, size: 36.0,
      color: const Color.fromRGBO(218, 165, 32, 1.0),
    ),
  ],
),
9个回答

135
你可以使用 StackPositioned 来实现这个效果:

enter image description here

class StackExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(),
      body:  new Container(
        padding: const EdgeInsets.all(8.0),
          height: 500.0,
          width: 500.0,
          // alignment: FractionalOffset.center,
          child: new Stack(
            //alignment:new Alignment(x, y)
            children: <Widget>[
              new Icon(Icons.monetization_on, size: 36.0, color: const Color.fromRGBO(218, 165, 32, 1.0)),
              new Positioned(
                left: 20.0,
                child: new Icon(Icons.monetization_on, size: 36.0, color: const Color.fromRGBO(218, 165, 32, 1.0)),
              ),
              new Positioned(
                left:40.0,
                child: new Icon(Icons.monetization_on, size: 36.0, color: const Color.fromRGBO(218, 165, 32, 1.0)),
              )

            ],
          ),
        ),
      )
    ;
  }
}

以下是一种方法,可以让图标投下阴影,让它更加突出:

在此输入图片描述

class StackExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(),
      body:  new Container(
        padding: const EdgeInsets.all(8.0),
          height: 500.0,
          width: 500.0,
          // alignment: FractionalOffset.center,
          child: new Stack(
            //alignment:new Alignment(x, y)
            children: <Widget>[
              new Container(
                decoration: new BoxDecoration(
                  borderRadius: BorderRadius.circular(25.0),
                  boxShadow: [
                    new BoxShadow(
                      blurRadius: 5.0,
                      offset: const Offset(3.0, 0.0),
                      color: Colors.grey,
                    )
                  ]
                ),
                child: new Icon(Icons.monetization_on, size: 36.0, color: const Color.fromRGBO(218, 165, 32, 1.0))),
              new Positioned(
                left: 20.0,
                child: new Container(
                  decoration: new BoxDecoration(
                  borderRadius: BorderRadius.circular(25.0),
                  boxShadow: [
                    new BoxShadow(
                      blurRadius: 5.0,
                      offset: const Offset(3.0, 0.0),
                      color: Colors.grey,
                    )
                  ]
                ),
                  child: new Icon(Icons.monetization_on, size: 36.0, color: const Color.fromRGBO(218, 165, 32, 1.0))),
              ),
              new Positioned(
                left:40.0,
                child: new Container(
                  decoration: new BoxDecoration(
                  borderRadius: BorderRadius.circular(25.0),
                  boxShadow: [
                    new BoxShadow(
                      blurRadius: 5.0,
                      offset: const Offset(3.0, 0.0),
                      color: Colors.grey,
                    )
                  ]
                )
                  ,child: new Icon(Icons.monetization_on, size: 36.0, color: const Color.fromRGBO(218, 165, 32, 1.0))),
              )

            ],
          ),
        ),
      )
    ;
  }
}

假设我们想把它放在屏幕中央? 我们需要在每个定位小部件中更改左属性,对吧? new Center(child: new StackExample()) - Raouf Rahiche
我不确定为什么会发生这种情况,但当我尝试设置 alignment: FractionalOffset.center 时,它会居中但被其他元素溢出。 - Shady Aziza
我遇到了同样的问题,现在我正在尝试使用Positioned.fromRelativeRect - Raouf Rahiche
7
啊,我知道为什么了,我们需要在“Stack”中明确设置overflow: Overflow.visible - Shady Aziza
好的,我正在我的应用程序中使用这个。我有一个列表视图(垂直滚动),每个元素都有这些堆叠元素,需要水平滚动。我该如何实现? - Ashok

44

截至2019年11月,我想添加第二个解决方案:

使用软件包:https://pub.dev/packages/assorted_layout_widgets

var widget1 = ...;
var widget2 = ...;

RowSuper(  
  children: [widget1, widget2],    
  innerDistance: -20.0,
  );

这将使行单元格重叠20像素。

与使用Stack的解决方案不同之处在于,Stack中的Positioned小部件不占用空间。因此,除非您预先知道其大小,否则不能使Stack成为其内容的大小。然而,RowSuper将具有其所有子小部件的大小。

请注意,还有一个ColumnSuper还要注意我是这个包的作者。


1
这帮助我减少列元素之间的间距。非常感谢。 - Piyush Chauhan
1
完美运行!谢谢! - penguru
1
这很恶心,但我喜欢它。谢谢。 - Dave
1
到目前为止,我还没有看到这种方法有任何问题。 - Diana

32

我希望找到一个没有依赖关系和固定布局的东西。 你可以通过使用媒体查询来以百分比方式重叠,从而增强效果。

 Widget overlapped() {
    final overlap = 10.0;
    final items = [
      CircleAvatar(child: Text('1'), backgroundColor: Colors.red),
      CircleAvatar(child: Text('2'), backgroundColor: Colors.green),
      CircleAvatar(child: Text('3'), backgroundColor: Colors.blue),
    ];

    List<Widget> stackLayers = List<Widget>.generate(items.length, (index) {
      return Padding(
        padding: EdgeInsets.fromLTRB(index.toDouble() * overlap, 0, 0, 0),
        child: items[index],
      );
    });

    return Stack(children: stackLayers);
  }

在我看来,这个解决方案比其他方案更加简洁。 - Gregory Seront
这是一个非常好的解决方案! - okihnjo
哈哈!这比推荐的好多了。感谢@Lee Higgins。 - 4xMafole
这为您提供了所有所需的灵活性,无需使用外部库。谢谢。 - Onwuka Daniel

2
您可以尝试我的软件包(signed_spacing_flex)。它与普通的Row(或ColumnFlex)完全相同。但它还允许您设置负间距,从而导致其子项重叠。您还可以设置哪些子项在重叠时应位于顶部。
在您的情况下,它可能类似于:
SignedSpacingRow(
  spacing: -12.0,
  stackingOrder: StackingOrder.lastOnTop,
  children: <Widget>[
    Icon(
      Icons.monetization_on, size: 36.0, 
      color: const Color.fromRGBO(218, 165, 32, 1.0),
    ),
    Icon(
      Icons.monetization_on, size: 36.0,
      color: const Color.fromRGBO(218, 165, 32, 1.0),
    ),
  ],
),

如果需要,它也可以与扩展的子元素一起使用。


1
这对我非常有帮助,因为我正在使用扩展的子元素,谢谢! - psyched
如果您正在尝试实现 Figma 设计,我还创建了 figma_auto_layout 包,该包依赖于此包。它复制了 Figma 的自动布局功能,也可以具有负间距。 - JakesMD
这对我非常有效,是一个很好的替代品。使用Stack或assorted_layout_widgets选项并不理想,因为它们不太容易支持crossAxisAlignement或Expanded子元素。 - Rik

1
使用OverflowBox包装元素,并赋予maxWidth值,可以实现此效果。
以下内容可用于行或列表视图中。
 return SizedBox(
            width: 35, //--> list children will be 35 in width
            child: OverflowBox(
              maxWidth: 50, // --> allowing the child to overflow will cause overlap between elements
              child: Container(
                width: 50,
                child: Text((index + 1).toString()),
              ),
            ),
          );
    

1

基于@Lee Higgins的反转变体

const size = 32.0;
const overlap = size - 6.0;
final containers = [
  Container(
    decoration: BoxDecoration(
      color: Colors.grey[300],
      shape: BoxShape.circle,
    ),
    width: size,
    height: size,
  ),
  Container(
    decoration: BoxDecoration(
      color: Colors.grey[400],
      shape: BoxShape.circle,
    ),
    width: size,
    height: size,
  ),
];
List<Widget> stackLayers = List<Widget>.generate(containers.length, (index) {
  return Padding(
    padding: EdgeInsets.fromLTRB(0, 0, index * overlap, 0),
    child: containers[index],
  );
});
return Stack(alignment: AlignmentDirectional.topEnd, children: stackLayers);

1

以下是我在Flutter中实现头像和相机图像重叠的代码。

输出结果: 点击此处查看输出图片

Container(
    constraints: new BoxConstraints(
      maxHeight: 200.0,
      maxWidth: 200.0
    ),
    padding: new EdgeInsets.only(left: 16.0, bottom: 8.0, right: 16.0),
    decoration: new BoxDecoration(
      shape: BoxShape.circle,
      image: new DecorationImage(
        image: new AssetImage('assets/images/profile.png'),
        fit: BoxFit.cover,
      ),
    ),
    child: Stack(
      children: [
        new Positioned(
          right: 0.0,
          bottom: 3.0,
          child: Container(
            constraints: new BoxConstraints(
                maxHeight: 50.0,
                maxWidth: 50.0
            ),
            decoration: new BoxDecoration(
              boxShadow: [
                BoxShadow(
                  color:Color(0xFFdedede),
                  offset: Offset(2,2)
                ),
              ],
              color: Colors.white,
              shape: BoxShape.circle,
            ),
            child: GestureDetector(
              child: Padding(
                padding: const EdgeInsets.all(8.0),
                child: Icon(
                  Icons.photo_camera,
                  size: 34,
                  color: Color(0xFF00cde7),
                ),
              ),
            ),
          ),
        ),
      ],
    ),
  )

1
你可以在 Stack 中使用 Padding。如果你想让前一个头像重叠到下一个头像上,可以这样做。
Stack(
    children: [
        Padding(
            padding: const EdgeInsets.only(left: 40),
            child: Image.asset('assets/icons/avatar3.png'),
        ),
        Padding(
            padding: const EdgeInsets.only(left: 20),
            child: Image.asset('assets/icons/avatar2.png'),
        ),
        Padding(
            padding: const EdgeInsets.only(left: 0),
            child: Image.asset('assets/icons/avatar1.png'),
        ),
    ],
)

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接