diff --git a/examples/material_gallery/lib/demo/grid_list_demo.dart b/examples/material_gallery/lib/demo/grid_list_demo.dart index 89850ae220..129be0913c 100644 --- a/examples/material_gallery/lib/demo/grid_list_demo.dart +++ b/examples/material_gallery/lib/demo/grid_list_demo.dart @@ -221,9 +221,8 @@ class GridListDemoState extends State { ]; final EdgeInsets padding = MediaQuery.of(context).padding; - final ModalPosition position = new ModalPosition( - right: padding.right + 16.0, - top: padding.top + 16.0 + final RelativeRect position = new RelativeRect.fromLTRB( + 0.0, padding.top + 16.0, padding.right + 16.0, 0.0 ); showMenu(context: context, position: position, items: items).then((GridDemoTileStyle value) { diff --git a/packages/flutter/lib/src/material/drop_down.dart b/packages/flutter/lib/src/material/drop_down.dart index 7e5caaabb8..b6ebaafd28 100644 --- a/packages/flutter/lib/src/material/drop_down.dart +++ b/packages/flutter/lib/src/material/drop_down.dart @@ -119,12 +119,12 @@ class _DropDownMenu extends StatusTransitionWidget { ); final Tween menuTop = new Tween( - begin: route.rect.top, - end: route.rect.top - route.selectedIndex * route.rect.height - _kMenuVerticalPadding.top + begin: route.buttonRect.top, + end: route.buttonRect.top - route.selectedIndex * route.buttonRect.height - _kMenuVerticalPadding.top ); final Tween menuBottom = new Tween( - begin: route.rect.bottom, - end: menuTop.end + route.items.length * route.rect.height + _kMenuVerticalPadding.vertical + begin: route.buttonRect.bottom, + end: menuTop.end + route.items.length * route.buttonRect.height + _kMenuVerticalPadding.vertical ); Widget child = new Material( @@ -156,6 +156,37 @@ class _DropDownMenu extends StatusTransitionWidget { } } +class _DropDownMenuRouteLayout extends SingleChildLayoutDelegate { + _DropDownMenuRouteLayout(this.buttonRect, this.selectedIndex); + + final Rect buttonRect; + final int selectedIndex; + + @override + BoxConstraints getConstraintsForChild(BoxConstraints constraints) { + return new BoxConstraints( + minWidth: buttonRect.width, + maxWidth: buttonRect.width, + minHeight: 0.0, + maxHeight: constraints.maxHeight + ); + } + + @override + Offset getPositionForChild(Size size, Size childSize) { + return new Offset( + buttonRect.left, + buttonRect.top - selectedIndex * buttonRect.height - _kMenuVerticalPadding.top + ); + } + + @override + bool shouldRelayout(_DropDownMenuRouteLayout oldDelegate) { + return oldDelegate.buttonRect != buttonRect + || oldDelegate.selectedIndex != selectedIndex; + } +} + // We box the return value so that the return value can be null. Otherwise, // canceling the route (which returns null) would get confused with actually // returning a real null value. @@ -180,14 +211,14 @@ class _DropDownRoute extends PopupRoute<_DropDownRouteResult> { _DropDownRoute({ Completer<_DropDownRouteResult> completer, this.items, + this.buttonRect, this.selectedIndex, - this.rect, this.elevation: 8 }) : super(completer: completer); final List> items; + final Rect buttonRect; final int selectedIndex; - final Rect rect; final int elevation; @override @@ -199,22 +230,12 @@ class _DropDownRoute extends PopupRoute<_DropDownRouteResult> { @override Color get barrierColor => null; - @override - ModalPosition getPosition(BuildContext context) { - RenderBox overlayBox = Overlay.of(context).context.findRenderObject(); - assert(overlayBox != null); // can't be null; routes get inserted by Navigator which has its own Overlay - Size overlaySize = overlayBox.size; - RelativeRect menuRect = new RelativeRect.fromSize(rect, overlaySize); - return new ModalPosition( - top: menuRect.top - selectedIndex * rect.height - _kMenuVerticalPadding.top, - left: menuRect.left, - right: menuRect.right - ); - } - @override Widget buildPage(BuildContext context, Animation animation, Animation forwardAnimation) { - return new _DropDownMenu(route: this); + return new CustomSingleChildLayout( + delegate: new _DropDownMenuRouteLayout(buttonRect, selectedIndex), + child: new _DropDownMenu(route: this) + ); } } @@ -359,13 +380,13 @@ class _DropDownButtonState extends State> { void _handleTap() { final RenderBox renderBox = indexedStackKey.currentContext.findRenderObject(); - final Rect rect = renderBox.localToGlobal(Point.origin) & renderBox.size; + final Rect buttonRect = renderBox.localToGlobal(Point.origin) & renderBox.size; final Completer<_DropDownRouteResult> completer = new Completer<_DropDownRouteResult>(); Navigator.push(context, new _DropDownRoute( completer: completer, items: config.items, + buttonRect: _kMenuHorizontalPadding.inflateRect(buttonRect), selectedIndex: _selectedIndex, - rect: _kMenuHorizontalPadding.inflateRect(rect), elevation: config.elevation )); completer.future.then((_DropDownRouteResult newValue) { diff --git a/packages/flutter/lib/src/material/popup_menu.dart b/packages/flutter/lib/src/material/popup_menu.dart index f3f617cb4b..d12fdf339e 100644 --- a/packages/flutter/lib/src/material/popup_menu.dart +++ b/packages/flutter/lib/src/material/popup_menu.dart @@ -328,17 +328,12 @@ class _PopupMenu extends StatelessWidget { class _PopupMenuRouteLayout extends SingleChildLayoutDelegate { _PopupMenuRouteLayout(this.position, this.selectedItemOffset); - final ModalPosition position; + final RelativeRect position; final double selectedItemOffset; @override BoxConstraints getConstraintsForChild(BoxConstraints constraints) { - return new BoxConstraints( - minWidth: 0.0, - maxWidth: constraints.maxWidth, - minHeight: 0.0, - maxHeight: constraints.maxHeight - ); + return constraints.loosen(); } // Put the child wherever position specifies, so long as it will fit within the @@ -380,14 +375,11 @@ class _PopupMenuRoute extends PopupRoute { this.elevation }) : super(completer: completer); - final ModalPosition position; + final RelativeRect position; final List> items; final dynamic initialValue; final int elevation; - @override - ModalPosition getPosition(BuildContext context) => null; - @override Animation createAnimation() { return new CurvedAnimation( @@ -416,13 +408,9 @@ class _PopupMenuRoute extends PopupRoute { selectedItemOffset += items[i].height; } } - final Size screenSize = MediaQuery.of(context).size; - return new ConstrainedBox( - constraints: new BoxConstraints(maxWidth: screenSize.width, maxHeight: screenSize.height), - child: new CustomSingleChildLayout( - delegate: new _PopupMenuRouteLayout(position, selectedItemOffset), - child: new _PopupMenu(route: this) - ) + return new CustomSingleChildLayout( + delegate: new _PopupMenuRouteLayout(position, selectedItemOffset), + child: new _PopupMenu(route: this) ); } } @@ -434,7 +422,7 @@ class _PopupMenuRoute extends PopupRoute { /// implies the menu's origin. Future showMenu/**/({ BuildContext context, - ModalPosition position, + RelativeRect position, List> items, dynamic/*=T*/ initialValue, int elevation: 8 @@ -518,9 +506,9 @@ class _PopupMenuButtonState extends State> { elevation: config.elevation, items: config.itemBuilder(context), initialValue: config.initialValue, - position: new ModalPosition( - left: topLeft.x, - top: topLeft.y + (config.initialValue != null ? renderBox.size.height / 2.0 : 0.0) + position: new RelativeRect.fromLTRB( + topLeft.x, topLeft.y + (config.initialValue != null ? renderBox.size.height / 2.0 : 0.0), + 0.0, 0.0 ) ) .then((T value) {