Unverified Commit fe57037a authored by Shi-Hao Hong's avatar Shi-Hao Hong Committed by GitHub

Revert "Update PopupMenuButton widget (#80420)" (#80831)

parent 61c30c41
......@@ -732,8 +732,6 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
this.shape,
this.color,
required this.capturedThemes,
this.menuKey,
this.positionCallback,
}) : itemSizes = List<Size?>.filled(items.length, null);
final RelativeRect position;
......@@ -745,8 +743,6 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
final ShapeBorder? shape;
final Color? color;
final CapturedThemes capturedThemes;
final Key? menuKey;
final PopupMenuButtonPositionCallback? positionCallback;
@override
Animation<double> createAnimation() {
......@@ -782,13 +778,12 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
final Widget menu = _PopupMenu<T>(route: this, semanticLabel: semanticLabel);
return StatefulBuilder(
key: menuKey,
builder: (BuildContext context, StateSetter setState) {
return Builder(
builder: (BuildContext context) {
final MediaQueryData mediaQuery = MediaQuery.of(context);
return CustomSingleChildLayout(
delegate: _PopupMenuRouteLayout(
positionCallback == null ? position : positionCallback!(),
position,
itemSizes,
selectedItemIndex,
Directionality.of(context),
......@@ -806,10 +801,6 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
///
/// `items` should be non-null and not empty.
///
/// Prefer to use `positionCallback` to obtain position instead of 'position'
/// when `positionCallback` is non-null. In this way, the position of the menu
/// can be recalculated through this callback during the rebuild phase of the menu.
///
/// If `initialValue` is specified then the first item with a matching value
/// will be highlighted and the value of `position` gives the rectangle whose
/// vertical center will be aligned with the vertical center of the highlighted
......@@ -871,8 +862,6 @@ Future<T?> showMenu<T>({
ShapeBorder? shape,
Color? color,
bool useRootNavigator = false,
Key? menuKey,
PopupMenuButtonPositionCallback? positionCallback,
}) {
assert(context != null);
assert(position != null);
......@@ -902,8 +891,6 @@ Future<T?> showMenu<T>({
shape: shape,
color: color,
capturedThemes: InheritedTheme.capture(from: context, to: navigator.context),
menuKey: menuKey,
positionCallback: positionCallback,
));
}
......@@ -1103,44 +1090,11 @@ class PopupMenuButton<T> extends StatefulWidget {
PopupMenuButtonState<T> createState() => PopupMenuButtonState<T>();
}
/// Signature for the callback used by [showMenu] to obtain the position of the
/// [PopupMenuButton].
///
/// Used by [showMenu].
typedef PopupMenuButtonPositionCallback = RelativeRect Function();
/// The [State] for a [PopupMenuButton].
///
/// See [showButtonMenu] for a way to programmatically open the popup menu
/// of your button state.
class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
GlobalKey _menuGlobalKey = GlobalKey();
RelativeRect? _buttonPosition;
RelativeRect _getButtonPosition() => _buttonPosition!;
RelativeRect _calculateButtonPosition() {
final RenderBox button = context.findRenderObject()! as RenderBox;
final RenderBox overlay = Navigator.of(context).overlay!.context.findRenderObject()! as RenderBox;
return RelativeRect.fromRect(
Rect.fromPoints(
button.localToGlobal(widget.offset, ancestor: overlay),
button.localToGlobal(button.size.bottomRight(Offset.zero) + widget.offset, ancestor: overlay),
),
Offset.zero & overlay.size,
);
}
void _maybeUpdateMenuPosition() {
WidgetsBinding.instance!.addPostFrameCallback((Duration duration) {
final RelativeRect newPosition = _calculateButtonPosition();
if (newPosition != _buttonPosition) {
_menuGlobalKey.currentState?.setState(() {});
_buttonPosition = newPosition;
}
});
}
/// A method to show a popup menu with the items supplied to
/// [PopupMenuButton.itemBuilder] at the position of your [PopupMenuButton].
///
......@@ -1151,12 +1105,16 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
/// show the menu of the button with `globalKey.currentState.showButtonMenu`.
void showButtonMenu() {
final PopupMenuThemeData popupMenuTheme = PopupMenuTheme.of(context);
final RenderBox button = context.findRenderObject()! as RenderBox;
final RenderBox overlay = Navigator.of(context).overlay!.context.findRenderObject()! as RenderBox;
final RelativeRect position = RelativeRect.fromRect(
Rect.fromPoints(
button.localToGlobal(widget.offset, ancestor: overlay),
button.localToGlobal(button.size.bottomRight(Offset.zero) + widget.offset, ancestor: overlay),
),
Offset.zero & overlay.size,
);
final List<PopupMenuEntry<T>> items = widget.itemBuilder(context);
// It is possible that the fade-out animation of the menu has not finished
// yet, and the key needs to be regenerated at this time, otherwise there will
// be an exception of duplicate GlobalKey.
if (_menuGlobalKey.currentState != null)
_menuGlobalKey = GlobalKey();
// Only show the menu if there is something to show
if (items.isNotEmpty) {
showMenu<T?>(
......@@ -1164,11 +1122,9 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
elevation: widget.elevation ?? popupMenuTheme.elevation,
items: items,
initialValue: widget.initialValue,
position: _buttonPosition!,
position: position,
shape: widget.shape ?? popupMenuTheme.shape,
color: widget.color ?? popupMenuTheme.color,
menuKey: _menuGlobalKey,
positionCallback: _getButtonPosition,
)
.then<void>((T? newValue) {
if (!mounted)
......@@ -1192,18 +1148,6 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
}
}
@override
void didUpdateWidget(PopupMenuButton<T> oldWidget) {
_maybeUpdateMenuPosition();
super.didUpdateWidget(oldWidget);
}
@override
void didChangeDependencies() {
_maybeUpdateMenuPosition();
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
final bool enableFeedback = widget.enableFeedback
......
......@@ -2209,87 +2209,6 @@ void main() {
await tester.pumpAndSettle();
expect(find.text('foo'), findsOneWidget);
});
testWidgets('The opened menu should follow if the button\'s position changed', (WidgetTester tester) async {
final GlobalKey buttonKey = GlobalKey();
Widget buildFrame(double width, double height) {
return MaterialApp(
home: Scaffold(
body: SizedBox(
height: height,
width: width,
child: Center(
child: PopupMenuButton<int>(
child: SizedBox(
key: buttonKey,
height: 10.0,
width: 10.0,
child: const ColoredBox(
color: Colors.pink,
),
),
itemBuilder: (BuildContext context) => <PopupMenuEntry<int>>[
const PopupMenuItem<int>(child: Text('-1-'), value: 1),
const PopupMenuItem<int>(child: Text('-2-'), value: 2),
],
),
),
),
),
);
}
await tester.pumpWidget(buildFrame(100.0, 100.0));
// Open the menu.
await tester.tap(find.byKey(buttonKey));
await tester.pumpAndSettle();
// +--------+--------+ 100
// | | |
// | | (50,50)|
// +--------+--------+
// | | |
// | | |
// 100 +--------+--------+
//
// The button is a rectangle of 10 * 10 size and is centered,
// so its top-left offset should be (45.0, 45.0).
Offset buttonOffset = tester.getTopLeft(find.byKey(buttonKey));
expect(buttonOffset, const Offset(45.0, 45.0));
// The top-left corner of the menu and button should be aligned.
Offset popupMenuOffset = tester.getTopLeft(find.byType(SingleChildScrollView));
expect(popupMenuOffset, buttonOffset);
// Keep the menu opened and re-layout the screen.
await tester.pumpWidget(buildFrame(200.0, 300.0));
// +-----------+-----------+ 200
// | | |
// | | |
// | | |
// | | |
// | | (100,150) |
// +-----------+-----------+
// | | |
// | | |
// | | |
// | | |
// | | |
// 300 +-----------+-----------+
//
// The button is a rectangle of 10 * 10 size and is centered,
// so its top-left offset should be (95.0, 145.0).
await tester.pump(); // Need a frame to update the menu.
buttonOffset = tester.getTopLeft(find.byKey(buttonKey));
expect(buttonOffset, const Offset(95.0, 145.0));
// The popup menu should follow the button.
popupMenuOffset = tester.getTopLeft(find.byType(SingleChildScrollView));
expect(popupMenuOffset, buttonOffset);
});
}
class TestApp extends StatefulWidget {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment