Flutter Button Stack - 图标需要与父级重叠

Flutter Button Stack - Icon needs to overlap parent

我有一个按钮,要求将图标稍微放在矩形之外。

我可以使用 Stack 完成此操作,但是按钮边框与图标重叠,如您在此处所见:

它应该是这样的:

这是我的代码:

OutlinedButton(
                style: scansButtonStyle,
                onPressed: () {}, // TODO: add later
                child: Stack(clipBehavior: Clip.none, children: [
                  Padding(
                    padding: const EdgeInsets.only(left: 50.0),
                    child: Text('CONNECT'),
                  ),
                  Positioned(
                    bottom: 0,
                    left: -20,
                    child: CircleAvatar(
                      radius: 30,
                      backgroundColor: Colors.black,
                      child: CircleAvatar(
                        radius: 27,
                        backgroundColor: Colors.white,
                        child: Icon(
                          Icons.bluetooth_connected,
                          color: Colors.black,
                          size: 48,
                        ),
                      ),
                    ),
                  ),
                ]),
              ),

和样式:

final ButtonStyle scansButtonStyle = OutlinedButton.styleFrom(
    alignment: Alignment.centerLeft,
    primary: Colors.black,
    backgroundColor: Color(0xfffcd722),
    minimumSize: Size(242, 48),
    padding: EdgeInsets.fromLTRB(20, 3, 20, 3),
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.only(
          topLeft: Radius.circular(16),
          topRight: Radius.zero,
          bottomRight: Radius.circular(16),
          bottomLeft: Radius.zero),
    ),
    textStyle: const TextStyle(
      color: Colors.black,
      fontWeight: FontWeight.w600,
      fontSize: 34,
      fontFamily: 'HP',
    ),
    side: BorderSide(
      color: Colors.black,
      width: 3.0,
      style: BorderStyle.solid,
    ));

我通过将按钮和图标放在堆栈中(而不是按钮内部的堆栈)实现了所需的视觉效果。

这里的缺点是,如果我现在单击图标,它不再触发按钮。

所以这个答案不太正确....

Stack(
   clipBehavior: Clip.none,
                children: [
                  OutlinedButton(
                  style: scansButtonStyle,
                  onPressed: () {}, // TODO: add later
                  child: Padding(
                    padding: const EdgeInsets.only(left: 50.0),
                    child: Text('CONNECT'),
                  ),
                    ),
                  Positioned(
                    bottom: 6,
                    left: 0,
                    child: CircleAvatar(
                      radius: 30,
                      backgroundColor: Colors.black,
                      child: CircleAvatar(
                        radius: 27,
                        backgroundColor: Colors.white,
                        child: Icon(
                          Icons.bluetooth_connected,
                          color: Colors.black,
                          size: 48,
                        ),
                      ),
                    ),
                  ),
              ]),

我已经更正了代码。
主要变化是:

  1. 将 Outlined 按钮放入 Stack 中
  2. 将 CircleAvatar 更改为带有装饰的简单容器(为此您真的需要 CircleAvatar 吗?您会以任何方式为图标设置动画吗?如果没有,那么简单的容器可能会更好)
  3. 将 IgnorePointer 添加到图标,使其忽略点击事件并将其向下传递给按钮本身(这是为了即使在按下图标时也能产生涟漪效果)
  4. 用 FittedBox 小部件包裹 Icon 并向外部容器添加填充。这将允许我们的图标根据外圈的大小动态改变它的大小。
  5. 向文本小部件添加了一些参数,以便在文本长于宽度时不会破坏设计。您可以使用 Text('CONNECT' * 10)
  6. 检查它
Stack(
                  clipBehavior: Clip.none,
                  children: [
                    OutlinedButton(
                      onPressed: () {},
                      style: scansButtonStyle,
                      child: Padding(
                        padding: const EdgeInsets.only(left: 50.0),
                        child: Text(
                          'CONNECT',
                          maxLines: 1,
                          overflow: TextOverflow.ellipsis,
                          textScaleFactor: 1.0, // We do not want OS preferences (text scale option) to break our button's design
                        ),
                      ),
                    ),
                    Positioned(
                      bottom: 0,
                      child: IgnorePointer(
                        //ignore touch events, so that the Outlined button triggers touch animation when pressed on the icon
                        child: SizedBox(
                          width: 60.0,
                          child: AspectRatio(
                            aspectRatio: 1.0,
                            child: Container(
                              padding: EdgeInsets.all(6.0),
                              decoration: BoxDecoration(
                                color: Colors.white,
                                border: Border.all(
                                  color: Colors.black,
                                  width: 3.0,
                                ),
                                shape: BoxShape.circle,
                              ),
                              child: FittedBox(
                                child: Icon(
                                  Icons.bluetooth_connected,
                                  color: Colors.black,
                                ),
                              ),
                            ),
                          ),
                        ),
                      ),
                    ),
                  ],
                )

代码可能需要进一步调整,但您会明白的。

另请注意,图标的顶部缺口不占用任何 space(因为 Clip.none 并且它在堆栈中的位置)。您可以通过将按钮包裹在彩色容器中来对其进行测试。

如果您不喜欢 SizedBox 中的 AspectRatio 这个想法 - 在 Container 中将其改回 width/height。