Adds useRootNavigator
property to PopupMenuButton
widget. (#137453)
Adds `useRootNavigator` property to `PopupMenuButton` widget. Fixes #95425
This commit is contained in:
parent
d7bf0a8f3d
commit
983b2f6bc8
@ -1128,6 +1128,7 @@ class PopupMenuButton<T> extends StatefulWidget {
|
|||||||
this.constraints,
|
this.constraints,
|
||||||
this.position,
|
this.position,
|
||||||
this.clipBehavior = Clip.none,
|
this.clipBehavior = Clip.none,
|
||||||
|
this.useRootNavigator = false,
|
||||||
}) : assert(
|
}) : assert(
|
||||||
!(child != null && icon != null),
|
!(child != null && icon != null),
|
||||||
'You can only pass [child] or [icon], not both.',
|
'You can only pass [child] or [icon], not both.',
|
||||||
@ -1292,6 +1293,12 @@ class PopupMenuButton<T> extends StatefulWidget {
|
|||||||
/// Defaults to [Clip.none].
|
/// Defaults to [Clip.none].
|
||||||
final Clip clipBehavior;
|
final Clip clipBehavior;
|
||||||
|
|
||||||
|
/// Used to determine whether to push the menu to the [Navigator] furthest
|
||||||
|
/// from or nearest to the given `context`.
|
||||||
|
///
|
||||||
|
/// Defaults to false.
|
||||||
|
final bool useRootNavigator;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PopupMenuButtonState<T> createState() => PopupMenuButtonState<T>();
|
PopupMenuButtonState<T> createState() => PopupMenuButtonState<T>();
|
||||||
}
|
}
|
||||||
@ -1348,6 +1355,7 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
|
|||||||
color: widget.color ?? popupMenuTheme.color,
|
color: widget.color ?? popupMenuTheme.color,
|
||||||
constraints: widget.constraints,
|
constraints: widget.constraints,
|
||||||
clipBehavior: widget.clipBehavior,
|
clipBehavior: widget.clipBehavior,
|
||||||
|
useRootNavigator: widget.useRootNavigator,
|
||||||
)
|
)
|
||||||
.then<void>((T? newValue) {
|
.then<void>((T? newValue) {
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
|
@ -3785,6 +3785,93 @@ void main() {
|
|||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(count, 1);
|
expect(count, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgetsWithLeakTracking('PopupMenuButton uses root navigator if useRootNavigator is true', (WidgetTester tester) async {
|
||||||
|
final MenuObserver rootObserver = MenuObserver();
|
||||||
|
final MenuObserver nestedObserver = MenuObserver();
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||||
|
home: Navigator(
|
||||||
|
observers: <NavigatorObserver>[nestedObserver],
|
||||||
|
onGenerateRoute: (RouteSettings settings) {
|
||||||
|
return MaterialPageRoute<dynamic>(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return Material(
|
||||||
|
child: PopupMenuButton<String>(
|
||||||
|
useRootNavigator: true,
|
||||||
|
child: const Text('button'),
|
||||||
|
itemBuilder: (BuildContext context) {
|
||||||
|
return <PopupMenuItem<String>>[
|
||||||
|
const CheckedPopupMenuItem<String>(
|
||||||
|
value: 'item1',
|
||||||
|
child: Text('item 1'),
|
||||||
|
),
|
||||||
|
const CheckedPopupMenuItem<String>(
|
||||||
|
value: 'item2',
|
||||||
|
child: Text('item 2'),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Open the dialog.
|
||||||
|
await tester.tap(find.text('button'));
|
||||||
|
|
||||||
|
expect(rootObserver.menuCount, 1);
|
||||||
|
expect(nestedObserver.menuCount, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgetsWithLeakTracking('PopupMenuButton does not use root navigator if useRootNavigator is false', (WidgetTester tester) async {
|
||||||
|
final MenuObserver rootObserver = MenuObserver();
|
||||||
|
final MenuObserver nestedObserver = MenuObserver();
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||||
|
home: Navigator(
|
||||||
|
observers: <NavigatorObserver>[nestedObserver],
|
||||||
|
onGenerateRoute: (RouteSettings settings) {
|
||||||
|
return MaterialPageRoute<dynamic>(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return Material(
|
||||||
|
child: PopupMenuButton<String>(
|
||||||
|
child: const Text('button'),
|
||||||
|
itemBuilder: (BuildContext context) {
|
||||||
|
return <PopupMenuItem<String>>[
|
||||||
|
const CheckedPopupMenuItem<String>(
|
||||||
|
value: 'item1',
|
||||||
|
child: Text('item 1'),
|
||||||
|
),
|
||||||
|
const CheckedPopupMenuItem<String>(
|
||||||
|
value: 'item2',
|
||||||
|
child: Text('item 2'),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Open the dialog.
|
||||||
|
await tester.tap(find.text('button'));
|
||||||
|
|
||||||
|
expect(rootObserver.menuCount, 0);
|
||||||
|
expect(nestedObserver.menuCount, 1);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestApp extends StatelessWidget {
|
class TestApp extends StatelessWidget {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user