mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Allow heroes to fly across navigators and restrict Cupertino nav bars to per navigator by default (#23322)
This commit is contained in:
parent
87ca3d52a9
commit
17d068d724
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildSystemType</key>
|
||||
<string>Original</string>
|
||||
</dict>
|
||||
</plist>
|
@ -65,13 +65,33 @@ const TextStyle _kLargeTitleTextStyle = TextStyle(
|
||||
|
||||
// There's a single tag for all instances of navigation bars because they can
|
||||
// all transition between each other (per Navigator) via Hero transitions.
|
||||
const _HeroTag _defaultHeroTag = _HeroTag();
|
||||
const _HeroTag _defaultHeroTag = _HeroTag(null);
|
||||
|
||||
class _HeroTag {
|
||||
const _HeroTag();
|
||||
const _HeroTag(this.navigator);
|
||||
|
||||
final NavigatorState navigator;
|
||||
|
||||
// Let the Hero tag be described in tree dumps.
|
||||
@override
|
||||
String toString() => 'Default Hero tag for Cupertino navigation bars';
|
||||
String toString() => 'Default Hero tag for Cupertino navigation bars with navigator $navigator';
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if (other.runtimeType != runtimeType) {
|
||||
return false;
|
||||
}
|
||||
final _HeroTag otherTag = other;
|
||||
return navigator == otherTag.navigator;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return identityHashCode(navigator);
|
||||
}
|
||||
}
|
||||
|
||||
TextStyle _navBarItemStyle(Color color) {
|
||||
@ -331,9 +351,13 @@ class CupertinoNavigationBar extends StatefulWidget implements ObstructingPrefer
|
||||
/// Tag for the navigation bar's Hero widget if [transitionBetweenRoutes] is true.
|
||||
///
|
||||
/// Defaults to a common tag between all [CupertinoNavigationBar] and
|
||||
/// [CupertinoSliverNavigationBar] instances so they can all transition
|
||||
/// between each other as long as there's only one per route. Use this tag
|
||||
/// override with different tags to have multiple navigation bars per route.
|
||||
/// [CupertinoSliverNavigationBar] instances of the same [Navigator]. With the
|
||||
/// default tag, all navigation bars of the same navigator can transition
|
||||
/// between each other as long as there's only one navigation bar per route.
|
||||
///
|
||||
/// This [heroTag] can be overridden to manually handle having multiple
|
||||
/// navigation bars per route or to transition between multiple
|
||||
/// [Navigator]s.
|
||||
///
|
||||
/// Cannot be null. To disable Hero transitions for this navigation bar,
|
||||
/// set [transitionBetweenRoutes] to false.
|
||||
@ -398,7 +422,9 @@ class _CupertinoNavigationBarState extends State<CupertinoNavigationBar> {
|
||||
}
|
||||
|
||||
return Hero(
|
||||
tag: widget.heroTag,
|
||||
tag: widget.heroTag == _defaultHeroTag
|
||||
? _HeroTag(Navigator.of(context))
|
||||
: widget.heroTag,
|
||||
createRectTween: _linearTranslateWithLargestRectSizeTween,
|
||||
placeholderBuilder: _navBarHeroLaunchPadBuilder,
|
||||
flightShuttleBuilder: _navBarHeroFlightShuttleBuilder,
|
||||
@ -733,7 +759,9 @@ class _LargeTitleNavigationBarSliverDelegate
|
||||
}
|
||||
|
||||
return Hero(
|
||||
tag: heroTag,
|
||||
tag: heroTag == _defaultHeroTag
|
||||
? _HeroTag(Navigator.of(context))
|
||||
: heroTag,
|
||||
createRectTween: _linearTranslateWithLargestRectSizeTween,
|
||||
flightShuttleBuilder: _navBarHeroFlightShuttleBuilder,
|
||||
placeholderBuilder: _navBarHeroLaunchPadBuilder,
|
||||
|
@ -222,10 +222,6 @@ class Hero extends StatefulWidget {
|
||||
result[tag] = heroState;
|
||||
}
|
||||
}
|
||||
// Don't perform transitions across different Navigators.
|
||||
if (element.widget is Navigator) {
|
||||
return;
|
||||
}
|
||||
element.visitChildren(visitor);
|
||||
}
|
||||
context.visitChildElements(visitor);
|
||||
|
@ -385,6 +385,82 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Multiple nav bars tags do not conflict if in different navigators',
|
||||
(WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: CupertinoTabScaffold(
|
||||
tabBar: CupertinoTabBar(
|
||||
items: const <BottomNavigationBarItem>[
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(CupertinoIcons.search),
|
||||
title: Text('Tab 1'),
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(CupertinoIcons.settings),
|
||||
title: Text('Tab 2'),
|
||||
),
|
||||
],
|
||||
),
|
||||
tabBuilder: (BuildContext context, int tab) {
|
||||
return CupertinoTabView(
|
||||
builder: (BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text('Tab ${tab + 1} Page 1'),
|
||||
),
|
||||
child: Center(
|
||||
child: CupertinoButton(
|
||||
child: const Text('Next'),
|
||||
onPressed: () {
|
||||
Navigator.push<void>(context, CupertinoPageRoute<void>(
|
||||
title: 'Tab ${tab + 1} Page 2',
|
||||
builder: (BuildContext context) {
|
||||
return const CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(),
|
||||
child: Placeholder(),
|
||||
);
|
||||
}
|
||||
));
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Tab 2'));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Tab 1 Page 1', skipOffstage: false), findsOneWidget);
|
||||
expect(find.text('Tab 2 Page 1'), findsOneWidget);
|
||||
|
||||
// At this point, there are 2 nav bars seeded with the same _defaultHeroTag.
|
||||
// But they're inside different navigators.
|
||||
|
||||
await tester.tap(find.text('Next'));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
|
||||
// One is inside the flight shuttle and another is invisible in the
|
||||
// incoming route in case a new flight needs to be created midflight.
|
||||
expect(find.text('Tab 2 Page 2'), findsNWidgets(2));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
|
||||
expect(find.text('Tab 2 Page 2'), findsOneWidget);
|
||||
// Offstaged by tab 2's navigator.
|
||||
expect(find.text('Tab 2 Page 1', skipOffstage: false), findsOneWidget);
|
||||
// Offstaged by the CupertinoTabScaffold.
|
||||
expect(find.text('Tab 1 Page 1', skipOffstage: false), findsOneWidget);
|
||||
// Never navigated to tab 1 page 2.
|
||||
expect(find.text('Tab 1 Page 2', skipOffstage: false), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('Transition box grows to large title size',
|
||||
(WidgetTester tester) async {
|
||||
await startTransitionBetween(
|
||||
|
Loading…
Reference in New Issue
Block a user