您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

1908 行
74 KiB

using System;
using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.service;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
using Rect = Unity.UIWidgets.ui.Rect;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
using Transform = Unity.UIWidgets.widgets.Transform;
namespace Unity.UIWidgets.cupertino {
class NavBarUtils {
public const float _kNavBarPersistentHeight = 44.0f;
public const float _kNavBarLargeTitleHeightExtension = 52.0f;
public const float _kNavBarShowLargeTitleThreshold = 10.0f;
public const float _kNavBarEdgePadding = 16.0f;
public const float _kNavBarBackButtonTapWidth = 50.0f;
public static readonly TimeSpan _kNavBarTitleFadeDuration = TimeSpan.FromMilliseconds(150);
public static readonly Color _kDefaultNavBarBorderColor = new Color(0x4C000000);
public static readonly Border _kDefaultNavBarBorder = new Border(
bottom: new BorderSide(
color: _kDefaultNavBarBorderColor,
0.0f, // One physical pixel.
style: BorderStyle.solid
)
);
public static readonly _HeroTag _defaultHeroTag = new _HeroTag(null);
public static HeroFlightShuttleBuilder _navBarHeroFlightShuttleBuilder =
(flightContext, animation, flightDirection, fromHeroContext, toHeroContext) => {
D.assert(animation != null);
D.assert(fromHeroContext != null);
D.assert(toHeroContext != null);
D.assert(fromHeroContext.widget is Hero);
D.assert(toHeroContext.widget is Hero);
var fromHeroWidget = (Hero) fromHeroContext.widget;
var toHeroWidget = (Hero) toHeroContext.widget;
D.assert(fromHeroWidget.child is _TransitionableNavigationBar);
D.assert(toHeroWidget.child is _TransitionableNavigationBar);
var fromNavBar = (_TransitionableNavigationBar) fromHeroWidget.child;
var toNavBar = (_TransitionableNavigationBar) toHeroWidget.child;
D.assert(fromNavBar.componentsKeys != null);
D.assert(toNavBar.componentsKeys != null);
D.assert(
fromNavBar.componentsKeys.navBarBoxKey.currentContext.owner != null,
() => "The from nav bar to Hero must have been mounted in the previous frame"
);
D.assert(
toNavBar.componentsKeys.navBarBoxKey.currentContext.owner != null,
() => "The to nav bar to Hero must have been mounted in the previous frame"
);
switch (flightDirection) {
case HeroFlightDirection.push:
return new _NavigationBarTransition(
animation: animation,
bottomNavBar: fromNavBar,
topNavBar: toNavBar
);
case HeroFlightDirection.pop:
return new _NavigationBarTransition(
animation: animation,
bottomNavBar: toNavBar,
topNavBar: fromNavBar
);
}
return null;
};
public static CreateRectTween _linearTranslateWithLargestRectSizeTween = (begin, end) => {
var largestSize = new Size(
Mathf.Max(a: begin.size.width, b: end.size.width),
Mathf.Max(a: begin.size.height, b: end.size.height)
);
return new RectTween(
begin.topLeft & largestSize,
end.topLeft & largestSize
);
};
public static HeroPlaceholderBuilder _navBarHeroLaunchPadBuilder = (context, heroSize, child) => {
D.assert(child is _TransitionableNavigationBar);
return new Visibility(
maintainSize: true,
maintainAnimation: true,
maintainState: true,
visible: false,
child: child
);
};
public static Widget _wrapWithBackground(
Border border = null,
Color backgroundColor = null,
Brightness? brightness = null,
Widget child = null,
bool updateSystemUiOverlay = true
) {
var result = child;
if (updateSystemUiOverlay) {
var isDark = backgroundColor.computeLuminance() < 0.179;
var newBrightness = brightness ?? (isDark ? Brightness.dark : Brightness.light);
SystemUiOverlayStyle overlayStyle;
switch (newBrightness) {
case Brightness.dark:
overlayStyle = SystemUiOverlayStyle.light;
break;
case Brightness.light:
default:
overlayStyle = SystemUiOverlayStyle.dark;
break;
}
result = new AnnotatedRegion<SystemUiOverlayStyle>(
value: overlayStyle,
sized: true,
child: result
);
}
var childWithBackground = new DecoratedBox(
decoration: new BoxDecoration(
border: border,
color: backgroundColor
),
child: result
);
if (backgroundColor.alpha == 0xFF) {
return childWithBackground;
}
return new ClipRect(
child: new BackdropFilter(
filter: ImageFilter.blur(10.0f, 10.0f),
child: childWithBackground
)
);
}
public static Widget _wrapActiveColor(Color color, BuildContext context, Widget child) {
if (color == null) {
return child;
}
return new CupertinoTheme(
data: CupertinoTheme.of(context: context).copyWith(primaryColor: color),
child: child
);
}
public static bool _isTransitionable(BuildContext context) {
var route = ModalRoute.of(context: context);
return route is PageRoute && !(route as PageRoute).fullscreenDialog;
}
}
class _HeroTag {
public readonly NavigatorState navigator;
public _HeroTag(
NavigatorState navigator
) {
this.navigator = navigator;
}
public override string ToString() {
return $"Default Hero tag for Cupertino navigation bars with navigator {navigator}";
}
public bool Equals(_HeroTag other) {
if (ReferenceEquals(null, objB: other)) {
return false;
}
if (ReferenceEquals(this, objB: other)) {
return true;
}
return other is _HeroTag && navigator == other.navigator;
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, objB: obj)) {
return false;
}
if (ReferenceEquals(this, objB: obj)) {
return true;
}
if (obj.GetType() != GetType()) {
return false;
}
return Equals((_HeroTag) obj);
}
public override int GetHashCode() {
return navigator.GetHashCode();
}
public static bool operator ==(_HeroTag left, _HeroTag right) {
return Equals(objA: left, objB: right);
}
public static bool operator !=(_HeroTag left, _HeroTag right) {
return !Equals(objA: left, objB: right);
}
}
public class CupertinoNavigationBar : ObstructingPreferredSizeWidget {
public readonly Color actionsForegroundColor;
public readonly bool automaticallyImplyLeading;
public readonly bool automaticallyImplyMiddle;
public readonly Color backgroundColor;
public readonly Border border;
public readonly Brightness? brightness;
public readonly object heroTag;
public readonly Widget leading;
public readonly Widget middle;
public readonly EdgeInsetsDirectional padding;
public readonly string previousPageTitle;
public readonly Widget trailing;
public readonly bool transitionBetweenRoutes;
public CupertinoNavigationBar(
Key key = null,
Widget leading = null,
bool automaticallyImplyLeading = true,
bool automaticallyImplyMiddle = true,
string previousPageTitle = null,
Widget middle = null,
Widget trailing = null,
Border border = null,
Color backgroundColor = null,
Brightness? brightness = null,
EdgeInsetsDirectional padding = null,
Color actionsForegroundColor = null,
bool transitionBetweenRoutes = true,
object heroTag = null
) : base(key: key) {
this.leading = leading;
this.automaticallyImplyLeading = automaticallyImplyLeading;
this.automaticallyImplyMiddle = automaticallyImplyMiddle;
this.previousPageTitle = previousPageTitle;
this.middle = middle;
this.trailing = trailing;
this.border = border ?? NavBarUtils._kDefaultNavBarBorder;
this.backgroundColor = backgroundColor;
this.brightness = brightness;
this.padding = padding;
this.actionsForegroundColor = actionsForegroundColor;
this.transitionBetweenRoutes = transitionBetweenRoutes;
this.heroTag = heroTag ?? NavBarUtils._defaultHeroTag;
D.assert(
this.heroTag != null,
() => "heroTag cannot be null. Use transitionBetweenRoutes = false to " +
"disable Hero transition on this navigation bar."
);
D.assert(
!transitionBetweenRoutes || ReferenceEquals(objA: this.heroTag, objB: NavBarUtils._defaultHeroTag),
() => "Cannot specify a heroTag override if this navigation bar does not " +
"transition due to transitionBetweenRoutes = false."
);
}
public override Size preferredSize {
get { return Size.fromHeight(height: NavBarUtils._kNavBarPersistentHeight); }
}
public override bool shouldFullyObstruct(BuildContext context) {
var backgroundColor = CupertinoDynamicColor.resolve(resolvable: this.backgroundColor, context: context)
?? CupertinoTheme.of(context: context).barBackgroundColor;
return backgroundColor.alpha == 0xFF;
}
public override State createState() {
return new _CupertinoNavigationBarState();
}
}
class _CupertinoNavigationBarState : State<CupertinoNavigationBar> {
_NavigationBarStaticComponentsKeys keys;
public override void initState() {
base.initState();
keys = new _NavigationBarStaticComponentsKeys();
}
public override Widget build(BuildContext context) {
var backgroundColor =
CupertinoDynamicColor.resolve(resolvable: widget.backgroundColor, context: context) ??
CupertinoTheme.of(context: context).barBackgroundColor;
var components = new _NavigationBarStaticComponents(
keys: keys,
route: ModalRoute.of(context: context),
userLeading: widget.leading,
automaticallyImplyLeading: widget.automaticallyImplyLeading,
automaticallyImplyTitle: widget.automaticallyImplyMiddle,
previousPageTitle: widget.previousPageTitle,
userMiddle: widget.middle,
userTrailing: widget.trailing,
padding: widget.padding,
userLargeTitle: null,
large: false
);
var navBar = NavBarUtils._wrapWithBackground(
border: widget.border,
backgroundColor: backgroundColor,
brightness: widget.brightness,
new DefaultTextStyle(
style: CupertinoTheme.of(context: context).textTheme.textStyle,
child: new _PersistentNavigationBar(
components: components,
padding: widget.padding
)
)
);
var actionsForegroundColor = CupertinoDynamicColor.resolve(
resolvable: widget.actionsForegroundColor, // ignore: deprecated_member_use_from_same_package
context: context
);
if (!widget.transitionBetweenRoutes || !NavBarUtils._isTransitionable(context: context)) {
return NavBarUtils._wrapActiveColor(color: actionsForegroundColor, context: context, child: navBar);
}
return NavBarUtils._wrapActiveColor(
color: actionsForegroundColor,
context: context,
new Builder(
builder: _context => {
return new Hero(
tag: widget.heroTag as _HeroTag == NavBarUtils._defaultHeroTag
? new _HeroTag(Navigator.of(context: _context))
: widget.heroTag,
createRectTween: NavBarUtils._linearTranslateWithLargestRectSizeTween,
placeholderBuilder: NavBarUtils._navBarHeroLaunchPadBuilder,
flightShuttleBuilder: NavBarUtils._navBarHeroFlightShuttleBuilder,
transitionOnUserGestures: true,
child: new _TransitionableNavigationBar(
componentsKeys: keys,
backgroundColor: backgroundColor,
backButtonTextStyle: CupertinoTheme.of(context: _context).textTheme.navActionTextStyle,
titleTextStyle: CupertinoTheme.of(context: _context).textTheme.navTitleTextStyle,
null,
border: widget.border,
widget.middle != null,
false,
child: navBar
)
);
}
)
);
}
}
public class CupertinoSliverNavigationBar : StatefulWidget {
public readonly Color actionsForegroundColor;
public readonly bool automaticallyImplyLeading;
public readonly bool automaticallyImplyTitle;
public readonly Color backgroundColor;
public readonly Border border;
public readonly Brightness? brightness;
public readonly object heroTag;
public readonly Widget largeTitle;
public readonly Widget leading;
public readonly Widget middle;
public readonly EdgeInsetsDirectional padding;
public readonly string previousPageTitle;
public readonly Widget trailing;
public readonly bool transitionBetweenRoutes;
public CupertinoSliverNavigationBar(
Key key = null,
Widget largeTitle = null,
Widget leading = null,
bool automaticallyImplyLeading = true,
bool automaticallyImplyTitle = true,
string previousPageTitle = null,
Widget middle = null,
Widget trailing = null,
Border border = null,
Color backgroundColor = null,
Brightness? brightness = null,
EdgeInsetsDirectional padding = null,
Color actionsForegroundColor = null,
bool transitionBetweenRoutes = true,
object heroTag = null
) : base(key: key) {
D.assert(
automaticallyImplyTitle || largeTitle != null,
() => "No largeTitle has been provided but automaticallyImplyTitle is also " +
"false. Either provide a largeTitle or set automaticallyImplyTitle to " +
"true."
);
this.largeTitle = largeTitle;
this.leading = leading;
this.automaticallyImplyLeading = automaticallyImplyLeading;
this.automaticallyImplyTitle = automaticallyImplyTitle;
this.previousPageTitle = previousPageTitle;
this.middle = middle;
this.trailing = trailing;
this.border = border ?? NavBarUtils._kDefaultNavBarBorder;
this.backgroundColor = backgroundColor;
this.brightness = brightness;
this.padding = padding;
this.actionsForegroundColor = actionsForegroundColor;
this.transitionBetweenRoutes = transitionBetweenRoutes;
this.heroTag = heroTag ?? NavBarUtils._defaultHeroTag;
}
public bool opaque {
get { return backgroundColor.alpha == 0xFF; }
}
public override State createState() {
return new _CupertinoSliverNavigationBarState();
}
}
class _CupertinoSliverNavigationBarState : State<CupertinoSliverNavigationBar> {
_NavigationBarStaticComponentsKeys keys;
public override void initState() {
base.initState();
keys = new _NavigationBarStaticComponentsKeys();
}
public override Widget build(BuildContext context) {
var actionsForegroundColor =
CupertinoDynamicColor.resolve(resolvable: widget.actionsForegroundColor,
context: context) // ignore: deprecated_member_use_from_same_package
?? CupertinoTheme.of(context: context).primaryColor;
var components = new _NavigationBarStaticComponents(
keys: keys,
route: ModalRoute.of(context: context),
userLeading: widget.leading,
automaticallyImplyLeading: widget.automaticallyImplyLeading,
automaticallyImplyTitle: widget.automaticallyImplyTitle,
previousPageTitle: widget.previousPageTitle,
userMiddle: widget.middle,
userTrailing: widget.trailing,
userLargeTitle: widget.largeTitle,
padding: widget.padding,
large: true
);
return NavBarUtils._wrapActiveColor(
color: actionsForegroundColor,
context: context,
new MediaQuery(
data: MediaQuery.of(context: context).copyWith(textScaleFactor: 1),
child: new SliverPersistentHeader(
pinned: true, // iOS navigation bars are always pinned.
del: new _LargeTitleNavigationBarSliverDelegate(
keys: keys,
components: components,
userMiddle: widget.middle,
CupertinoDynamicColor.resolve(resolvable: widget.backgroundColor, context: context) ??
CupertinoTheme.of(context: context).barBackgroundColor,
brightness: widget.brightness,
border: widget.border,
padding: widget.padding,
actionsForegroundColor: actionsForegroundColor,
transitionBetweenRoutes: widget.transitionBetweenRoutes,
heroTag: widget.heroTag,
NavBarUtils._kNavBarPersistentHeight + MediaQuery.of(context: context).padding.top,
widget.middle != null
)
)
)
);
}
}
class _LargeTitleNavigationBarSliverDelegate : SliverPersistentHeaderDelegate {
public readonly Color actionsForegroundColor;
public readonly bool alwaysShowMiddle;
public readonly Color backgroundColor;
public readonly Border border;
public readonly Brightness? brightness;
public readonly _NavigationBarStaticComponents components;
public readonly object heroTag;
public readonly _NavigationBarStaticComponentsKeys keys;
public readonly EdgeInsetsDirectional padding;
public readonly float persistentHeight;
public readonly bool transitionBetweenRoutes;
public readonly Widget userMiddle;
public _LargeTitleNavigationBarSliverDelegate(
_NavigationBarStaticComponentsKeys keys = null,
_NavigationBarStaticComponents components = null,
Widget userMiddle = null,
Color backgroundColor = null,
Brightness? brightness = null,
Border border = null,
EdgeInsetsDirectional padding = null,
Color actionsForegroundColor = null,
bool transitionBetweenRoutes = false,
object heroTag = null,
float persistentHeight = 0.0f,
bool alwaysShowMiddle = false
) {
this.keys = keys;
this.components = components;
this.userMiddle = userMiddle;
this.backgroundColor = backgroundColor;
this.border = border;
this.brightness = brightness;
this.padding = padding;
this.actionsForegroundColor = actionsForegroundColor;
this.transitionBetweenRoutes = transitionBetweenRoutes;
this.heroTag = heroTag;
this.persistentHeight = persistentHeight;
this.alwaysShowMiddle = alwaysShowMiddle;
}
public override float? minExtent {
get { return persistentHeight; }
}
public override float? maxExtent {
get { return persistentHeight + NavBarUtils._kNavBarLargeTitleHeightExtension; }
}
public override Widget build(BuildContext context, float shrinkOffset, bool overlapsContent) {
var showLargeTitle =
shrinkOffset < maxExtent - minExtent - NavBarUtils._kNavBarShowLargeTitleThreshold;
var persistentNavigationBar =
new _PersistentNavigationBar(
components: components,
padding: padding,
middleVisible: alwaysShowMiddle ? null : (bool?) !showLargeTitle
);
var navBar = NavBarUtils._wrapWithBackground(
border: border,
CupertinoDynamicColor.resolve(resolvable: backgroundColor, context: context),
brightness: brightness,
new DefaultTextStyle(
style: CupertinoTheme.of(context: context).textTheme.textStyle,
child: new Stack(
fit: StackFit.expand,
children: new List<Widget> {
new Positioned(
top: persistentHeight,
left: 0.0f,
right: 0.0f,
bottom: 0.0f,
child: new ClipRect(
child: new OverflowBox(
minHeight: 0.0f,
maxHeight: float.PositiveInfinity,
alignment: AlignmentDirectional.bottomStart,
child: new Padding(
padding: EdgeInsetsDirectional.only(
start: NavBarUtils._kNavBarEdgePadding,
bottom: 8.0f
),
child: new SafeArea(
top: false,
bottom: false,
child: new AnimatedOpacity(
opacity: showLargeTitle ? 1.0f : 0.0f,
duration: NavBarUtils._kNavBarTitleFadeDuration,
child: new DefaultTextStyle(
style: CupertinoTheme.of(context: context).textTheme
.navLargeTitleTextStyle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
child: components.largeTitle
)
)
)
)
)
)
),
new Positioned(
left: 0.0f,
right: 0.0f,
top: 0.0f,
child: persistentNavigationBar
)
}
)
)
);
if (!transitionBetweenRoutes || !NavBarUtils._isTransitionable(context: context)) {
return navBar;
}
return new Hero(
tag: heroTag as _HeroTag == NavBarUtils._defaultHeroTag
? new _HeroTag(Navigator.of(context: context))
: heroTag,
createRectTween: NavBarUtils._linearTranslateWithLargestRectSizeTween,
flightShuttleBuilder: NavBarUtils._navBarHeroFlightShuttleBuilder,
placeholderBuilder: NavBarUtils._navBarHeroLaunchPadBuilder,
transitionOnUserGestures: true,
child: new _TransitionableNavigationBar(
componentsKeys: keys,
CupertinoDynamicColor.resolve(resolvable: backgroundColor, context: context),
backButtonTextStyle: CupertinoTheme.of(context: context).textTheme.navActionTextStyle,
titleTextStyle: CupertinoTheme.of(context: context).textTheme.navTitleTextStyle,
largeTitleTextStyle: CupertinoTheme.of(context: context).textTheme.navLargeTitleTextStyle,
border: border,
userMiddle != null,
largeExpanded: showLargeTitle,
child: navBar
)
);
}
public override bool shouldRebuild(SliverPersistentHeaderDelegate _oldDelegate) {
var oldDelegate = _oldDelegate as _LargeTitleNavigationBarSliverDelegate;
return components != oldDelegate.components
|| userMiddle != oldDelegate.userMiddle
|| backgroundColor != oldDelegate.backgroundColor
|| border != oldDelegate.border
|| padding != oldDelegate.padding
|| actionsForegroundColor != oldDelegate.actionsForegroundColor
|| transitionBetweenRoutes != oldDelegate.transitionBetweenRoutes
|| persistentHeight != oldDelegate.persistentHeight
|| alwaysShowMiddle != oldDelegate.alwaysShowMiddle
|| heroTag != oldDelegate.heroTag;
}
}
class _PersistentNavigationBar : StatelessWidget {
public readonly _NavigationBarStaticComponents components;
public readonly bool middleVisible;
public readonly EdgeInsetsDirectional padding;
public _PersistentNavigationBar(
Key key = null,
_NavigationBarStaticComponents components = null,
EdgeInsetsDirectional padding = null,
bool? middleVisible = null
) : base(key: key) {
this.components = components;
this.padding = padding;
this.middleVisible = middleVisible ?? true;
}
public override Widget build(BuildContext context) {
Widget middle = components.middle;
if (middle != null) {
middle = new DefaultTextStyle(
style: CupertinoTheme.of(context: context).textTheme.navTitleTextStyle,
child: middle
);
middle = middleVisible == null
? middle
: new AnimatedOpacity(
opacity: middleVisible ? 1.0f : 0.0f,
duration: NavBarUtils._kNavBarTitleFadeDuration,
child: middle
);
}
Widget leading = components.leading;
Widget backChevron = components.backChevron;
Widget backLabel = components.backLabel;
if (leading == null && backChevron != null && backLabel != null) {
leading = CupertinoNavigationBarBackButton._assemble(
_backChevron: backChevron,
_backLabel: backLabel
);
}
Widget paddedToolbar = new NavigationToolbar(
leading: leading,
middle: middle,
trailing: components.trailing,
centerMiddle: true,
middleSpacing: 6.0f
);
if (padding != null) {
paddedToolbar = new Padding(
padding: EdgeInsets.only(
top: padding.top,
bottom: padding.bottom
),
child: paddedToolbar
);
}
return new SizedBox(
height: NavBarUtils._kNavBarPersistentHeight + MediaQuery.of(context: context).padding.top,
child: new SafeArea(
bottom: false,
child: paddedToolbar
)
);
}
}
class _NavigationBarStaticComponentsKeys {
public readonly GlobalKey backChevronKey;
public readonly GlobalKey backLabelKey;
public readonly GlobalKey largeTitleKey;
public readonly GlobalKey leadingKey;
public readonly GlobalKey middleKey;
public readonly GlobalKey navBarBoxKey;
public readonly GlobalKey trailingKey;
public _NavigationBarStaticComponentsKeys() {
navBarBoxKey = GlobalKey.key("Navigation bar render box");
leadingKey = GlobalKey.key("Leading");
backChevronKey = GlobalKey.key("Back chevron");
backLabelKey = GlobalKey.key("Back label");
middleKey = GlobalKey.key("Middle");
trailingKey = GlobalKey.key("Trailing");
largeTitleKey = GlobalKey.key("Large title");
}
}
class _NavigationBarStaticComponents {
public readonly KeyedSubtree backChevron;
public readonly KeyedSubtree backLabel;
public readonly KeyedSubtree largeTitle;
public readonly KeyedSubtree leading;
public readonly KeyedSubtree middle;
public readonly KeyedSubtree trailing;
public _NavigationBarStaticComponents(
bool automaticallyImplyLeading,
bool automaticallyImplyTitle,
bool large,
_NavigationBarStaticComponentsKeys keys = null,
ModalRoute route = null,
Widget userLeading = null,
string previousPageTitle = null,
Widget userMiddle = null,
Widget userTrailing = null,
Widget userLargeTitle = null,
EdgeInsetsDirectional padding = null
) {
leading = createLeading(
leadingKey: keys.leadingKey,
userLeading: userLeading,
route: route,
automaticallyImplyLeading: automaticallyImplyLeading,
padding: padding
);
backChevron = createBackChevron(
backChevronKey: keys.backChevronKey,
userLeading: userLeading,
route: route,
automaticallyImplyLeading: automaticallyImplyLeading
);
backLabel = createBackLabel(
backLabelKey: keys.backLabelKey,
userLeading: userLeading,
route: route,
previousPageTitle: previousPageTitle,
automaticallyImplyLeading: automaticallyImplyLeading
);
middle = createMiddle(
middleKey: keys.middleKey,
userMiddle: userMiddle,
userLargeTitle: userLargeTitle,
route: route,
automaticallyImplyTitle: automaticallyImplyTitle,
large: large
);
trailing = createTrailing(
trailingKey: keys.trailingKey,
userTrailing: userTrailing,
padding: padding
);
largeTitle = createLargeTitle(
largeTitleKey: keys.largeTitleKey,
userLargeTitle: userLargeTitle,
route: route,
automaticImplyTitle: automaticallyImplyTitle,
large: large
);
}
static Widget _derivedTitle(
bool automaticallyImplyTitle,
ModalRoute currentRoute = null
) {
if (automaticallyImplyTitle &&
currentRoute is CupertinoPageRoute route &&
route.title != null) {
return new Text(data: route.title);
}
return null;
}
static KeyedSubtree createLeading(
bool automaticallyImplyLeading,
GlobalKey leadingKey = null,
Widget userLeading = null,
ModalRoute route = null,
EdgeInsetsDirectional padding = null
) {
Widget leadingContent = null;
if (userLeading != null) {
leadingContent = userLeading;
}
else if (
automaticallyImplyLeading &&
route is PageRoute pageRoute &&
route.canPop &&
pageRoute.fullscreenDialog
) {
leadingContent = new CupertinoButton(
child: new Text("Close"),
padding: EdgeInsets.zero,
onPressed: () => { route.navigator.maybePop<object>(); }
);
}
if (leadingContent == null) {
return null;
}
return new KeyedSubtree(
key: leadingKey,
new Padding(
padding: EdgeInsetsDirectional.only(
padding?.start ?? NavBarUtils._kNavBarEdgePadding
),
child: IconTheme.merge(
data: new IconThemeData(
size: 32.0f
),
child: leadingContent
)
)
);
}
static KeyedSubtree createBackChevron(
bool automaticallyImplyLeading,
GlobalKey backChevronKey = null,
Widget userLeading = null,
ModalRoute route = null
) {
if (
userLeading != null ||
!automaticallyImplyLeading ||
route == null ||
!route.canPop ||
route is PageRoute pageRoute && pageRoute.fullscreenDialog
) {
return null;
}
return new KeyedSubtree(key: backChevronKey, new _BackChevron());
}
static KeyedSubtree createBackLabel(
bool automaticallyImplyLeading,
GlobalKey backLabelKey = null,
Widget userLeading = null,
ModalRoute route = null,
string previousPageTitle = null
) {
if (
userLeading != null ||
!automaticallyImplyLeading ||
route == null ||
!route.canPop ||
route is PageRoute pageRoute && pageRoute.fullscreenDialog
) {
return null;
}
return new KeyedSubtree(
key: backLabelKey,
new _BackLabel(
specifiedPreviousTitle: previousPageTitle,
route: route
)
);
}
static KeyedSubtree createMiddle(
bool large,
bool automaticallyImplyTitle,
GlobalKey middleKey = null,
Widget userMiddle = null,
Widget userLargeTitle = null,
ModalRoute route = null
) {
var middleContent = userMiddle;
if (large) {
middleContent = middleContent ?? userLargeTitle;
}
middleContent = middleContent ??
_derivedTitle(automaticallyImplyTitle: automaticallyImplyTitle, currentRoute: route);
if (middleContent == null) {
return null;
}
return new KeyedSubtree(
key: middleKey,
child: middleContent
);
}
static KeyedSubtree createTrailing(
GlobalKey trailingKey = null,
Widget userTrailing = null,
EdgeInsetsDirectional padding = null
) {
if (userTrailing == null) {
return null;
}
return new KeyedSubtree(
key: trailingKey,
new Padding(
padding: EdgeInsetsDirectional.only(
end: padding?.end ?? NavBarUtils._kNavBarEdgePadding
),
child: IconTheme.merge(
data: new IconThemeData(
size: 32.0f
),
child: userTrailing
)
)
);
}
static KeyedSubtree createLargeTitle(
bool large,
bool automaticImplyTitle,
GlobalKey largeTitleKey = null,
Widget userLargeTitle = null,
ModalRoute route = null
) {
if (!large) {
return null;
}
var largeTitleContent = userLargeTitle ?? _derivedTitle(
automaticallyImplyTitle: automaticImplyTitle,
currentRoute: route);
D.assert(
largeTitleContent != null,
() => "largeTitle was not provided and there was no title from the route."
);
return new KeyedSubtree(
key: largeTitleKey,
child: largeTitleContent
);
}
}
public class CupertinoNavigationBarBackButton : StatelessWidget {
public readonly Widget _backChevron;
public readonly Widget _backLabel;
public readonly Color color;
public readonly VoidCallback onPressed;
public readonly string previousPageTitle;
public CupertinoNavigationBarBackButton(
Key key = null,
Color color = null,
string previousPageTitle = null,
VoidCallback onPressed = null
) : base(key: key) {
_backChevron = null;
_backLabel = null;
this.color = color;
this.previousPageTitle = previousPageTitle;
this.onPressed = onPressed;
}
internal CupertinoNavigationBarBackButton(
Widget backChevron,
Widget backLabel,
Color color = null,
string previousPageTitle = null,
VoidCallback onPressed = null
) {
_backChevron = backChevron;
_backLabel = backLabel;
this.color = color;
this.previousPageTitle = previousPageTitle;
this.onPressed = onPressed;
}
public static CupertinoNavigationBarBackButton _assemble(
Widget _backChevron,
Widget _backLabel
) {
return new CupertinoNavigationBarBackButton(
backChevron: _backChevron,
backLabel: _backLabel
);
}
public override Widget build(BuildContext context) {
var currentRoute = ModalRoute.of(context: context);
if (onPressed == null) {
D.assert(
currentRoute?.canPop == true,
() => "CupertinoNavigationBarBackButton should only be used in routes that can be popped"
);
}
var actionTextStyle = CupertinoTheme.of(context: context).textTheme.navActionTextStyle;
if (color != null) {
actionTextStyle =
actionTextStyle.copyWith(color: CupertinoDynamicColor.resolve(resolvable: color, context: context));
}
return new CupertinoButton(
child: new DefaultTextStyle(
style: actionTextStyle,
child: new ConstrainedBox(
constraints: new BoxConstraints(minWidth: NavBarUtils._kNavBarBackButtonTapWidth),
child: new Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: new List<Widget> {
new Padding(padding: EdgeInsetsDirectional.only(8.0f)),
_backChevron ?? new _BackChevron(),
new Padding(padding: EdgeInsetsDirectional.only(6.0f)),
new Flexible(
child: _backLabel ?? new _BackLabel(
specifiedPreviousTitle: previousPageTitle,
route: currentRoute
)
)
}
)
)
),
padding: EdgeInsets.zero,
onPressed: () => {
if (onPressed != null) {
onPressed();
}
else {
Navigator.maybePop<object>(context: context);
}
}
);
}
}
class _BackChevron : StatelessWidget {
public _BackChevron(Key key = null) : base(key: key) {
}
public override Widget build(BuildContext context) {
var textDirection = Directionality.of(context: context);
var textStyle = DefaultTextStyle.of(context: context).style;
Widget iconWidget = Text.rich(
new TextSpan(
new string(new[] {(char) CupertinoIcons.back.codePoint}),
new TextStyle(
false,
color: textStyle.color,
fontSize: 34.0f,
fontFamily: CupertinoIcons.back.fontFamily
//package: CupertinoIcons.back.fontPackage
)
)
);
var matrix = Matrix4.identity();
matrix.scale(-1.0f, 1.0f, 1.0f);
switch (textDirection) {
case TextDirection.rtl:
iconWidget = new Transform(
transform: matrix,
alignment: Alignment.center,
transformHitTests: false,
child: iconWidget
);
break;
case TextDirection.ltr:
break;
}
return iconWidget;
}
}
class _BackLabel : StatelessWidget {
public readonly ModalRoute route;
public readonly string specifiedPreviousTitle;
public _BackLabel(
Key key = null,
string specifiedPreviousTitle = null,
ModalRoute route = null
) : base(key: key) {
this.specifiedPreviousTitle = specifiedPreviousTitle;
this.route = route;
}
Widget _buildPreviousTitleWidget(BuildContext context, string previousTitle, Widget child) {
if (previousTitle == null) {
return new SizedBox(height: 0.0f, width: 0.0f);
}
var textWidget = new Text(
data: previousTitle,
maxLines: 1,
overflow: TextOverflow.ellipsis
);
if (previousTitle.Length > 12) {
textWidget = new Text("Back");
}
return new Align(
alignment: AlignmentDirectional.centerStart,
widthFactor: 1.0f,
child: textWidget
);
}
public override Widget build(BuildContext context) {
if (specifiedPreviousTitle != null) {
return _buildPreviousTitleWidget(context: context, previousTitle: specifiedPreviousTitle, null);
}
if (route is CupertinoPageRoute && !route.isFirst) {
var cupertinoRoute = route as CupertinoPageRoute;
return new ValueListenableBuilder<string>(
valueListenable: cupertinoRoute.previousTitle,
builder: _buildPreviousTitleWidget
);
}
return new SizedBox(height: 0.0f, width: 0.0f);
}
}
class _TransitionableNavigationBar : StatelessWidget {
public readonly TextStyle backButtonTextStyle;
public readonly Color backgroundColor;
public readonly Border border;
public readonly Widget child;
public readonly _NavigationBarStaticComponentsKeys componentsKeys;
public readonly bool? hasUserMiddle;
public readonly bool? largeExpanded;
public readonly TextStyle largeTitleTextStyle;
public readonly TextStyle titleTextStyle;
public _TransitionableNavigationBar(
_NavigationBarStaticComponentsKeys componentsKeys = null,
Color backgroundColor = null,
TextStyle backButtonTextStyle = null,
TextStyle titleTextStyle = null,
TextStyle largeTitleTextStyle = null,
Border border = null,
bool? hasUserMiddle = null,
bool? largeExpanded = null,
Widget child = null
) : base(key: componentsKeys.navBarBoxKey) {
D.assert(largeExpanded != null);
D.assert(!largeExpanded.Value || largeTitleTextStyle != null);
this.componentsKeys = componentsKeys;
this.backgroundColor = backgroundColor;
this.backButtonTextStyle = backButtonTextStyle;
this.titleTextStyle = titleTextStyle;
this.largeTitleTextStyle = largeTitleTextStyle;
this.border = border;
this.hasUserMiddle = hasUserMiddle;
this.largeExpanded = largeExpanded;
this.child = child;
}
public RenderBox renderBox {
get {
var box = componentsKeys.navBarBoxKey.currentContext.findRenderObject() as RenderBox;
D.assert(
result: box.attached,
() => "_TransitionableNavigationBar.renderBox should be called when building " +
"hero flight shuttles when the from and the to nav bar boxes are already " +
"laid out and painted."
);
return box;
}
}
public override Widget build(BuildContext context) {
D.assert(() => {
bool? inHero = null;
context.visitAncestorElements(ancestor => {
if (ancestor is ComponentElement) {
D.assert(
ancestor.widget.GetType() != typeof(_NavigationBarTransition),
() => "_TransitionableNavigationBar should never re-appear inside " +
"_NavigationBarTransition. Keyed _TransitionableNavigationBar should " +
"only serve as anchor points in routes rather than appearing inside " +
"Hero flights themselves."
);
if (ancestor.widget.GetType() == typeof(Hero)) {
inHero = true;
}
}
inHero = inHero ?? false;
return true;
});
D.assert(
inHero == true,
() => "_TransitionableNavigationBar should only be added as the immediate " +
"child of Hero widgets."
);
return true;
});
return child;
}
}
class _NavigationBarTransition : StatelessWidget {
public readonly Animation<float> animation;
public readonly ColorTween backgroundTween;
public readonly BorderTween borderTween;
public readonly _TransitionableNavigationBar bottomNavBar;
public readonly FloatTween heightTween;
public readonly _TransitionableNavigationBar topNavBar;
public _NavigationBarTransition(
Animation<float> animation = null,
_TransitionableNavigationBar topNavBar = null,
_TransitionableNavigationBar bottomNavBar = null
) {
this.animation = animation;
this.topNavBar = topNavBar;
this.bottomNavBar = bottomNavBar;
heightTween = new FloatTween(
begin: bottomNavBar.renderBox.size.height,
end: topNavBar.renderBox.size.height
);
backgroundTween = new ColorTween(
begin: bottomNavBar.backgroundColor,
end: topNavBar.backgroundColor
);
borderTween = new BorderTween(
begin: bottomNavBar.border,
end: topNavBar.border
);
}
public override Widget build(BuildContext context) {
var componentsTransition = new _NavigationBarComponentsTransition(
animation: animation,
bottomNavBar: bottomNavBar,
topNavBar: topNavBar,
Directionality.of(context: context)
);
var children = new List<Widget> {
new AnimatedBuilder(
animation: animation,
(_context, child) => {
return NavBarUtils._wrapWithBackground(
updateSystemUiOverlay: false,
backgroundColor: backgroundTween.evaluate(animation: animation),
border: borderTween.evaluate(animation: animation),
child: new SizedBox(
height: heightTween.evaluate(animation: animation),
width: float.PositiveInfinity
)
);
}
),
componentsTransition.bottomBackChevron,
componentsTransition.bottomBackLabel,
componentsTransition.bottomLeading,
componentsTransition.bottomMiddle,
componentsTransition.bottomLargeTitle,
componentsTransition.bottomTrailing,
componentsTransition.topLeading,
componentsTransition.topBackChevron,
componentsTransition.topBackLabel,
componentsTransition.topMiddle,
componentsTransition.topLargeTitle,
componentsTransition.topTrailing
};
children.RemoveAll(child => child == null);
return new SizedBox(
height: Mathf.Max(a: heightTween.begin, b: heightTween.end) +
MediaQuery.of(context: context).padding.top,
width: float.PositiveInfinity,
child: new Stack(
children: children
)
);
}
}
class _NavigationBarComponentsTransition {
public static Animatable<float> fadeOut = new FloatTween(
1.0f,
0.0f
);
public static Animatable<float> fadeIn = new FloatTween(
0.0f,
1.0f
);
public readonly Animation<float> animation;
public readonly TextStyle bottomBackButtonTextStyle;
public readonly _NavigationBarStaticComponentsKeys bottomComponents;
public readonly bool? bottomHasUserMiddle;
public readonly bool? bottomLargeExpanded;
public readonly TextStyle bottomLargeTitleTextStyle;
public readonly RenderBox bottomNavBarBox;
public readonly TextStyle bottomTitleTextStyle;
public readonly float forwardDirection;
public readonly TextStyle topBackButtonTextStyle;
public readonly _NavigationBarStaticComponentsKeys topComponents;
public readonly bool? topHasUserMiddle;
public readonly bool? topLargeExpanded;
public readonly TextStyle topLargeTitleTextStyle;
public readonly RenderBox topNavBarBox;
public readonly TextStyle topTitleTextStyle;
public readonly Rect transitionBox;
public _NavigationBarComponentsTransition(
Animation<float> animation = null,
_TransitionableNavigationBar bottomNavBar = null,
_TransitionableNavigationBar topNavBar = null,
TextDirection? directionality = null
) {
this.animation = animation;
bottomComponents = bottomNavBar.componentsKeys;
topComponents = topNavBar.componentsKeys;
bottomNavBarBox = bottomNavBar.renderBox;
topNavBarBox = topNavBar.renderBox;
bottomBackButtonTextStyle = bottomNavBar.backButtonTextStyle;
topBackButtonTextStyle = topNavBar.backButtonTextStyle;
bottomTitleTextStyle = bottomNavBar.titleTextStyle;
topTitleTextStyle = topNavBar.titleTextStyle;
bottomLargeTitleTextStyle = bottomNavBar.largeTitleTextStyle;
topLargeTitleTextStyle = topNavBar.largeTitleTextStyle;
bottomHasUserMiddle = bottomNavBar.hasUserMiddle;
topHasUserMiddle = topNavBar.hasUserMiddle;
bottomLargeExpanded = bottomNavBar.largeExpanded;
topLargeExpanded = topNavBar.largeExpanded;
transitionBox =
bottomNavBar.renderBox.paintBounds.expandToInclude(other: topNavBar.renderBox.paintBounds);
forwardDirection = directionality == TextDirection.ltr ? 1.0f : -1.0f;
}
public Widget bottomLeading {
get {
var bottomLeading = bottomComponents.leadingKey.currentWidget as KeyedSubtree;
if (bottomLeading == null) {
return null;
}
return Positioned.fromRelativeRect(
rect: positionInTransitionBox(key: bottomComponents.leadingKey, from: bottomNavBarBox),
child: new FadeTransition(
opacity: fadeOutBy(0.4f),
child: bottomLeading.child
)
);
}
}
public Widget bottomBackChevron {
get {
var bottomBackChevron = bottomComponents.backChevronKey.currentWidget as KeyedSubtree;
if (bottomBackChevron == null) {
return null;
}
return Positioned.fromRelativeRect(
rect: positionInTransitionBox(key: bottomComponents.backChevronKey, from: bottomNavBarBox),
child: new FadeTransition(
opacity: fadeOutBy(0.6f),
child: new DefaultTextStyle(
style: bottomBackButtonTextStyle,
child: bottomBackChevron.child
)
)
);
}
}
public Widget bottomBackLabel {
get {
var bottomBackLabel = bottomComponents.backLabelKey.currentWidget as KeyedSubtree;
if (bottomBackLabel == null) {
return null;
}
var from =
positionInTransitionBox(key: bottomComponents.backLabelKey, from: bottomNavBarBox);
var positionTween = new RelativeRectTween(
begin: from,
from.shift(
new Offset(forwardDirection * (-bottomNavBarBox.size.width / 2.0f),
0.0f
)
)
);
return new PositionedTransition(
animation.drive(child: positionTween),
child: new FadeTransition(
opacity: fadeOutBy(0.2f),
child: new DefaultTextStyle(
style: bottomBackButtonTextStyle,
child: bottomBackLabel.child
)
)
);
}
}
public Widget bottomMiddle {
get {
var bottomMiddle = bottomComponents.middleKey.currentWidget as KeyedSubtree;
var topBackLabel = topComponents.backLabelKey.currentWidget as KeyedSubtree;
var topLeading = topComponents.leadingKey.currentWidget as KeyedSubtree;
if (bottomHasUserMiddle != true && bottomLargeExpanded == true) {
return null;
}
if (bottomMiddle != null && topBackLabel != null) {
return new PositionedTransition(
animation.drive(slideFromLeadingEdge(
fromKey: bottomComponents.middleKey,
fromNavBarBox: bottomNavBarBox,
toKey: topComponents.backLabelKey,
toNavBarBox: topNavBarBox
)),
child: new FadeTransition(
opacity: fadeOutBy(bottomHasUserMiddle == true ? 0.4f : 0.7f),
child: new Align(
alignment: AlignmentDirectional.centerStart,
child: new DefaultTextStyleTransition(
animation.drive(new TextStyleTween(
begin: bottomTitleTextStyle,
end: topBackButtonTextStyle
)),
child: bottomMiddle.child
)
)
)
);
}
if (bottomMiddle != null && topLeading != null) {
return Positioned.fromRelativeRect(
rect: positionInTransitionBox(key: bottomComponents.middleKey, from: bottomNavBarBox),
child: new FadeTransition(
opacity: fadeOutBy(bottomHasUserMiddle == true ? 0.4f : 0.7f),
child: new DefaultTextStyle(
style: bottomTitleTextStyle,
child: bottomMiddle.child
)
)
);
}
return null;
}
}
public Widget bottomLargeTitle {
get {
var bottomLargeTitle = (KeyedSubtree) bottomComponents.largeTitleKey.currentWidget;
var topBackLabel = (KeyedSubtree) topComponents.backLabelKey.currentWidget;
var topLeading = (KeyedSubtree) topComponents.leadingKey.currentWidget;
if (bottomLargeTitle == null || bottomLargeExpanded != true) {
return null;
}
if (bottomLargeTitle != null && topBackLabel != null) {
return new PositionedTransition(
animation.drive(slideFromLeadingEdge(
fromKey: bottomComponents.largeTitleKey,
fromNavBarBox: bottomNavBarBox,
toKey: topComponents.backLabelKey,
toNavBarBox: topNavBarBox
)),
child: new FadeTransition(
opacity: fadeOutBy(0.6f),
child: new Align(
alignment: AlignmentDirectional.centerStart,
child: new DefaultTextStyleTransition(
animation.drive(new TextStyleTween(
begin: bottomLargeTitleTextStyle,
end: topBackButtonTextStyle
)),
maxLines: 1,
overflow: TextOverflow.ellipsis,
child: bottomLargeTitle.child
)
)
)
);
}
if (bottomLargeTitle != null && topLeading != null) {
var from = positionInTransitionBox(key: bottomComponents.largeTitleKey, from: bottomNavBarBox);
var positionTween = new RelativeRectTween(
begin: from,
from.shift(
new Offset(forwardDirection * bottomNavBarBox.size.width / 4.0f,
0.0f
)
)
);
return new PositionedTransition(
animation.drive(child: positionTween),
child: new FadeTransition(
opacity: fadeOutBy(0.4f),
child: new DefaultTextStyle(
style: bottomLargeTitleTextStyle,
child: bottomLargeTitle.child
)
)
);
}
return null;
}
}
public Widget bottomTrailing {
get {
var bottomTrailing = (KeyedSubtree) bottomComponents.trailingKey.currentWidget;
if (bottomTrailing == null) {
return null;
}
return Positioned.fromRelativeRect(
rect: positionInTransitionBox(key: bottomComponents.trailingKey, from: bottomNavBarBox),
child: new FadeTransition(
opacity: fadeOutBy(0.6f),
child: bottomTrailing.child
)
);
}
}
public Widget topLeading {
get {
var topLeading = (KeyedSubtree) topComponents.leadingKey.currentWidget;
if (topLeading == null) {
return null;
}
return Positioned.fromRelativeRect(
rect: positionInTransitionBox(key: topComponents.leadingKey, from: topNavBarBox),
child: new FadeTransition(
opacity: fadeInFrom(0.6f),
child: topLeading.child
)
);
}
}
public Widget topBackChevron {
get {
var topBackChevron = (KeyedSubtree) topComponents.backChevronKey.currentWidget;
var bottomBackChevron = (KeyedSubtree) bottomComponents.backChevronKey.currentWidget;
if (topBackChevron == null) {
return null;
}
var to = positionInTransitionBox(key: topComponents.backChevronKey, from: topNavBarBox);
var from = to;
if (bottomBackChevron == null) {
var topBackChevronBox =
(RenderBox) topComponents.backChevronKey.currentContext.findRenderObject();
from = to.shift(
new Offset(forwardDirection * topBackChevronBox.size.width * 2.0f,
0.0f
)
);
}
var positionTween = new RelativeRectTween(
begin: from,
end: to
);
return new PositionedTransition(
animation.drive(child: positionTween),
child: new FadeTransition(
opacity: fadeInFrom(bottomBackChevron == null ? 0.7f : 0.4f),
child: new DefaultTextStyle(
style: topBackButtonTextStyle,
child: topBackChevron.child
)
)
);
}
}
public Widget topBackLabel {
get {
var bottomMiddle = (KeyedSubtree) bottomComponents.middleKey.currentWidget;
var bottomLargeTitle = (KeyedSubtree) bottomComponents.largeTitleKey.currentWidget;
var topBackLabel = (KeyedSubtree) topComponents.backLabelKey.currentWidget;
if (topBackLabel == null) {
return null;
}
var topBackLabelOpacity =
topComponents.backLabelKey.currentContext?.findAncestorRenderObjectOfType<RenderAnimatedOpacity>();
Animation<float> midClickOpacity = null;
if (topBackLabelOpacity != null && topBackLabelOpacity.opacity.value < 1.0f) {
midClickOpacity = animation.drive(new FloatTween(
0.0f,
end: topBackLabelOpacity.opacity.value
));
}
if (bottomLargeTitle != null &&
topBackLabel != null && bottomLargeExpanded.Value) {
return new PositionedTransition(
animation.drive(slideFromLeadingEdge(
fromKey: bottomComponents.largeTitleKey,
fromNavBarBox: bottomNavBarBox,
toKey: topComponents.backLabelKey,
toNavBarBox: topNavBarBox
)),
child: new FadeTransition(
opacity: midClickOpacity ?? fadeInFrom(0.4f),
child: new DefaultTextStyleTransition(
animation.drive(new TextStyleTween(
begin: bottomLargeTitleTextStyle,
end: topBackButtonTextStyle
)),
maxLines: 1,
overflow: TextOverflow.ellipsis,
child: topBackLabel.child
)
)
);
}
if (bottomMiddle != null && topBackLabel != null) {
return new PositionedTransition(
animation.drive(slideFromLeadingEdge(
fromKey: bottomComponents.middleKey,
fromNavBarBox: bottomNavBarBox,
toKey: topComponents.backLabelKey,
toNavBarBox: topNavBarBox
)),
child: new FadeTransition(
opacity: midClickOpacity ?? fadeInFrom(0.3f),
child: new DefaultTextStyleTransition(
animation.drive(new TextStyleTween(
begin: bottomTitleTextStyle,
end: topBackButtonTextStyle
)),
child: topBackLabel.child
)
)
);
}
return null;
}
}
public Widget topMiddle {
get {
var topMiddle = (KeyedSubtree) topComponents.middleKey.currentWidget;
if (topMiddle == null) {
return null;
}
if (topHasUserMiddle != true && topLargeExpanded == true) {
return null;
}
var to = positionInTransitionBox(key: topComponents.middleKey, from: topNavBarBox);
var positionTween = new RelativeRectTween(
to.shift(
new Offset(forwardDirection * topNavBarBox.size.width / 2.0f,
0.0f
)
),
end: to
);
return new PositionedTransition(
animation.drive(child: positionTween),
child: new FadeTransition(
opacity: fadeInFrom(0.25f),
child: new DefaultTextStyle(
style: topTitleTextStyle,
child: topMiddle.child
)
)
);
}
}
public Widget topTrailing {
get {
var topTrailing = (KeyedSubtree) topComponents.trailingKey.currentWidget;
if (topTrailing == null) {
return null;
}
return Positioned.fromRelativeRect(
rect: positionInTransitionBox(key: topComponents.trailingKey, from: topNavBarBox),
child: new FadeTransition(
opacity: fadeInFrom(0.4f),
child: topTrailing.child
)
);
}
}
public Widget topLargeTitle {
get {
var topLargeTitle = (KeyedSubtree) topComponents.largeTitleKey.currentWidget;
if (topLargeTitle == null || topLargeExpanded != true) {
return null;
}
var to = positionInTransitionBox(key: topComponents.largeTitleKey, from: topNavBarBox);
var positionTween = new RelativeRectTween(
to.shift(
new Offset(forwardDirection * topNavBarBox.size.width,
0.0f
)
),
end: to
);
return new PositionedTransition(
animation.drive(child: positionTween),
child: new FadeTransition(
opacity: fadeInFrom(0.3f),
child: new DefaultTextStyle(
style: topLargeTitleTextStyle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
child: topLargeTitle.child
)
)
);
}
}
public RelativeRect positionInTransitionBox(
GlobalKey key = null,
RenderBox from = null
) {
var componentBox = key.currentContext.findRenderObject() as RenderBox;
D.assert(result: componentBox.attached);
return RelativeRect.fromRect(
componentBox.localToGlobal(point: Offset.zero, ancestor: from) & componentBox.size,
container: transitionBox
);
}
public RelativeRectTween slideFromLeadingEdge(
GlobalKey fromKey = null,
RenderBox fromNavBarBox = null,
GlobalKey toKey = null,
RenderBox toNavBarBox = null
) {
var fromRect = positionInTransitionBox(key: fromKey, from: fromNavBarBox);
var fromBox = fromKey.currentContext.findRenderObject() as RenderBox;
var toBox = toKey.currentContext.findRenderObject() as RenderBox;
var toRect =
toBox.localToGlobal(
point: Offset.zero,
ancestor: toNavBarBox
).translate(
0.0f,
-fromBox.size.height / 2 + toBox.size.height / 2
) & fromBox.size; // Keep the from render object"s size.
if (forwardDirection < 0) {
toRect = toRect.translate(-fromBox.size.width + toBox.size.width, 0.0f);
}
return new RelativeRectTween(
begin: fromRect,
RelativeRect.fromRect(rect: toRect, container: transitionBox)
);
}
public Animation<float> fadeInFrom(float t, Curve curve = null) {
return animation.drive(fadeIn.chain(
new CurveTween(new Interval(begin: t, 1.0f, curve ?? Curves.easeIn))
));
}
public Animation<float> fadeOutBy(float t, Curve curve = null) {
return animation.drive(fadeOut.chain(
new CurveTween(new Interval(0.0f, end: t, curve ?? Curves.easeOut))
));
}
}
}