您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
616 行
27 KiB
616 行
27 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Unity.UIWidgets.animation;
|
|
using Unity.UIWidgets.foundation;
|
|
using Unity.UIWidgets.gestures;
|
|
using Unity.UIWidgets.material;
|
|
using Unity.UIWidgets.painting;
|
|
using Unity.UIWidgets.physics;
|
|
using Unity.UIWidgets.rendering;
|
|
using Unity.UIWidgets.ui;
|
|
using Unity.UIWidgets.widgets;
|
|
using UnityEngine;
|
|
using Color = Unity.UIWidgets.ui.Color;
|
|
using Rect = Unity.UIWidgets.ui.Rect;
|
|
|
|
namespace UIWidgetsGallery.gallery {
|
|
class AnimationHomeUtils {
|
|
public static readonly Color _kAppBackgroundColor = new Color(0xFF353662);
|
|
public static readonly TimeSpan _kScrollDuration = new TimeSpan(0, 0, 0, 0, 400);
|
|
public static readonly Curve _kScrollCurve = Curves.fastOutSlowIn;
|
|
public const float _kAppBarMinHeight = 90.0f;
|
|
public const float _kAppBarMidHeight = 256.0f;
|
|
}
|
|
|
|
class _RenderStatusBarPaddingSliver : RenderSliver {
|
|
public _RenderStatusBarPaddingSliver(
|
|
float? maxHeight = null,
|
|
float? scrollFactor = null
|
|
) {
|
|
D.assert(maxHeight >= 0.0f);
|
|
D.assert(scrollFactor >= 1.0f);
|
|
this._maxHeight = maxHeight;
|
|
this._scrollFactor = scrollFactor;
|
|
}
|
|
|
|
public float? maxHeight {
|
|
get { return this._maxHeight; }
|
|
set {
|
|
D.assert(this.maxHeight >= 0.0f);
|
|
if (this._maxHeight == value) {
|
|
return;
|
|
}
|
|
|
|
this._maxHeight = value;
|
|
this.markNeedsLayout();
|
|
}
|
|
}
|
|
|
|
float? _maxHeight;
|
|
|
|
public float? scrollFactor {
|
|
get { return this._scrollFactor; }
|
|
set {
|
|
D.assert(this.scrollFactor >= 1.0f);
|
|
if (this._scrollFactor == value) {
|
|
return;
|
|
}
|
|
|
|
this._scrollFactor = value;
|
|
this.markNeedsLayout();
|
|
}
|
|
}
|
|
|
|
float? _scrollFactor;
|
|
|
|
protected override void performLayout() {
|
|
float? height =
|
|
(this.maxHeight - this.constraints.scrollOffset / this.scrollFactor)?.clamp(0.0f,
|
|
this.maxHeight ?? 0.0f);
|
|
this.geometry = new SliverGeometry(
|
|
paintExtent: Mathf.Min(height ?? 0.0f, this.constraints.remainingPaintExtent),
|
|
scrollExtent: this.maxHeight ?? 0.0f,
|
|
maxPaintExtent: this.maxHeight ?? 0.0f
|
|
);
|
|
}
|
|
}
|
|
|
|
class _StatusBarPaddingSliver : SingleChildRenderObjectWidget {
|
|
public _StatusBarPaddingSliver(
|
|
Key key = null,
|
|
float? maxHeight = null,
|
|
float scrollFactor = 5.0f
|
|
) : base(key: key) {
|
|
D.assert(maxHeight != null && maxHeight >= 0.0f);
|
|
D.assert(scrollFactor >= 1.0f);
|
|
this.maxHeight = maxHeight;
|
|
this.scrollFactor = scrollFactor;
|
|
}
|
|
|
|
public readonly float? maxHeight;
|
|
public readonly float scrollFactor;
|
|
|
|
public override RenderObject createRenderObject(BuildContext context) {
|
|
return new _RenderStatusBarPaddingSliver(
|
|
maxHeight: this.maxHeight,
|
|
scrollFactor: this.scrollFactor
|
|
);
|
|
}
|
|
|
|
public override void updateRenderObject(BuildContext context, RenderObject _renderObject) {
|
|
_RenderStatusBarPaddingSliver renderObject = _renderObject as _RenderStatusBarPaddingSliver;
|
|
renderObject.maxHeight = this.maxHeight;
|
|
renderObject.scrollFactor = this.scrollFactor;
|
|
}
|
|
|
|
public override void debugFillProperties(DiagnosticPropertiesBuilder description) {
|
|
base.debugFillProperties(description);
|
|
description.add(new FloatProperty("maxHeight", this.maxHeight));
|
|
description.add(new FloatProperty("scrollFactor", this.scrollFactor));
|
|
}
|
|
}
|
|
|
|
class _SliverAppBarDelegate : SliverPersistentHeaderDelegate {
|
|
public _SliverAppBarDelegate(
|
|
float minHeight,
|
|
float maxHeight,
|
|
Widget child
|
|
) {
|
|
this.minHeight = minHeight;
|
|
this.maxHeight = maxHeight;
|
|
this.child = child;
|
|
}
|
|
|
|
public readonly float minHeight;
|
|
public readonly float maxHeight;
|
|
public readonly Widget child;
|
|
|
|
public override float? minExtent {
|
|
get { return this.minHeight; }
|
|
}
|
|
|
|
public override float? maxExtent {
|
|
get { return Mathf.Max(this.maxHeight, this.minHeight); }
|
|
}
|
|
|
|
public override Widget build(BuildContext context, float shrinkOffset, bool overlapsContent) {
|
|
return SizedBox.expand(child: this.child);
|
|
}
|
|
|
|
public override bool shouldRebuild(SliverPersistentHeaderDelegate _oldDelegate) {
|
|
_SliverAppBarDelegate oldDelegate = _oldDelegate as _SliverAppBarDelegate;
|
|
return this.maxHeight != oldDelegate.maxHeight
|
|
|| this.minHeight != oldDelegate.minHeight
|
|
|| this.child != oldDelegate.child;
|
|
}
|
|
|
|
public override string ToString() {
|
|
return "_SliverAppBarDelegate";
|
|
}
|
|
}
|
|
|
|
class _AllSectionsLayout : MultiChildLayoutDelegate {
|
|
public _AllSectionsLayout(
|
|
Alignment translation,
|
|
float tColumnToRow,
|
|
float tCollapsed,
|
|
int cardCount,
|
|
float selectedIndex
|
|
) {
|
|
this.translation = translation;
|
|
this.tColumnToRow = tColumnToRow;
|
|
this.tCollapsed = tCollapsed;
|
|
this.cardCount = cardCount;
|
|
this.selectedIndex = selectedIndex;
|
|
}
|
|
|
|
public readonly Alignment translation;
|
|
public readonly float tColumnToRow;
|
|
public readonly float tCollapsed;
|
|
public readonly int cardCount;
|
|
public readonly float selectedIndex;
|
|
|
|
Rect _interpolateRect(Rect begin, Rect end) {
|
|
return Rect.lerp(begin, end, this.tColumnToRow);
|
|
}
|
|
|
|
Offset _interpolatePoint(Offset begin, Offset end) {
|
|
return Offset.lerp(begin, end, this.tColumnToRow);
|
|
}
|
|
|
|
public override void performLayout(Size size) {
|
|
float columnCardX = size.width / 5.0f;
|
|
float columnCardWidth = size.width - columnCardX;
|
|
float columnCardHeight = size.height / this.cardCount;
|
|
float rowCardWidth = size.width;
|
|
Offset offset = this.translation.alongSize(size);
|
|
float columnCardY = 0.0f;
|
|
float rowCardX = -(this.selectedIndex * rowCardWidth);
|
|
|
|
float columnTitleX = size.width / 10.0f;
|
|
float rowTitleWidth = size.width * ((1 + this.tCollapsed) / 2.25f);
|
|
float rowTitleX = (size.width - rowTitleWidth) / 2.0f - this.selectedIndex * rowTitleWidth;
|
|
|
|
const float paddedSectionIndicatorWidth = AnimationWidgetsUtils.kSectionIndicatorWidth + 8.0f;
|
|
float rowIndicatorWidth = paddedSectionIndicatorWidth +
|
|
(1.0f - this.tCollapsed) * (rowTitleWidth - paddedSectionIndicatorWidth);
|
|
float rowIndicatorX = (size.width - rowIndicatorWidth) / 2.0f - this.selectedIndex * rowIndicatorWidth;
|
|
|
|
for (int index = 0; index < this.cardCount; index++) {
|
|
Rect columnCardRect = Rect.fromLTWH(columnCardX, columnCardY, columnCardWidth, columnCardHeight);
|
|
Rect rowCardRect = Rect.fromLTWH(rowCardX, 0.0f, rowCardWidth, size.height);
|
|
Rect cardRect = this._interpolateRect(columnCardRect, rowCardRect).shift(offset);
|
|
string cardId = $"card{index}";
|
|
if (this.hasChild(cardId)) {
|
|
this.layoutChild(cardId, BoxConstraints.tight(cardRect.size));
|
|
this.positionChild(cardId, cardRect.topLeft);
|
|
}
|
|
|
|
Size titleSize = this.layoutChild($"title{index}", BoxConstraints.loose(cardRect.size));
|
|
float columnTitleY = columnCardRect.centerLeft.dy - titleSize.height / 2.0f;
|
|
float rowTitleY = rowCardRect.centerLeft.dy - titleSize.height / 2.0f;
|
|
float centeredRowTitleX = rowTitleX + (rowTitleWidth - titleSize.width) / 2.0f;
|
|
Offset columnTitleOrigin = new Offset(columnTitleX, columnTitleY);
|
|
Offset rowTitleOrigin = new Offset(centeredRowTitleX, rowTitleY);
|
|
Offset titleOrigin = this._interpolatePoint(columnTitleOrigin, rowTitleOrigin);
|
|
this.positionChild($"title{index}", titleOrigin + offset);
|
|
|
|
Size indicatorSize = this.layoutChild($"indicator{index}", BoxConstraints.loose(cardRect.size));
|
|
float columnIndicatorX = cardRect.centerRight.dx - indicatorSize.width - 16.0f;
|
|
float columnIndicatorY = cardRect.bottomRight.dy - indicatorSize.height - 16.0f;
|
|
Offset columnIndicatorOrigin = new Offset(columnIndicatorX, columnIndicatorY);
|
|
Rect titleRect = Rect.fromPoints(titleOrigin, titleSize.bottomRight(titleOrigin));
|
|
float centeredRowIndicatorX = rowIndicatorX + (rowIndicatorWidth - indicatorSize.width) / 2.0f;
|
|
float rowIndicatorY = titleRect.bottomCenter.dy + 16.0f;
|
|
Offset rowIndicatorOrigin = new Offset(centeredRowIndicatorX, rowIndicatorY);
|
|
Offset indicatorOrigin = this._interpolatePoint(columnIndicatorOrigin, rowIndicatorOrigin);
|
|
this.positionChild($"indicator{index}", indicatorOrigin + offset);
|
|
|
|
columnCardY += columnCardHeight;
|
|
rowCardX += rowCardWidth;
|
|
rowTitleX += rowTitleWidth;
|
|
rowIndicatorX += rowIndicatorWidth;
|
|
}
|
|
}
|
|
|
|
public override bool shouldRelayout(MultiChildLayoutDelegate _oldDelegate) {
|
|
_AllSectionsLayout oldDelegate = _oldDelegate as _AllSectionsLayout;
|
|
return this.tColumnToRow != oldDelegate.tColumnToRow
|
|
|| this.cardCount != oldDelegate.cardCount
|
|
|| this.selectedIndex != oldDelegate.selectedIndex;
|
|
}
|
|
}
|
|
|
|
class _AllSectionsView : AnimatedWidget {
|
|
public _AllSectionsView(
|
|
Key key = null,
|
|
int? sectionIndex = null,
|
|
List<Section> sections = null,
|
|
ValueNotifier<float> selectedIndex = null,
|
|
float? minHeight = null,
|
|
float? midHeight = null,
|
|
float? maxHeight = null,
|
|
List<Widget> sectionCards = null
|
|
) : base(key: key, listenable: selectedIndex) {
|
|
sectionCards = sectionCards ?? new List<Widget>();
|
|
D.assert(sections != null);
|
|
D.assert(sectionCards.Count == sections.Count);
|
|
D.assert(sectionIndex >= 0 && sectionIndex < sections.Count);
|
|
D.assert(selectedIndex != null);
|
|
D.assert(selectedIndex.value >= 0.0f && (float) selectedIndex.value < sections.Count);
|
|
this.sectionIndex = sectionIndex;
|
|
this.sections = sections;
|
|
this.selectedIndex = selectedIndex;
|
|
this.minHeight = minHeight;
|
|
this.midHeight = midHeight;
|
|
this.maxHeight = maxHeight;
|
|
this.sectionCards = sectionCards;
|
|
}
|
|
|
|
public readonly int? sectionIndex;
|
|
public readonly List<Section> sections;
|
|
public readonly ValueNotifier<float> selectedIndex;
|
|
public readonly float? minHeight;
|
|
public readonly float? midHeight;
|
|
public readonly float? maxHeight;
|
|
public readonly List<Widget> sectionCards;
|
|
|
|
float _selectedIndexDelta(int index) {
|
|
return (index - this.selectedIndex.value).abs().clamp(0.0f, 1.0f);
|
|
}
|
|
|
|
Widget _build(BuildContext context, BoxConstraints constraints) {
|
|
Size size = constraints.biggest;
|
|
|
|
float? tColumnToRow =
|
|
1.0f - ((size.height - this.midHeight) /
|
|
(this.maxHeight - this.midHeight))?.clamp(0.0f, 1.0f);
|
|
|
|
|
|
float? tCollapsed =
|
|
1.0f - ((size.height - this.minHeight) /
|
|
(this.midHeight - this.minHeight))?.clamp(0.0f, 1.0f);
|
|
|
|
float _indicatorOpacity(int index) {
|
|
return 1.0f - this._selectedIndexDelta(index) * 0.5f;
|
|
}
|
|
|
|
float? _titleOpacity(int index) {
|
|
return 1.0f - this._selectedIndexDelta(index) * tColumnToRow * 0.5f;
|
|
}
|
|
|
|
float? _titleScale(int index) {
|
|
return 1.0f - this._selectedIndexDelta(index) * tColumnToRow * 0.15f;
|
|
}
|
|
|
|
List<Widget> children = new List<Widget>(this.sectionCards);
|
|
|
|
for (int index = 0; index < this.sections.Count; index++) {
|
|
Section section = this.sections[index];
|
|
children.Add(new LayoutId(
|
|
id: $"title{index}",
|
|
child: new SectionTitle(
|
|
section: section,
|
|
scale: _titleScale(index),
|
|
opacity: _titleOpacity(index)
|
|
)
|
|
));
|
|
}
|
|
|
|
for (int index = 0; index < this.sections.Count; index++) {
|
|
children.Add(new LayoutId(
|
|
id: $"indicator{index}",
|
|
child: new SectionIndicator(
|
|
opacity: _indicatorOpacity(index)
|
|
)
|
|
));
|
|
}
|
|
|
|
return new CustomMultiChildLayout(
|
|
layoutDelegate: new _AllSectionsLayout(
|
|
translation: new Alignment((this.selectedIndex.value - this.sectionIndex) * 2.0f - 1.0f ?? 0.0f,
|
|
-1.0f),
|
|
tColumnToRow: tColumnToRow ?? 0.0f,
|
|
tCollapsed: tCollapsed ?? 0.0f,
|
|
cardCount: this.sections.Count,
|
|
selectedIndex: this.selectedIndex.value
|
|
),
|
|
children: children
|
|
);
|
|
}
|
|
|
|
protected override Widget build(BuildContext context) {
|
|
return new LayoutBuilder(builder: this._build);
|
|
}
|
|
}
|
|
|
|
class _SnappingScrollPhysics : ClampingScrollPhysics {
|
|
public _SnappingScrollPhysics(
|
|
ScrollPhysics parent = null,
|
|
float? midScrollOffset = null
|
|
) : base(parent: parent) {
|
|
D.assert(midScrollOffset != null);
|
|
this.midScrollOffset = midScrollOffset ?? 0.0f;
|
|
}
|
|
|
|
public readonly float midScrollOffset;
|
|
|
|
public override ScrollPhysics applyTo(ScrollPhysics ancestor) {
|
|
return new _SnappingScrollPhysics(parent: this.buildParent(ancestor),
|
|
midScrollOffset: this.midScrollOffset);
|
|
}
|
|
|
|
Simulation _toMidScrollOffsetSimulation(float offset, float dragVelocity) {
|
|
float velocity = Mathf.Max(dragVelocity, this.minFlingVelocity);
|
|
return new ScrollSpringSimulation(this.spring, offset, this.midScrollOffset, velocity,
|
|
tolerance: this.tolerance);
|
|
}
|
|
|
|
Simulation _toZeroScrollOffsetSimulation(float offset, float dragVelocity) {
|
|
float velocity = Mathf.Max(dragVelocity, this.minFlingVelocity);
|
|
return new ScrollSpringSimulation(this.spring, offset, 0.0f, velocity, tolerance: this.tolerance);
|
|
}
|
|
|
|
public override Simulation createBallisticSimulation(ScrollMetrics position, float dragVelocity) {
|
|
Simulation simulation = base.createBallisticSimulation(position, dragVelocity);
|
|
float offset = position.pixels;
|
|
|
|
if (simulation != null) {
|
|
float simulationEnd = simulation.x(float.PositiveInfinity);
|
|
if (simulationEnd >= this.midScrollOffset) {
|
|
return simulation;
|
|
}
|
|
|
|
if (dragVelocity > 0.0f) {
|
|
return this._toMidScrollOffsetSimulation(offset, dragVelocity);
|
|
}
|
|
|
|
if (dragVelocity < 0.0f) {
|
|
return this._toZeroScrollOffsetSimulation(offset, dragVelocity);
|
|
}
|
|
}
|
|
else {
|
|
float snapThreshold = this.midScrollOffset / 2.0f;
|
|
if (offset >= snapThreshold && offset < this.midScrollOffset) {
|
|
return this._toMidScrollOffsetSimulation(offset, dragVelocity);
|
|
}
|
|
|
|
if (offset > 0.0f && offset < snapThreshold) {
|
|
return this._toZeroScrollOffsetSimulation(offset, dragVelocity);
|
|
}
|
|
}
|
|
|
|
return simulation;
|
|
}
|
|
}
|
|
|
|
public class AnimationDemoHome : StatefulWidget {
|
|
public AnimationDemoHome(Key key = null) : base(key: key) {
|
|
}
|
|
|
|
public const string routeName = "/animation";
|
|
|
|
public override State createState() {
|
|
return new _AnimationDemoHomeState();
|
|
}
|
|
}
|
|
|
|
class _AnimationDemoHomeState : State<AnimationDemoHome> {
|
|
ScrollController _scrollController = new ScrollController();
|
|
PageController _headingPageController = new PageController();
|
|
PageController _detailsPageController = new PageController();
|
|
ScrollPhysics _headingScrollPhysics = new NeverScrollableScrollPhysics();
|
|
ValueNotifier<float> selectedIndex = new ValueNotifier<float>(0.0f);
|
|
|
|
public override Widget build(BuildContext context) {
|
|
return new Scaffold(
|
|
backgroundColor: AnimationHomeUtils._kAppBackgroundColor,
|
|
body: new Builder(
|
|
builder: this._buildBody
|
|
)
|
|
);
|
|
}
|
|
|
|
void _handleBackButton(float midScrollOffset) {
|
|
if (this._scrollController.offset >= midScrollOffset) {
|
|
this._scrollController.animateTo(0.0f, curve: AnimationHomeUtils._kScrollCurve,
|
|
duration: AnimationHomeUtils._kScrollDuration);
|
|
}
|
|
else {
|
|
Navigator.maybePop(this.context);
|
|
}
|
|
}
|
|
|
|
bool _handleScrollNotification(ScrollNotification notification, float midScrollOffset) {
|
|
if (notification.depth == 0 && notification is ScrollUpdateNotification) {
|
|
ScrollPhysics physics = this._scrollController.position.pixels >= midScrollOffset
|
|
? (ScrollPhysics) new PageScrollPhysics()
|
|
: new NeverScrollableScrollPhysics();
|
|
if (physics != this._headingScrollPhysics) {
|
|
this.setState(() => { this._headingScrollPhysics = physics; });
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void _maybeScroll(float midScrollOffset, int pageIndex, float xOffset) {
|
|
if (this._scrollController.offset < midScrollOffset) {
|
|
this._headingPageController.animateToPage(pageIndex, curve: AnimationHomeUtils._kScrollCurve,
|
|
duration: AnimationHomeUtils._kScrollDuration);
|
|
this._scrollController.animateTo(midScrollOffset, curve: AnimationHomeUtils._kScrollCurve,
|
|
duration: AnimationHomeUtils._kScrollDuration);
|
|
}
|
|
else {
|
|
float centerX = this._headingPageController.position.viewportDimension / 2.0f;
|
|
int newPageIndex = xOffset > centerX ? pageIndex + 1 : pageIndex - 1;
|
|
this._headingPageController.animateToPage(newPageIndex, curve: AnimationHomeUtils._kScrollCurve,
|
|
duration: AnimationHomeUtils._kScrollDuration);
|
|
}
|
|
}
|
|
|
|
bool _handlePageNotification(ScrollNotification notification, PageController leader, PageController follower) {
|
|
if (notification.depth == 0 && notification is ScrollUpdateNotification) {
|
|
this.selectedIndex.value = leader.page;
|
|
if (follower.page != leader.page) {
|
|
follower.position.jumpTo(leader.position.pixels); // ignore: deprecated_member_use
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
IEnumerable<Widget> _detailItemsFor(Section section) {
|
|
IEnumerable<Widget> detailItems = section.details.Select<SectionDetail, Widget>((SectionDetail detail) => {
|
|
return new SectionDetailView(detail: detail);
|
|
});
|
|
return ListTile.divideTiles(context: this.context, tiles: detailItems);
|
|
}
|
|
|
|
List<Widget> _allHeadingItems(float maxHeight, float midScrollOffset) {
|
|
List<Widget> sectionCards = new List<Widget> { };
|
|
for (int index = 0; index < AnimationSectionsUtils.allSections.Count; index++) {
|
|
sectionCards.Add(new LayoutId(
|
|
id: $"card{index}",
|
|
child: new GestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
child: new SectionCard(section: AnimationSectionsUtils.allSections[index]),
|
|
onTapUp: (TapUpDetails details) => {
|
|
float xOffset = details.globalPosition.dx;
|
|
this.setState(() => { this._maybeScroll(midScrollOffset, index, xOffset); });
|
|
}
|
|
)
|
|
));
|
|
}
|
|
|
|
List<Widget> headings = new List<Widget> { };
|
|
for (int index = 0; index < AnimationSectionsUtils.allSections.Count; index++) {
|
|
headings.Add(new Container(
|
|
color: AnimationHomeUtils._kAppBackgroundColor,
|
|
child: new ClipRect(
|
|
child: new _AllSectionsView(
|
|
sectionIndex: index,
|
|
sections: AnimationSectionsUtils.allSections,
|
|
selectedIndex: this.selectedIndex,
|
|
minHeight: AnimationHomeUtils._kAppBarMinHeight,
|
|
midHeight: AnimationHomeUtils._kAppBarMidHeight,
|
|
maxHeight: maxHeight,
|
|
sectionCards: sectionCards
|
|
)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
return headings;
|
|
}
|
|
|
|
Widget _buildBody(BuildContext context) {
|
|
MediaQueryData mediaQueryData = MediaQuery.of(context);
|
|
float statusBarHeight = mediaQueryData.padding.top;
|
|
float screenHeight = mediaQueryData.size.height;
|
|
float appBarMaxHeight = screenHeight - statusBarHeight;
|
|
|
|
float appBarMidScrollOffset = statusBarHeight + appBarMaxHeight - AnimationHomeUtils._kAppBarMidHeight;
|
|
|
|
return SizedBox.expand(
|
|
child: new Stack(
|
|
children: new List<Widget> {
|
|
new NotificationListener<ScrollNotification>(
|
|
onNotification: (ScrollNotification notification) => {
|
|
return this._handleScrollNotification(notification, appBarMidScrollOffset);
|
|
},
|
|
child: new CustomScrollView(
|
|
controller: this._scrollController,
|
|
physics: new _SnappingScrollPhysics(midScrollOffset: appBarMidScrollOffset),
|
|
slivers: new List<Widget> {
|
|
new _StatusBarPaddingSliver(
|
|
maxHeight: statusBarHeight,
|
|
scrollFactor: 7.0f
|
|
),
|
|
new SliverPersistentHeader(
|
|
pinned: true,
|
|
del: new _SliverAppBarDelegate(
|
|
minHeight: AnimationHomeUtils._kAppBarMinHeight,
|
|
maxHeight: appBarMaxHeight,
|
|
child: new NotificationListener<ScrollNotification>(
|
|
onNotification: (ScrollNotification notification) => {
|
|
return this._handlePageNotification(notification,
|
|
this._headingPageController, this._detailsPageController);
|
|
},
|
|
child: new PageView(
|
|
physics: this._headingScrollPhysics,
|
|
controller: this._headingPageController,
|
|
children: this._allHeadingItems(appBarMaxHeight,
|
|
appBarMidScrollOffset)
|
|
)
|
|
)
|
|
)
|
|
),
|
|
new SliverToBoxAdapter(
|
|
child: new SizedBox(
|
|
height: 610.0f,
|
|
child: new NotificationListener<ScrollNotification>(
|
|
onNotification: (ScrollNotification notification) => {
|
|
return this._handlePageNotification(notification,
|
|
this._detailsPageController, this._headingPageController);
|
|
},
|
|
child: new PageView(
|
|
controller: this._detailsPageController,
|
|
children: AnimationSectionsUtils.allSections
|
|
.Select<Section, Widget>((Section section) => {
|
|
return new Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: this._detailItemsFor(section).ToList()
|
|
);
|
|
}).ToList()
|
|
)
|
|
)
|
|
)
|
|
)
|
|
}
|
|
)
|
|
),
|
|
new Positioned(
|
|
top: statusBarHeight,
|
|
left: 0.0f,
|
|
child: new IconTheme(
|
|
data: new IconThemeData(color: Colors.white),
|
|
child: new SafeArea(
|
|
top: false,
|
|
bottom: false,
|
|
child: new IconButton(
|
|
icon: new BackButtonIcon(),
|
|
tooltip: "Back",
|
|
onPressed: () => { this._handleBackButton(appBarMidScrollOffset); }
|
|
)
|
|
)
|
|
)
|
|
)
|
|
}
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|