扑。如何使子小部件超出其父级?

时间:2019-09-10 13:08:33

标签: flutter flutter-layout

我需要在底部显示一个“购物车”图标,并显示其中的许多物品。问题是我不能将数字标签放置得足够高。它受到其父约束的限制。屏幕截图演示了它

enter image description here

按钮本身是Column小部件,在其顶部具有Frare动画“ wave” actor,在其下方有一个堆栈(绿线下方) 这就是将标签上移的结果:

enter image description here

标签本身被剪裁,并且波浪以某种方式消失。 我试图将堆栈包装在LimitedBox,OverflowBox,SizedBox中,但是都不能解决问题。

这是我想要的(理想情况下):

enter image description here

将父级的原始位置和大小留给父级,但在子级之外显示一部分子级

这是按钮类的完整代码(计数器是通过_getCounter()方法创建的):

class BottomBarButton extends StatefulWidget {

  BottomBarMenuItemModel itemModel;
  final double barHeight;


  BottomBarButton(this.itemModel, this.barHeight);

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

class _BottomBarButtonState extends State<BottomBarButton> with SingleTickerProviderStateMixin {

  AnimationController _scaleController;
  Animation<double> _scaleTween;
  Animation<Color> _colorTween;
  Animation<Color> _reversedColorTween;
  StreamSubscription<String> _streamSubscription;
  StreamSubscription<String> _counterSubscription;
  String _inactiveAnimation = 'idle';

  @override
  void initState() {
    _streamSubscription = Dispatch().onChangeBottomBar.stream.listen((menuId) {
      if (widget.itemModel.id == menuId) {
        return;
      }
      setState(() {});
    });

    _scaleController = AnimationController(
        vsync: this,
        duration: const Duration(milliseconds: 250)
    );
    _scaleTween = Tween(begin: 1.0, end: 1.2).animate(
        CurvedAnimation(
            parent: _scaleController,
            curve: Curves.bounceInOut
        )
    );
    _colorTween = ColorTween(begin: pizzaBottomBarIconNormalColor, end: pizzaYellow).animate(
        CurvedAnimation(parent: _scaleController, curve: Curves.bounceInOut)
    );
    _reversedColorTween = ColorTween(begin: pizzaYellow, end: pizzaBottomBarIconNormalColor).animate(
        CurvedAnimation(parent: _scaleController, curve: Curves.bounceInOut)
    );
    DataStore().onCartDataChange.stream.listen((newCounter) {
      setState(() {});
    });

    super.initState();
  }
  @override
  void dispose() {
    _scaleController?.dispose();
    _streamSubscription.cancel();
    _counterSubscription.cancel();
    super.dispose();
  }

  Widget _getCounter() {
    if (widget.itemModel.id == Dispatch.CartMenu) {
      var itemsInCart = DataStore().cartData.length;
      if (itemsInCart > 0) {
        return AnimatedBuilder(
          animation: _scaleTween,
          builder: (c, w) {
            return Positioned(
              right: 25 / _scaleTween.value,
              top: -15 + (20 / _scaleTween.value),
              child: AnimatedBuilder(
                animation: _colorTween,
                builder: (c, w) {
                  return Container(
                    width: 20,
                    height: 20,
                    decoration: BoxDecoration(
                      shape: BoxShape.circle,
                      color: _reversedColorTween.value,
                    ),
                    child: Text(
                      itemsInCart.toString(),
                      textAlign: TextAlign.center,
                      style: TextStyle(
                        color: pizzaBottomBarColor,
                        fontFamily: "OpenSans",
                        fontWeight: FontWeight.w500
                      ),
                    ),
                  );
                },
              ),
            );
          },
        );
      }
    }
    return Container();
  }

