|
|
|
@ -52,8 +52,7 @@ class _HeroTag {
|
|
|
|
|
|
|
|
|
|
// Let the Hero tag be described in tree dumps.
|
|
|
|
|
@override
|
|
|
|
|
String toString() =>
|
|
|
|
|
'Default Hero tag for Cupertino navigation bars with navigator $navigator';
|
|
|
|
|
String toString() => 'Default Hero tag for Cupertino navigation bars with navigator $navigator';
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
bool operator ==(Object other) {
|
|
|
|
@ -130,9 +129,8 @@ Widget _wrapWithBackground({
|
|
|
|
|
Widget result = child;
|
|
|
|
|
if (updateSystemUiOverlay) {
|
|
|
|
|
final bool isDark = backgroundColor.computeLuminance() < 0.179;
|
|
|
|
|
final Brightness newBrightness =
|
|
|
|
|
brightness ?? (isDark ? Brightness.dark : Brightness.light);
|
|
|
|
|
final SystemUiOverlayStyle overlayStyle;
|
|
|
|
|
final Brightness newBrightness = brightness ?? (isDark ? Brightness.dark : Brightness.light);
|
|
|
|
|
SystemUiOverlayStyle overlayStyle;
|
|
|
|
|
switch (newBrightness) {
|
|
|
|
|
case Brightness.dark:
|
|
|
|
|
overlayStyle = SystemUiOverlayStyle.light;
|
|
|
|
@ -141,6 +139,12 @@ Widget _wrapWithBackground({
|
|
|
|
|
overlayStyle = SystemUiOverlayStyle.dark;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
overlayStyle = overlayStyle.copyWith(
|
|
|
|
|
systemStatusBarContrastEnforced: false,
|
|
|
|
|
systemNavigationBarColor: CupertinoColors.black.withAlpha(1),
|
|
|
|
|
systemNavigationBarContrastEnforced: false,
|
|
|
|
|
systemNavigationBarIconBrightness: Brightness.dark,
|
|
|
|
|
);
|
|
|
|
|
result = AnnotatedRegion<SystemUiOverlayStyle>(
|
|
|
|
|
value: overlayStyle,
|
|
|
|
|
child: result,
|
|
|
|
@ -233,8 +237,7 @@ bool _isTransitionable(BuildContext context) {
|
|
|
|
|
/// * [IsekaiSliverNavigationBar] for a navigation bar to be placed in a
|
|
|
|
|
/// scrolling list and that supports iOS-11-style large titles.
|
|
|
|
|
/// * <https://developer.apple.com/design/human-interface-guidelines/ios/bars/navigation-bars/>
|
|
|
|
|
class IsekaiNavigationBar extends StatefulWidget
|
|
|
|
|
implements ObstructingPreferredSizeWidget {
|
|
|
|
|
class IsekaiNavigationBar extends StatefulWidget implements ObstructingPreferredSizeWidget {
|
|
|
|
|
/// Creates a navigation bar in the iOS style.
|
|
|
|
|
const IsekaiNavigationBar({
|
|
|
|
|
super.key,
|
|
|
|
@ -447,8 +450,7 @@ class _IsekaiNavigationBarState extends State<IsekaiNavigationBar> {
|
|
|
|
|
CupertinoDynamicColor.maybeResolve(widget.backgroundColor, context) ??
|
|
|
|
|
CupertinoTheme.of(context).barBackgroundColor;
|
|
|
|
|
|
|
|
|
|
final _NavigationBarStaticComponents components =
|
|
|
|
|
_NavigationBarStaticComponents(
|
|
|
|
|
final _NavigationBarStaticComponents components = _NavigationBarStaticComponents(
|
|
|
|
|
keys: keys,
|
|
|
|
|
route: ModalRoute.of(context),
|
|
|
|
|
userLeading: widget.leading,
|
|
|
|
@ -484,9 +486,7 @@ class _IsekaiNavigationBarState extends State<IsekaiNavigationBar> {
|
|
|
|
|
// Get the context that might have a possibly changed CupertinoTheme.
|
|
|
|
|
builder: (BuildContext context) {
|
|
|
|
|
return Hero(
|
|
|
|
|
tag: widget.heroTag == _defaultHeroTag
|
|
|
|
|
? _HeroTag(Navigator.of(context))
|
|
|
|
|
: widget.heroTag,
|
|
|
|
|
tag: widget.heroTag == _defaultHeroTag ? _HeroTag(Navigator.of(context)) : widget.heroTag,
|
|
|
|
|
createRectTween: _linearTranslateWithLargestRectSizeTween,
|
|
|
|
|
placeholderBuilder: _navBarHeroLaunchPadBuilder,
|
|
|
|
|
flightShuttleBuilder: _navBarHeroFlightShuttleBuilder,
|
|
|
|
@ -494,10 +494,8 @@ class _IsekaiNavigationBarState extends State<IsekaiNavigationBar> {
|
|
|
|
|
child: _TransitionableNavigationBar(
|
|
|
|
|
componentsKeys: keys,
|
|
|
|
|
backgroundColor: backgroundColor,
|
|
|
|
|
backButtonTextStyle:
|
|
|
|
|
CupertinoTheme.of(context).textTheme.navActionTextStyle,
|
|
|
|
|
titleTextStyle:
|
|
|
|
|
CupertinoTheme.of(context).textTheme.navTitleTextStyle,
|
|
|
|
|
backButtonTextStyle: CupertinoTheme.of(context).textTheme.navActionTextStyle,
|
|
|
|
|
titleTextStyle: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
|
|
|
|
|
largeTitleTextStyle: null,
|
|
|
|
|
border: widget.border,
|
|
|
|
|
hasUserMiddle: widget.middle != null,
|
|
|
|
@ -696,8 +694,7 @@ class IsekaiSliverNavigationBar extends StatefulWidget {
|
|
|
|
|
final bool stretch;
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
State<IsekaiSliverNavigationBar> createState() =>
|
|
|
|
|
_IsekaiSliverNavigationBarState();
|
|
|
|
|
State<IsekaiSliverNavigationBar> createState() => _IsekaiSliverNavigationBarState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A state class exists for the nav bar so that the keys of its sub-components
|
|
|
|
@ -714,8 +711,7 @@ class _IsekaiSliverNavigationBarState extends State<IsekaiSliverNavigationBar> {
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
final _NavigationBarStaticComponents components =
|
|
|
|
|
_NavigationBarStaticComponents(
|
|
|
|
|
final _NavigationBarStaticComponents components = _NavigationBarStaticComponents(
|
|
|
|
|
keys: keys,
|
|
|
|
|
route: ModalRoute.of(context),
|
|
|
|
|
userLeading: widget.leading,
|
|
|
|
@ -736,8 +732,7 @@ class _IsekaiSliverNavigationBarState extends State<IsekaiSliverNavigationBar> {
|
|
|
|
|
keys: keys,
|
|
|
|
|
components: components,
|
|
|
|
|
userMiddle: widget.middle,
|
|
|
|
|
backgroundColor: CupertinoDynamicColor.maybeResolve(
|
|
|
|
|
widget.backgroundColor, context) ??
|
|
|
|
|
backgroundColor: CupertinoDynamicColor.maybeResolve(widget.backgroundColor, context) ??
|
|
|
|
|
CupertinoTheme.of(context).barBackgroundColor,
|
|
|
|
|
brightness: widget.brightness,
|
|
|
|
|
border: widget.border,
|
|
|
|
@ -745,18 +740,17 @@ class _IsekaiSliverNavigationBarState extends State<IsekaiSliverNavigationBar> {
|
|
|
|
|
actionsForegroundColor: CupertinoTheme.of(context).primaryColor,
|
|
|
|
|
transitionBetweenRoutes: widget.transitionBetweenRoutes,
|
|
|
|
|
heroTag: widget.heroTag,
|
|
|
|
|
persistentHeight: (_kNavBarPersistentHeight * scaleFactor) +
|
|
|
|
|
MediaQuery.of(context).padding.top,
|
|
|
|
|
persistentHeight:
|
|
|
|
|
(_kNavBarPersistentHeight * scaleFactor) + MediaQuery.of(context).padding.top,
|
|
|
|
|
alwaysShowMiddle: widget.middle != null,
|
|
|
|
|
stretchConfiguration:
|
|
|
|
|
widget.stretch ? OverScrollHeaderStretchConfiguration() : null,
|
|
|
|
|
stretchConfiguration: widget.stretch ? OverScrollHeaderStretchConfiguration() : null,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class _LargeTitleNavigationBarSliverDelegate
|
|
|
|
|
extends SliverPersistentHeaderDelegate with DiagnosticableTreeMixin {
|
|
|
|
|
class _LargeTitleNavigationBarSliverDelegate extends SliverPersistentHeaderDelegate
|
|
|
|
|
with DiagnosticableTreeMixin {
|
|
|
|
|
_LargeTitleNavigationBarSliverDelegate({
|
|
|
|
|
required this.keys,
|
|
|
|
|
required this.components,
|
|
|
|
@ -794,20 +788,17 @@ class _LargeTitleNavigationBarSliverDelegate
|
|
|
|
|
@override
|
|
|
|
|
double get maxExtent =>
|
|
|
|
|
persistentHeight +
|
|
|
|
|
(_kNavBarLargeTitleHeightExtension *
|
|
|
|
|
MediaQuery.of(Get.context!).textScaleFactor);
|
|
|
|
|
(_kNavBarLargeTitleHeightExtension * MediaQuery.of(Get.context!).textScaleFactor);
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
OverScrollHeaderStretchConfiguration? stretchConfiguration;
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(
|
|
|
|
|
BuildContext context, double shrinkOffset, bool overlapsContent) {
|
|
|
|
|
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
|
|
|
|
|
final bool showLargeTitle =
|
|
|
|
|
shrinkOffset < maxExtent - minExtent - _kNavBarShowLargeTitleThreshold;
|
|
|
|
|
|
|
|
|
|
final _PersistentNavigationBar persistentNavigationBar =
|
|
|
|
|
_PersistentNavigationBar(
|
|
|
|
|
final _PersistentNavigationBar persistentNavigationBar = _PersistentNavigationBar(
|
|
|
|
|
components: components,
|
|
|
|
|
padding: padding,
|
|
|
|
|
// If a user specified middle exists, always show it. Otherwise, show
|
|
|
|
@ -851,9 +842,7 @@ class _LargeTitleNavigationBarSliverDelegate
|
|
|
|
|
child: Semantics(
|
|
|
|
|
header: true,
|
|
|
|
|
child: DefaultTextStyle(
|
|
|
|
|
style: CupertinoTheme.of(context)
|
|
|
|
|
.textTheme
|
|
|
|
|
.navLargeTitleTextStyle,
|
|
|
|
|
style: CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle,
|
|
|
|
|
maxLines: 1,
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
child: components.largeTitle!,
|
|
|
|
@ -881,9 +870,7 @@ class _LargeTitleNavigationBarSliverDelegate
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Hero(
|
|
|
|
|
tag: heroTag == _defaultHeroTag
|
|
|
|
|
? _HeroTag(Navigator.of(context))
|
|
|
|
|
: heroTag,
|
|
|
|
|
tag: heroTag == _defaultHeroTag ? _HeroTag(Navigator.of(context)) : heroTag,
|
|
|
|
|
createRectTween: _linearTranslateWithLargestRectSizeTween,
|
|
|
|
|
flightShuttleBuilder: _navBarHeroFlightShuttleBuilder,
|
|
|
|
|
placeholderBuilder: _navBarHeroLaunchPadBuilder,
|
|
|
|
@ -893,13 +880,10 @@ class _LargeTitleNavigationBarSliverDelegate
|
|
|
|
|
// needs to wrap the top level RenderBox rather than a RenderSliver.
|
|
|
|
|
child: _TransitionableNavigationBar(
|
|
|
|
|
componentsKeys: keys,
|
|
|
|
|
backgroundColor:
|
|
|
|
|
CupertinoDynamicColor.resolve(backgroundColor, context),
|
|
|
|
|
backButtonTextStyle:
|
|
|
|
|
CupertinoTheme.of(context).textTheme.navActionTextStyle,
|
|
|
|
|
backgroundColor: CupertinoDynamicColor.resolve(backgroundColor, context),
|
|
|
|
|
backButtonTextStyle: CupertinoTheme.of(context).textTheme.navActionTextStyle,
|
|
|
|
|
titleTextStyle: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
|
|
|
|
|
largeTitleTextStyle:
|
|
|
|
|
CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle,
|
|
|
|
|
largeTitleTextStyle: CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle,
|
|
|
|
|
border: border,
|
|
|
|
|
hasUserMiddle: userMiddle != null,
|
|
|
|
|
largeExpanded: showLargeTitle,
|
|
|
|
@ -994,8 +978,7 @@ class _PersistentNavigationBar extends StatelessWidget {
|
|
|
|
|
double scaleFactor = MediaQuery.of(context).textScaleFactor;
|
|
|
|
|
|
|
|
|
|
return SizedBox(
|
|
|
|
|
height: (_kNavBarPersistentHeight * scaleFactor) +
|
|
|
|
|
MediaQuery.of(context).padding.top,
|
|
|
|
|
height: (_kNavBarPersistentHeight * scaleFactor) + MediaQuery.of(context).padding.top,
|
|
|
|
|
child: SafeArea(
|
|
|
|
|
bottom: false,
|
|
|
|
|
child: paddedToolbar,
|
|
|
|
@ -1354,11 +1337,10 @@ class IsekaiNavigationBarBackButton extends StatelessWidget {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TextStyle actionTextStyle =
|
|
|
|
|
CupertinoTheme.of(context).textTheme.navActionTextStyle;
|
|
|
|
|
TextStyle actionTextStyle = CupertinoTheme.of(context).textTheme.navActionTextStyle;
|
|
|
|
|
if (color != null) {
|
|
|
|
|
actionTextStyle = actionTextStyle.copyWith(
|
|
|
|
|
color: CupertinoDynamicColor.maybeResolve(color, context));
|
|
|
|
|
actionTextStyle =
|
|
|
|
|
actionTextStyle.copyWith(color: CupertinoDynamicColor.maybeResolve(color, context));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return CupertinoButton(
|
|
|
|
@ -1371,8 +1353,7 @@ class IsekaiNavigationBarBackButton extends StatelessWidget {
|
|
|
|
|
child: DefaultTextStyle(
|
|
|
|
|
style: actionTextStyle,
|
|
|
|
|
child: ConstrainedBox(
|
|
|
|
|
constraints:
|
|
|
|
|
const BoxConstraints(minWidth: _kNavBarBackButtonTapWidth),
|
|
|
|
|
constraints: const BoxConstraints(minWidth: _kNavBarBackButtonTapWidth),
|
|
|
|
|
child: Row(
|
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
|
children: <Widget>[
|
|
|
|
@ -1457,8 +1438,7 @@ class _BackLabel extends StatelessWidget {
|
|
|
|
|
|
|
|
|
|
// `child` is never passed in into ValueListenableBuilder so it's always
|
|
|
|
|
// null here and unused.
|
|
|
|
|
Widget _buildPreviousTitleWidget(
|
|
|
|
|
BuildContext context, String? previousTitle, Widget? child) {
|
|
|
|
|
Widget _buildPreviousTitleWidget(BuildContext context, String? previousTitle, Widget? child) {
|
|
|
|
|
previousTitle ??= "返回";
|
|
|
|
|
|
|
|
|
|
Text textWidget = Text(
|
|
|
|
@ -1482,8 +1462,7 @@ class _BackLabel extends StatelessWidget {
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
if (specifiedPreviousTitle != null) {
|
|
|
|
|
return _buildPreviousTitleWidget(context, specifiedPreviousTitle, null);
|
|
|
|
|
} else if (route is CupertinoRouteTransitionMixin<dynamic> &&
|
|
|
|
|
!route!.isFirst) {
|
|
|
|
|
} else if (route is CupertinoRouteTransitionMixin<dynamic> && !route!.isFirst) {
|
|
|
|
|
final CupertinoRouteTransitionMixin<dynamic> cupertinoRoute =
|
|
|
|
|
route! as CupertinoRouteTransitionMixin<dynamic>;
|
|
|
|
|
// There is no timing issue because the previousTitle Listenable changes
|
|
|
|
@ -1534,8 +1513,8 @@ class _TransitionableNavigationBar extends StatelessWidget {
|
|
|
|
|
final Widget child;
|
|
|
|
|
|
|
|
|
|
RenderBox get renderBox {
|
|
|
|
|
final RenderBox box = componentsKeys.navBarBoxKey.currentContext!
|
|
|
|
|
.findRenderObject()! as RenderBox;
|
|
|
|
|
final RenderBox box =
|
|
|
|
|
componentsKeys.navBarBoxKey.currentContext!.findRenderObject()! as RenderBox;
|
|
|
|
|
assert(
|
|
|
|
|
box.attached,
|
|
|
|
|
'_TransitionableNavigationBar.renderBox should be called when building '
|
|
|
|
@ -1645,31 +1624,19 @@ class _NavigationBarTransition extends StatelessWidget {
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
// Draw all the components on top of the empty bar box.
|
|
|
|
|
if (componentsTransition.bottomBackChevron != null)
|
|
|
|
|
componentsTransition.bottomBackChevron!,
|
|
|
|
|
if (componentsTransition.bottomBackLabel != null)
|
|
|
|
|
componentsTransition.bottomBackLabel!,
|
|
|
|
|
if (componentsTransition.bottomLeading != null)
|
|
|
|
|
componentsTransition.bottomLeading!,
|
|
|
|
|
if (componentsTransition.bottomMiddle != null)
|
|
|
|
|
componentsTransition.bottomMiddle!,
|
|
|
|
|
if (componentsTransition.bottomLargeTitle != null)
|
|
|
|
|
componentsTransition.bottomLargeTitle!,
|
|
|
|
|
if (componentsTransition.bottomTrailing != null)
|
|
|
|
|
componentsTransition.bottomTrailing!,
|
|
|
|
|
if (componentsTransition.bottomBackChevron != null) componentsTransition.bottomBackChevron!,
|
|
|
|
|
if (componentsTransition.bottomBackLabel != null) componentsTransition.bottomBackLabel!,
|
|
|
|
|
if (componentsTransition.bottomLeading != null) componentsTransition.bottomLeading!,
|
|
|
|
|
if (componentsTransition.bottomMiddle != null) componentsTransition.bottomMiddle!,
|
|
|
|
|
if (componentsTransition.bottomLargeTitle != null) componentsTransition.bottomLargeTitle!,
|
|
|
|
|
if (componentsTransition.bottomTrailing != null) componentsTransition.bottomTrailing!,
|
|
|
|
|
// Draw top components on top of the bottom components.
|
|
|
|
|
if (componentsTransition.topLeading != null)
|
|
|
|
|
componentsTransition.topLeading!,
|
|
|
|
|
if (componentsTransition.topBackChevron != null)
|
|
|
|
|
componentsTransition.topBackChevron!,
|
|
|
|
|
if (componentsTransition.topBackLabel != null)
|
|
|
|
|
componentsTransition.topBackLabel!,
|
|
|
|
|
if (componentsTransition.topMiddle != null)
|
|
|
|
|
componentsTransition.topMiddle!,
|
|
|
|
|
if (componentsTransition.topLargeTitle != null)
|
|
|
|
|
componentsTransition.topLargeTitle!,
|
|
|
|
|
if (componentsTransition.topTrailing != null)
|
|
|
|
|
componentsTransition.topTrailing!,
|
|
|
|
|
if (componentsTransition.topLeading != null) componentsTransition.topLeading!,
|
|
|
|
|
if (componentsTransition.topBackChevron != null) componentsTransition.topBackChevron!,
|
|
|
|
|
if (componentsTransition.topBackLabel != null) componentsTransition.topBackLabel!,
|
|
|
|
|
if (componentsTransition.topMiddle != null) componentsTransition.topMiddle!,
|
|
|
|
|
if (componentsTransition.topLargeTitle != null) componentsTransition.topLargeTitle!,
|
|
|
|
|
if (componentsTransition.topTrailing != null) componentsTransition.topTrailing!,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// The actual outer box is big enough to contain both the bottom and top
|
|
|
|
@ -1677,8 +1644,7 @@ class _NavigationBarTransition extends StatelessWidget {
|
|
|
|
|
// can actually be outside the linearly lerp'ed Rect in the middle of
|
|
|
|
|
// the animation, such as the topLargeTitle.
|
|
|
|
|
return SizedBox(
|
|
|
|
|
height: math.max(heightTween.begin!, heightTween.end!) +
|
|
|
|
|
MediaQuery.of(context).padding.top,
|
|
|
|
|
height: math.max(heightTween.begin!, heightTween.end!) + MediaQuery.of(context).padding.top,
|
|
|
|
|
width: double.infinity,
|
|
|
|
|
child: Stack(
|
|
|
|
|
children: children,
|
|
|
|
@ -1731,8 +1697,7 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
topLargeExpanded = topNavBar.largeExpanded,
|
|
|
|
|
transitionBox =
|
|
|
|
|
// paintBounds are based on offset zero so it's ok to expand the Rects.
|
|
|
|
|
bottomNavBar.renderBox.paintBounds
|
|
|
|
|
.expandToInclude(topNavBar.renderBox.paintBounds),
|
|
|
|
|
bottomNavBar.renderBox.paintBounds.expandToInclude(topNavBar.renderBox.paintBounds),
|
|
|
|
|
forwardDirection = directionality == TextDirection.ltr ? 1.0 : -1.0;
|
|
|
|
|
|
|
|
|
|
static final Animatable<double> fadeOut = Tween<double>(
|
|
|
|
@ -1779,13 +1744,11 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
GlobalKey key, {
|
|
|
|
|
required RenderBox from,
|
|
|
|
|
}) {
|
|
|
|
|
final RenderBox componentBox =
|
|
|
|
|
key.currentContext!.findRenderObject()! as RenderBox;
|
|
|
|
|
final RenderBox componentBox = key.currentContext!.findRenderObject()! as RenderBox;
|
|
|
|
|
assert(componentBox.attached);
|
|
|
|
|
|
|
|
|
|
return RelativeRect.fromRect(
|
|
|
|
|
componentBox.localToGlobal(Offset.zero, ancestor: from) &
|
|
|
|
|
componentBox.size,
|
|
|
|
|
componentBox.localToGlobal(Offset.zero, ancestor: from) & componentBox.size,
|
|
|
|
|
transitionBox,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
@ -1810,10 +1773,8 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
required RenderBox toNavBarBox,
|
|
|
|
|
required Widget child,
|
|
|
|
|
}) {
|
|
|
|
|
final RenderBox fromBox =
|
|
|
|
|
fromKey.currentContext!.findRenderObject()! as RenderBox;
|
|
|
|
|
final RenderBox toBox =
|
|
|
|
|
toKey.currentContext!.findRenderObject()! as RenderBox;
|
|
|
|
|
final RenderBox fromBox = fromKey.currentContext!.findRenderObject()! as RenderBox;
|
|
|
|
|
final RenderBox toBox = toKey.currentContext!.findRenderObject()! as RenderBox;
|
|
|
|
|
|
|
|
|
|
final bool isLTR = forwardDirection > 0;
|
|
|
|
|
|
|
|
|
@ -1829,8 +1790,7 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
);
|
|
|
|
|
final Offset fromAnchorInFromBox =
|
|
|
|
|
fromBox.localToGlobal(fromAnchorLocal, ancestor: fromNavBarBox);
|
|
|
|
|
final Offset toAnchorInToBox =
|
|
|
|
|
toBox.localToGlobal(toAnchorLocal, ancestor: toNavBarBox);
|
|
|
|
|
final Offset toAnchorInToBox = toBox.localToGlobal(toAnchorLocal, ancestor: toNavBarBox);
|
|
|
|
|
|
|
|
|
|
// We can't get ahold of the render box of the stack (i.e., `transitionBox`)
|
|
|
|
|
// we place components on yet, but we know the stack needs to be top-leading
|
|
|
|
@ -1842,13 +1802,10 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
// coordinates.
|
|
|
|
|
final Offset translation = isLTR
|
|
|
|
|
? toAnchorInToBox - fromAnchorInFromBox
|
|
|
|
|
: Offset(toNavBarBox.size.width - toAnchorInToBox.dx,
|
|
|
|
|
toAnchorInToBox.dy) -
|
|
|
|
|
Offset(fromNavBarBox.size.width - fromAnchorInFromBox.dx,
|
|
|
|
|
fromAnchorInFromBox.dy);
|
|
|
|
|
: Offset(toNavBarBox.size.width - toAnchorInToBox.dx, toAnchorInToBox.dy) -
|
|
|
|
|
Offset(fromNavBarBox.size.width - fromAnchorInFromBox.dx, fromAnchorInFromBox.dy);
|
|
|
|
|
|
|
|
|
|
final RelativeRect fromBoxMargin =
|
|
|
|
|
positionInTransitionBox(fromKey, from: fromNavBarBox);
|
|
|
|
|
final RelativeRect fromBoxMargin = positionInTransitionBox(fromKey, from: fromNavBarBox);
|
|
|
|
|
final Offset fromOriginInTransitionBox = Offset(
|
|
|
|
|
isLTR ? fromBoxMargin.left : fromBoxMargin.right,
|
|
|
|
|
fromBoxMargin.top,
|
|
|
|
@ -1880,16 +1837,14 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget? get bottomLeading {
|
|
|
|
|
final KeyedSubtree? bottomLeading =
|
|
|
|
|
bottomComponents.leadingKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
final KeyedSubtree? bottomLeading = bottomComponents.leadingKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
|
|
|
|
|
if (bottomLeading == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Positioned.fromRelativeRect(
|
|
|
|
|
rect: positionInTransitionBox(bottomComponents.leadingKey,
|
|
|
|
|
from: bottomNavBarBox),
|
|
|
|
|
rect: positionInTransitionBox(bottomComponents.leadingKey, from: bottomNavBarBox),
|
|
|
|
|
child: FadeTransition(
|
|
|
|
|
opacity: fadeOutBy(0.4),
|
|
|
|
|
child: bottomLeading.child,
|
|
|
|
@ -1906,8 +1861,7 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Positioned.fromRelativeRect(
|
|
|
|
|
rect: positionInTransitionBox(bottomComponents.backChevronKey,
|
|
|
|
|
from: bottomNavBarBox),
|
|
|
|
|
rect: positionInTransitionBox(bottomComponents.backChevronKey, from: bottomNavBarBox),
|
|
|
|
|
child: FadeTransition(
|
|
|
|
|
opacity: fadeOutBy(0.6),
|
|
|
|
|
child: DefaultTextStyle(
|
|
|
|
@ -1926,9 +1880,8 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final RelativeRect from = positionInTransitionBox(
|
|
|
|
|
bottomComponents.backLabelKey,
|
|
|
|
|
from: bottomNavBarBox);
|
|
|
|
|
final RelativeRect from =
|
|
|
|
|
positionInTransitionBox(bottomComponents.backLabelKey, from: bottomNavBarBox);
|
|
|
|
|
|
|
|
|
|
// Transition away by sliding horizontally to the leading edge off of the screen.
|
|
|
|
|
final RelativeRectTween positionTween = RelativeRectTween(
|
|
|
|
@ -1954,12 +1907,9 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget? get bottomMiddle {
|
|
|
|
|
final KeyedSubtree? bottomMiddle =
|
|
|
|
|
bottomComponents.middleKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
final KeyedSubtree? topBackLabel =
|
|
|
|
|
topComponents.backLabelKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
final KeyedSubtree? topLeading =
|
|
|
|
|
topComponents.leadingKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
final KeyedSubtree? bottomMiddle = bottomComponents.middleKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
final KeyedSubtree? topBackLabel = topComponents.backLabelKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
final KeyedSubtree? topLeading = topComponents.leadingKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
|
|
|
|
|
// The middle component is non-null when the nav bar is a large title
|
|
|
|
|
// nav bar but would be invisible when expanded, therefore don't show it here.
|
|
|
|
@ -1998,8 +1948,7 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
// fade.
|
|
|
|
|
if (bottomMiddle != null && topLeading != null) {
|
|
|
|
|
return Positioned.fromRelativeRect(
|
|
|
|
|
rect: positionInTransitionBox(bottomComponents.middleKey,
|
|
|
|
|
from: bottomNavBarBox),
|
|
|
|
|
rect: positionInTransitionBox(bottomComponents.middleKey, from: bottomNavBarBox),
|
|
|
|
|
child: FadeTransition(
|
|
|
|
|
opacity: fadeOutBy(bottomHasUserMiddle ? 0.4 : 0.7),
|
|
|
|
|
// Keep the font when transitioning into a non-back label leading.
|
|
|
|
@ -2017,10 +1966,8 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
Widget? get bottomLargeTitle {
|
|
|
|
|
final KeyedSubtree? bottomLargeTitle =
|
|
|
|
|
bottomComponents.largeTitleKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
final KeyedSubtree? topBackLabel =
|
|
|
|
|
topComponents.backLabelKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
final KeyedSubtree? topLeading =
|
|
|
|
|
topComponents.leadingKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
final KeyedSubtree? topBackLabel = topComponents.backLabelKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
final KeyedSubtree? topLeading = topComponents.leadingKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
|
|
|
|
|
if (bottomLargeTitle == null || !bottomLargeExpanded) {
|
|
|
|
|
return null;
|
|
|
|
@ -2056,9 +2003,8 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
if (bottomLargeTitle != null && topLeading != null) {
|
|
|
|
|
// Unlike bottom middle, the bottom large title moves when it can't
|
|
|
|
|
// transition to the top back label position.
|
|
|
|
|
final RelativeRect from = positionInTransitionBox(
|
|
|
|
|
bottomComponents.largeTitleKey,
|
|
|
|
|
from: bottomNavBarBox);
|
|
|
|
|
final RelativeRect from =
|
|
|
|
|
positionInTransitionBox(bottomComponents.largeTitleKey, from: bottomNavBarBox);
|
|
|
|
|
|
|
|
|
|
final RelativeRectTween positionTween = RelativeRectTween(
|
|
|
|
|
begin: from,
|
|
|
|
@ -2097,8 +2043,7 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Positioned.fromRelativeRect(
|
|
|
|
|
rect: positionInTransitionBox(bottomComponents.trailingKey,
|
|
|
|
|
from: bottomNavBarBox),
|
|
|
|
|
rect: positionInTransitionBox(bottomComponents.trailingKey, from: bottomNavBarBox),
|
|
|
|
|
child: FadeTransition(
|
|
|
|
|
opacity: fadeOutBy(0.6),
|
|
|
|
|
child: bottomTrailing.child,
|
|
|
|
@ -2107,16 +2052,14 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget? get topLeading {
|
|
|
|
|
final KeyedSubtree? topLeading =
|
|
|
|
|
topComponents.leadingKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
final KeyedSubtree? topLeading = topComponents.leadingKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
|
|
|
|
|
if (topLeading == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Positioned.fromRelativeRect(
|
|
|
|
|
rect:
|
|
|
|
|
positionInTransitionBox(topComponents.leadingKey, from: topNavBarBox),
|
|
|
|
|
rect: positionInTransitionBox(topComponents.leadingKey, from: topNavBarBox),
|
|
|
|
|
child: FadeTransition(
|
|
|
|
|
opacity: fadeInFrom(0.6),
|
|
|
|
|
child: topLeading.child,
|
|
|
|
@ -2134,17 +2077,15 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final RelativeRect to = positionInTransitionBox(
|
|
|
|
|
topComponents.backChevronKey,
|
|
|
|
|
from: topNavBarBox);
|
|
|
|
|
final RelativeRect to =
|
|
|
|
|
positionInTransitionBox(topComponents.backChevronKey, from: topNavBarBox);
|
|
|
|
|
RelativeRect from = to;
|
|
|
|
|
|
|
|
|
|
// If it's the first page with a back chevron, shift in slightly from the
|
|
|
|
|
// right.
|
|
|
|
|
if (bottomBackChevron == null) {
|
|
|
|
|
final RenderBox topBackChevronBox =
|
|
|
|
|
topComponents.backChevronKey.currentContext!.findRenderObject()!
|
|
|
|
|
as RenderBox;
|
|
|
|
|
topComponents.backChevronKey.currentContext!.findRenderObject()! as RenderBox;
|
|
|
|
|
from = to.shift(
|
|
|
|
|
Offset(
|
|
|
|
|
forwardDirection * topBackChevronBox.size.width * 2.0,
|
|
|
|
@ -2171,24 +2112,20 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget? get topBackLabel {
|
|
|
|
|
final KeyedSubtree? bottomMiddle =
|
|
|
|
|
bottomComponents.middleKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
final KeyedSubtree? bottomMiddle = bottomComponents.middleKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
final KeyedSubtree? bottomLargeTitle =
|
|
|
|
|
bottomComponents.largeTitleKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
final KeyedSubtree? topBackLabel =
|
|
|
|
|
topComponents.backLabelKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
final KeyedSubtree? topBackLabel = topComponents.backLabelKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
|
|
|
|
|
if (topBackLabel == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final RenderAnimatedOpacity? topBackLabelOpacity = topComponents
|
|
|
|
|
.backLabelKey.currentContext
|
|
|
|
|
final RenderAnimatedOpacity? topBackLabelOpacity = topComponents.backLabelKey.currentContext
|
|
|
|
|
?.findAncestorRenderObjectOfType<RenderAnimatedOpacity>();
|
|
|
|
|
|
|
|
|
|
Animation<double>? midClickOpacity;
|
|
|
|
|
if (topBackLabelOpacity != null &&
|
|
|
|
|
topBackLabelOpacity.opacity.value < 1.0) {
|
|
|
|
|
if (topBackLabelOpacity != null && topBackLabelOpacity.opacity.value < 1.0) {
|
|
|
|
|
midClickOpacity = animation.drive(Tween<double>(
|
|
|
|
|
begin: 0.0,
|
|
|
|
|
end: topBackLabelOpacity.opacity.value,
|
|
|
|
@ -2200,9 +2137,7 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
// content text might be different. For instance, if the bottomLargeTitle
|
|
|
|
|
// text is too long, the topBackLabel will say 'Back' instead of the original
|
|
|
|
|
// text.
|
|
|
|
|
if (bottomLargeTitle != null &&
|
|
|
|
|
topBackLabel != null &&
|
|
|
|
|
bottomLargeExpanded) {
|
|
|
|
|
if (bottomLargeTitle != null && topBackLabel != null && bottomLargeExpanded) {
|
|
|
|
|
return slideFromLeadingEdge(
|
|
|
|
|
fromKey: bottomComponents.largeTitleKey,
|
|
|
|
|
fromNavBarBox: bottomNavBarBox,
|
|
|
|
@ -2248,8 +2183,7 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget? get topMiddle {
|
|
|
|
|
final KeyedSubtree? topMiddle =
|
|
|
|
|
topComponents.middleKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
final KeyedSubtree? topMiddle = topComponents.middleKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
|
|
|
|
|
if (topMiddle == null) {
|
|
|
|
|
return null;
|
|
|
|
@ -2261,10 +2195,9 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final RelativeRect to =
|
|
|
|
|
positionInTransitionBox(topComponents.middleKey, from: topNavBarBox);
|
|
|
|
|
final RenderBox toBox = topComponents.middleKey.currentContext!
|
|
|
|
|
.findRenderObject()! as RenderBox;
|
|
|
|
|
final RelativeRect to = positionInTransitionBox(topComponents.middleKey, from: topNavBarBox);
|
|
|
|
|
final RenderBox toBox =
|
|
|
|
|
topComponents.middleKey.currentContext!.findRenderObject()! as RenderBox;
|
|
|
|
|
|
|
|
|
|
final bool isLTR = forwardDirection > 0;
|
|
|
|
|
|
|
|
|
@ -2301,16 +2234,14 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget? get topTrailing {
|
|
|
|
|
final KeyedSubtree? topTrailing =
|
|
|
|
|
topComponents.trailingKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
final KeyedSubtree? topTrailing = topComponents.trailingKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
|
|
|
|
|
if (topTrailing == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Positioned.fromRelativeRect(
|
|
|
|
|
rect: positionInTransitionBox(topComponents.trailingKey,
|
|
|
|
|
from: topNavBarBox),
|
|
|
|
|
rect: positionInTransitionBox(topComponents.trailingKey, from: topNavBarBox),
|
|
|
|
|
child: FadeTransition(
|
|
|
|
|
opacity: fadeInFrom(0.4),
|
|
|
|
|
child: topTrailing.child,
|
|
|
|
@ -2319,15 +2250,14 @@ class _NavigationBarComponentsTransition {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget? get topLargeTitle {
|
|
|
|
|
final KeyedSubtree? topLargeTitle =
|
|
|
|
|
topComponents.largeTitleKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
final KeyedSubtree? topLargeTitle = topComponents.largeTitleKey.currentWidget as KeyedSubtree?;
|
|
|
|
|
|
|
|
|
|
if (topLargeTitle == null || !topLargeExpanded) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final RelativeRect to = positionInTransitionBox(topComponents.largeTitleKey,
|
|
|
|
|
from: topNavBarBox);
|
|
|
|
|
final RelativeRect to =
|
|
|
|
|
positionInTransitionBox(topComponents.largeTitleKey, from: topNavBarBox);
|
|
|
|
|
|
|
|
|
|
// Shift in from the trailing edge of the screen.
|
|
|
|
|
final RelativeRectTween positionTween = RelativeRectTween(
|
|
|
|
@ -2417,8 +2347,7 @@ Widget _navBarHeroFlightShuttleBuilder(
|
|
|
|
|
|
|
|
|
|
final _TransitionableNavigationBar fromNavBar =
|
|
|
|
|
fromHeroWidget.child as _TransitionableNavigationBar;
|
|
|
|
|
final _TransitionableNavigationBar toNavBar =
|
|
|
|
|
toHeroWidget.child as _TransitionableNavigationBar;
|
|
|
|
|
final _TransitionableNavigationBar toNavBar = toHeroWidget.child as _TransitionableNavigationBar;
|
|
|
|
|
|
|
|
|
|
assert(fromNavBar.componentsKeys != null);
|
|
|
|
|
assert(toNavBar.componentsKeys != null);
|
|
|
|
|