Reland "Fix Backbutton is not displayed when there is a endDrawer (#1… (#104110)
* Reland "Fix Backbutton is not displayed when there is a endDrawer (#102093)" This reverts commit a4a8e73bce152ab39d6ae839ca51e447f87293fa. * add todos
This commit is contained in:
parent
c29a7a2d22
commit
bd7d34f09d
@ -970,9 +970,11 @@ class _AppBarState extends State<AppBar> {
|
|||||||
onPressed: _handleDrawerButton,
|
onPressed: _handleDrawerButton,
|
||||||
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
|
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
|
||||||
);
|
);
|
||||||
} else {
|
// TODO(chunhtai): remove (!hasEndDrawer && canPop) once internal tests
|
||||||
if (!hasEndDrawer && canPop)
|
// are migrated.
|
||||||
leading = useCloseButton ? const CloseButton() : const BackButton();
|
// https://github.com/flutter/flutter/issues/80256.
|
||||||
|
} else if ((!hasEndDrawer && canPop) || (parentRoute?.impliesAppBarDismissal ?? false)) {
|
||||||
|
leading = useCloseButton ? const CloseButton() : const BackButton();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (leading != null) {
|
if (leading != null) {
|
||||||
|
@ -401,7 +401,7 @@ class DrawerControllerState extends State<DrawerController> with SingleTickerPro
|
|||||||
if (_historyEntry == null) {
|
if (_historyEntry == null) {
|
||||||
final ModalRoute<dynamic>? route = ModalRoute.of(context);
|
final ModalRoute<dynamic>? route = ModalRoute.of(context);
|
||||||
if (route != null) {
|
if (route != null) {
|
||||||
_historyEntry = LocalHistoryEntry(onRemove: _handleHistoryEntryRemoved);
|
_historyEntry = LocalHistoryEntry(onRemove: _handleHistoryEntryRemoved, impliesAppBarDismissal: false);
|
||||||
route.addLocalHistoryEntry(_historyEntry!);
|
route.addLocalHistoryEntry(_historyEntry!);
|
||||||
FocusScope.of(context).setFirstFocus(_focusScopeNode);
|
FocusScope.of(context).setFirstFocus(_focusScopeNode);
|
||||||
}
|
}
|
||||||
|
@ -455,13 +455,21 @@ abstract class TransitionRoute<T> extends OverlayRoute<T> {
|
|||||||
/// An entry in the history of a [LocalHistoryRoute].
|
/// An entry in the history of a [LocalHistoryRoute].
|
||||||
class LocalHistoryEntry {
|
class LocalHistoryEntry {
|
||||||
/// Creates an entry in the history of a [LocalHistoryRoute].
|
/// Creates an entry in the history of a [LocalHistoryRoute].
|
||||||
LocalHistoryEntry({ this.onRemove });
|
///
|
||||||
|
/// The [impliesAppBarDismissal] defaults to true if not provided.
|
||||||
|
LocalHistoryEntry({ this.onRemove, this.impliesAppBarDismissal = true });
|
||||||
|
|
||||||
/// Called when this entry is removed from the history of its associated [LocalHistoryRoute].
|
/// Called when this entry is removed from the history of its associated [LocalHistoryRoute].
|
||||||
final VoidCallback? onRemove;
|
final VoidCallback? onRemove;
|
||||||
|
|
||||||
LocalHistoryRoute<dynamic>? _owner;
|
LocalHistoryRoute<dynamic>? _owner;
|
||||||
|
|
||||||
|
/// Whether an [AppBar] in the route this entry belongs to should
|
||||||
|
/// automatically add a back button or close button.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
final bool impliesAppBarDismissal;
|
||||||
|
|
||||||
/// Remove this entry from the history of its associated [LocalHistoryRoute].
|
/// Remove this entry from the history of its associated [LocalHistoryRoute].
|
||||||
void remove() {
|
void remove() {
|
||||||
_owner?.removeLocalHistoryEntry(this);
|
_owner?.removeLocalHistoryEntry(this);
|
||||||
@ -482,7 +490,7 @@ class LocalHistoryEntry {
|
|||||||
/// is removed from the list and its [LocalHistoryEntry.onRemove] is called.
|
/// is removed from the list and its [LocalHistoryEntry.onRemove] is called.
|
||||||
mixin LocalHistoryRoute<T> on Route<T> {
|
mixin LocalHistoryRoute<T> on Route<T> {
|
||||||
List<LocalHistoryEntry>? _localHistory;
|
List<LocalHistoryEntry>? _localHistory;
|
||||||
|
int _entriesImpliesAppBarDismissal = 0;
|
||||||
/// Adds a local history entry to this route.
|
/// Adds a local history entry to this route.
|
||||||
///
|
///
|
||||||
/// When asked to pop, if this route has any local history entries, this route
|
/// When asked to pop, if this route has any local history entries, this route
|
||||||
@ -620,7 +628,12 @@ mixin LocalHistoryRoute<T> on Route<T> {
|
|||||||
_localHistory ??= <LocalHistoryEntry>[];
|
_localHistory ??= <LocalHistoryEntry>[];
|
||||||
final bool wasEmpty = _localHistory!.isEmpty;
|
final bool wasEmpty = _localHistory!.isEmpty;
|
||||||
_localHistory!.add(entry);
|
_localHistory!.add(entry);
|
||||||
if (wasEmpty)
|
bool internalStateChanged = false;
|
||||||
|
if (entry.impliesAppBarDismissal) {
|
||||||
|
internalStateChanged = _entriesImpliesAppBarDismissal == 0;
|
||||||
|
_entriesImpliesAppBarDismissal += 1;
|
||||||
|
}
|
||||||
|
if (wasEmpty || internalStateChanged)
|
||||||
changedInternalState();
|
changedInternalState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -632,10 +645,15 @@ mixin LocalHistoryRoute<T> on Route<T> {
|
|||||||
assert(entry != null);
|
assert(entry != null);
|
||||||
assert(entry._owner == this);
|
assert(entry._owner == this);
|
||||||
assert(_localHistory!.contains(entry));
|
assert(_localHistory!.contains(entry));
|
||||||
_localHistory!.remove(entry);
|
bool internalStateChanged = false;
|
||||||
|
if (_localHistory!.remove(entry) && entry.impliesAppBarDismissal) {
|
||||||
|
_entriesImpliesAppBarDismissal -= 1;
|
||||||
|
internalStateChanged = _entriesImpliesAppBarDismissal == 0;
|
||||||
|
}
|
||||||
entry._owner = null;
|
entry._owner = null;
|
||||||
entry._notifyRemoved();
|
entry._notifyRemoved();
|
||||||
if (_localHistory!.isEmpty) {
|
if (_localHistory!.isEmpty || internalStateChanged) {
|
||||||
|
assert(_entriesImpliesAppBarDismissal == 0);
|
||||||
if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) {
|
if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) {
|
||||||
// The local history might be removed as a result of disposing inactive
|
// The local history might be removed as a result of disposing inactive
|
||||||
// elements during finalizeTree. The state is locked at this moment, and
|
// elements during finalizeTree. The state is locked at this moment, and
|
||||||
@ -663,7 +681,12 @@ mixin LocalHistoryRoute<T> on Route<T> {
|
|||||||
assert(entry._owner == this);
|
assert(entry._owner == this);
|
||||||
entry._owner = null;
|
entry._owner = null;
|
||||||
entry._notifyRemoved();
|
entry._notifyRemoved();
|
||||||
if (_localHistory!.isEmpty)
|
bool internalStateChanged = false;
|
||||||
|
if (entry.impliesAppBarDismissal) {
|
||||||
|
_entriesImpliesAppBarDismissal -= 1;
|
||||||
|
internalStateChanged = _entriesImpliesAppBarDismissal == 0;
|
||||||
|
}
|
||||||
|
if (_localHistory!.isEmpty || internalStateChanged)
|
||||||
changedInternalState();
|
changedInternalState();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -697,6 +720,7 @@ class _ModalScopeStatus extends InheritedWidget {
|
|||||||
const _ModalScopeStatus({
|
const _ModalScopeStatus({
|
||||||
required this.isCurrent,
|
required this.isCurrent,
|
||||||
required this.canPop,
|
required this.canPop,
|
||||||
|
required this.impliesAppBarDismissal,
|
||||||
required this.route,
|
required this.route,
|
||||||
required super.child,
|
required super.child,
|
||||||
}) : assert(isCurrent != null),
|
}) : assert(isCurrent != null),
|
||||||
@ -706,12 +730,14 @@ class _ModalScopeStatus extends InheritedWidget {
|
|||||||
|
|
||||||
final bool isCurrent;
|
final bool isCurrent;
|
||||||
final bool canPop;
|
final bool canPop;
|
||||||
|
final bool impliesAppBarDismissal;
|
||||||
final Route<dynamic> route;
|
final Route<dynamic> route;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool updateShouldNotify(_ModalScopeStatus old) {
|
bool updateShouldNotify(_ModalScopeStatus old) {
|
||||||
return isCurrent != old.isCurrent ||
|
return isCurrent != old.isCurrent ||
|
||||||
canPop != old.canPop ||
|
canPop != old.canPop ||
|
||||||
|
impliesAppBarDismissal != old.impliesAppBarDismissal ||
|
||||||
route != old.route;
|
route != old.route;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -720,6 +746,7 @@ class _ModalScopeStatus extends InheritedWidget {
|
|||||||
super.debugFillProperties(description);
|
super.debugFillProperties(description);
|
||||||
description.add(FlagProperty('isCurrent', value: isCurrent, ifTrue: 'active', ifFalse: 'inactive'));
|
description.add(FlagProperty('isCurrent', value: isCurrent, ifTrue: 'active', ifFalse: 'inactive'));
|
||||||
description.add(FlagProperty('canPop', value: canPop, ifTrue: 'can pop'));
|
description.add(FlagProperty('canPop', value: canPop, ifTrue: 'can pop'));
|
||||||
|
description.add(FlagProperty('impliesAppBarDismissal', value: impliesAppBarDismissal, ifTrue: 'implies app bar dismissal'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -822,6 +849,7 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> {
|
|||||||
route: widget.route,
|
route: widget.route,
|
||||||
isCurrent: widget.route.isCurrent, // _routeSetState is called if this updates
|
isCurrent: widget.route.isCurrent, // _routeSetState is called if this updates
|
||||||
canPop: widget.route.canPop, // _routeSetState is called if this updates
|
canPop: widget.route.canPop, // _routeSetState is called if this updates
|
||||||
|
impliesAppBarDismissal: widget.route.impliesAppBarDismissal,
|
||||||
child: Offstage(
|
child: Offstage(
|
||||||
offstage: widget.route.offstage, // _routeSetState is called if this updates
|
offstage: widget.route.offstage, // _routeSetState is called if this updates
|
||||||
child: PageStorage(
|
child: PageStorage(
|
||||||
@ -1561,6 +1589,14 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
|
|||||||
/// notified.
|
/// notified.
|
||||||
bool get canPop => hasActiveRouteBelow || willHandlePopInternally;
|
bool get canPop => hasActiveRouteBelow || willHandlePopInternally;
|
||||||
|
|
||||||
|
/// Whether an [AppBar] in the route should automatically add a back button or
|
||||||
|
/// close button.
|
||||||
|
///
|
||||||
|
/// This getter returns true if there is at least one active route below it,
|
||||||
|
/// or there is at least one [LocalHistoryEntry] with `impliesAppBarDismissal`
|
||||||
|
/// set to true
|
||||||
|
bool get impliesAppBarDismissal => hasActiveRouteBelow || _entriesImpliesAppBarDismissal > 0;
|
||||||
|
|
||||||
// Internals
|
// Internals
|
||||||
|
|
||||||
final GlobalKey<_ModalScopeState<T>> _scopeKey = GlobalKey<_ModalScopeState<T>>();
|
final GlobalKey<_ModalScopeState<T>> _scopeKey = GlobalKey<_ModalScopeState<T>>();
|
||||||
|
@ -3267,6 +3267,44 @@ void main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/80256
|
||||||
|
testWidgets('The second page should have a back button even it has a end drawer', (WidgetTester tester) async {
|
||||||
|
final Page<void> page1 = MaterialPage<void>(
|
||||||
|
key: const ValueKey<String>('1'),
|
||||||
|
child: Scaffold(
|
||||||
|
key: const ValueKey<String>('1'),
|
||||||
|
appBar: AppBar(),
|
||||||
|
endDrawer: const Drawer(),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
final Page<void> page2 = MaterialPage<void>(
|
||||||
|
key: const ValueKey<String>('2'),
|
||||||
|
child: Scaffold(
|
||||||
|
key: const ValueKey<String>('2'),
|
||||||
|
appBar: AppBar(),
|
||||||
|
endDrawer: const Drawer(),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
final List<Page<void>> pages = <Page<void>>[ page1, page2 ];
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Navigator(
|
||||||
|
pages: pages,
|
||||||
|
onPopPage: (Route<Object?> route, Object? result) => false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// The page2 should have a back button.
|
||||||
|
expect(
|
||||||
|
find.descendant(
|
||||||
|
of: find.byKey(const ValueKey<String>('2')),
|
||||||
|
matching: find.byType(BackButton),
|
||||||
|
),
|
||||||
|
findsOneWidget
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('AppBar.preferredHeightFor', (WidgetTester tester) async {
|
testWidgets('AppBar.preferredHeightFor', (WidgetTester tester) async {
|
||||||
late double preferredHeight;
|
late double preferredHeight;
|
||||||
late Size preferredSize;
|
late Size preferredSize;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user