  @override
  Widget build(BuildContext context) {
    widget.itemModel.isActive
      ? _scaleController.forward()
      : _scaleController.reverse();
    var animationName = widget.itemModel.isActive ? 'jump_in' : _inactiveAnimation;

    return Expanded(
      child: GestureDetector(
        onTap: () {
          setState(() {
            // нужно чтобы при первом рендере не запускать все анимации, а врубить айдл
            _inactiveAnimation = 'jump_out';
            animationName = widget.itemModel.isActive ? 'jump_in' : _inactiveAnimation;
            Dispatch().selectMenu(widget.itemModel.id);
//            print(widget.itemModel.id);
          });
        },
        child: AbsorbPointer(
          child: Column(
            children: <Widget>[
              Stack(
                children: <Widget>[
                  Container( // выпрыгивающаяя волна сверху
                    // анимацию надо сдвинуть на 20 пиеселей вверх,
                    // чтобы учесть пространство
                    // для выпрыгивающей части
                    transform: Matrix4.translationValues(0, -20, 0),
                    alignment: Alignment.bottomCenter,
                    height: 20,
                    width: double.infinity,
                    child: FlareActor(
                      "assets/anim/bottom_bar_jumpy_button.flr",
                      alignment: Alignment.bottomCenter,
                      fit: BoxFit.fitWidth,
                      animation: animationName,
                      shouldClip: false,
                    )
                  ),
                  ScaleTransition(
                    scale: _scaleTween,
                    child: Container(
                      // неанимированная иконка
                      alignment: Alignment.center,
                      child: AnimatedBuilder(
                        animation: _colorTween,
                        builder: (context, w) {
                          return Icon(
                            widget.itemModel.iconData,
                            size: 20,
                            color: _colorTween.value,
                          );
                        },
                      ),
                      height: widget.barHeight - 22,
                    ),
                  ),
                  _getCounter(),
                ],
              ),
              ScaleTransition(
                // текст кнопок в нижнем баре
                scale: _scaleTween,
                alignment: FractionalOffset.topCenter, // пивот по верхней кромке
                child: Transform(
                  transform: Matrix4.translationValues(0, -6, 0),
                  child: AnimatedBuilder(
                    animation: _colorTween,
                    builder: (context, w) {
                      return Padding(
                        padding: const EdgeInsets.only(top: 7.0),
                        child: Text(widget.itemModel.title,
                          maxLines: 1,
                          overflow: TextOverflow.ellipsis,
                          textAlign: TextAlign.start,
                          style: TextStyle(
                            fontFamily: 'OpenSans',
                            fontWeight: FontWeight.w500,
                            fontSize: 13,
                            height: 1,
                            color: _colorTween.value
                          ),
                        ),
                      );
                    },
                  ),
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

3 个答案:

答案 0 :(得分:1)

我认为您可以通过使用

解决此问题

overflow:Overflow.visible,

在堆栈内部。 因此,您的代码将类似于:

Stack(
    overflow: Overflow.visible,
    children: [
        /* Your children*/
    ],
)

答案 1 :(得分:0)

一些魔术发生了。我已经为此苦苦挣扎了几个小时,但是在创建了这个主题之后的几分钟内,我想出了一个解决方案。 因此,可以使用Transform小部件很容易地解决它。 就是这样:

Widget _getCounter() {
    if (widget.itemModel.id == Dispatch.CartMenu) {
      var itemsInCart = DataStore().cartData.length;
      if (itemsInCart > 0) {
        return AnimatedBuilder(
          animation: _scaleTween,
          builder: (c, w) {
            return Transform(
              transform: Matrix4.translationValues(
                  45 + (5 * _scaleTween.value), 
                  6 - (6 * _scaleTween.value), 0
              ),
              child: AnimatedBuilder(
                animation: _colorTween,
                builder: (c, w) {
                  return Container(
                    width: 20,
                    height: 20,
                    decoration: BoxDecoration(
                      shape: BoxShape.circle,
                      color: _reversedColorTween.value,
                    ),
                    child: Text(
                      itemsInCart.toString(),
                      textAlign: TextAlign.center,
                      style: TextStyle(
                        color: pizzaBottomBarColor,
                        fontFamily: "OpenSans",
                        fontWeight: FontWeight.w500
                      ),
                    ),
                  );
                },
              ),
            );
          },
        );
      }
    }
    return Container();
  }

答案 2 :(得分:0)

请勿使用overflow,现在已弃用。请改用clipBehavior

Stack(
  clipBehavior: Clip.none, // This is what you need. 
  children: [],
)