import 'package:flutter/material.dart'; // ignore: must_be_immutable class PopupAnimationWidget extends AnimatedWidget { final GlobalKey stackKey; final GlobalKey startKey; final Color color; final Widget child; final Offset popupOffset; final Animation animation; PopupAnimationWidget({ @required this.animation, @required this.stackKey, @required this.startKey, @required this.child, this.color = Colors.yellow, this.popupOffset = Offset.zero, }) : super(listenable: animation); Offset _startOffset; Offset _offset = Offset.zero; @override Widget build(BuildContext context) { _calAnimation(); final Animation opacityAnimation = Tween(begin: 0, end: 1) .animate(CurvedAnimation( parent: animation, curve: Interval(0, 0.4, curve: Curves.ease))); _offset = Offset(_startOffset.dx, _startOffset.dy - opacityAnimation.value * 80); return Positioned( left: _offset.dx, top: _offset.dy, child: Opacity( opacity: opacityAnimation.value, child: ScaleTransition( alignment: Alignment.bottomCenter, scale: opacityAnimation, child: Container( child: child, ), ), ), ); } void _calAnimation() { if (_startOffset == null) { final RenderBox stackBox = stackKey.currentContext.findRenderObject(); final Offset stackBoxOffset = stackBox.globalToLocal(Offset.zero); final EdgeInsets startMargin = _margin(startKey); final RenderBox startBox = startKey.currentContext.findRenderObject(); _startOffset = startBox.localToGlobal(Offset( startMargin.left + popupOffset.dx, stackBoxOffset.dy + startMargin.top + popupOffset.dy)); } } EdgeInsets _margin(GlobalKey key) { final Widget widget = key.currentContext.widget; final EdgeInsets margin = (widget is Container) ? widget.margin : EdgeInsets.zero; return margin ?? EdgeInsets.zero; } }