Commit 4cc094ac authored by Adam Barth's avatar Adam Barth Committed by GitHub

Remove pop in AppBar hero animations (#5243)

Now the flexible space bar computes its effect from its size rather than from
the Scaffold's animation.
parent d2b39761
...@@ -42,13 +42,14 @@ class _AppBarBackground extends StatelessWidget { ...@@ -42,13 +42,14 @@ class _AppBarBackground extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Animation<double> effectiveAnimation = animation ?? kAlwaysCompleteAnimation;
return new AnimatedBuilder( return new AnimatedBuilder(
animation: animation, animation: effectiveAnimation,
builder: (BuildContext context, Widget child) { builder: (BuildContext context, Widget child) {
return new Stack( return new Stack(
children: _kBackgroundLayers.map((_BackgroundLayer layer) { children: _kBackgroundLayers.map((_BackgroundLayer layer) {
return new Positioned( return new Positioned(
top: -layer.parallaxTween.evaluate(animation), top: -layer.parallaxTween.evaluate(effectiveAnimation),
left: 0.0, left: 0.0,
right: 0.0, right: 0.0,
bottom: 0.0, bottom: 0.0,
...@@ -147,7 +148,7 @@ class GalleryHomeState extends State<GalleryHome> { ...@@ -147,7 +148,7 @@ class GalleryHomeState extends State<GalleryHome> {
background: new Builder( background: new Builder(
builder: (BuildContext context) { builder: (BuildContext context) {
return new _AppBarBackground( return new _AppBarBackground(
animation: Scaffold.of(context).appBarAnimation animation: Scaffold.of(context)?.appBarAnimation
); );
} }
) )
......
...@@ -30,6 +30,22 @@ abstract class AppBarBottomWidget extends Widget { ...@@ -30,6 +30,22 @@ abstract class AppBarBottomWidget extends Widget {
// Mobile Portrait: 56dp // Mobile Portrait: 56dp
// Tablet/Desktop: 64dp // Tablet/Desktop: 64dp
class _AppBarExpandedHeight extends InheritedWidget {
_AppBarExpandedHeight({
this.expandedHeight,
Widget child
}) : super(child: child) {
assert(expandedHeight != null);
}
final double expandedHeight;
@override
bool updateShouldNotify(_AppBarExpandedHeight oldWidget) {
return expandedHeight != oldWidget.expandedHeight;
}
}
/// A material design app bar. /// A material design app bar.
/// ///
/// An app bar consists of a toolbar and potentially other widgets, such as a /// An app bar consists of a toolbar and potentially other widgets, such as a
...@@ -164,6 +180,11 @@ class AppBar extends StatelessWidget { ...@@ -164,6 +180,11 @@ class AppBar extends StatelessWidget {
final double _expandedHeight; final double _expandedHeight;
final double _collapsedHeight; final double _collapsedHeight;
static double getExpandedHeightFor(BuildContext context) {
_AppBarExpandedHeight marker = context.inheritFromWidgetOfExactType(_AppBarExpandedHeight);
return marker?.expandedHeight ?? 0.0;
}
/// Creates a copy of this app bar but with the given fields replaced with the new values. /// Creates a copy of this app bar but with the given fields replaced with the new values.
AppBar copyWith({ AppBar copyWith({
Key key, Key key,
...@@ -377,12 +398,15 @@ class AppBar extends StatelessWidget { ...@@ -377,12 +398,15 @@ class AppBar extends StatelessWidget {
return new Hero( return new Hero(
tag: heroTag ?? _kDefaultHeroTag, tag: heroTag ?? _kDefaultHeroTag,
child: new Material( child: new _AppBarExpandedHeight(
color: backgroundColor ?? themeData.primaryColor, expandedHeight: expandedHeight,
elevation: elevation, child: new Material(
child: new Align( color: backgroundColor ?? themeData.primaryColor,
alignment: FractionalOffset.topCenter, elevation: elevation,
child: appBar child: new Align(
alignment: FractionalOffset.topCenter,
child: appBar
)
) )
) )
); );
......
...@@ -6,6 +6,7 @@ import 'dart:math' as math; ...@@ -6,6 +6,7 @@ import 'dart:math' as math;
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'app_bar.dart';
import 'constants.dart'; import 'constants.dart';
import 'scaffold.dart'; import 'scaffold.dart';
import 'theme.dart'; import 'theme.dart';
...@@ -57,27 +58,6 @@ class FlexibleSpaceBar extends StatefulWidget { ...@@ -57,27 +58,6 @@ class FlexibleSpaceBar extends StatefulWidget {
} }
class _FlexibleSpaceBarState extends State<FlexibleSpaceBar> { class _FlexibleSpaceBarState extends State<FlexibleSpaceBar> {
final ProxyAnimation _scaffoldAnimation = new ProxyAnimation();
double _lastAppBarHeight;
@override
void initState() {
super.initState();
_scaffoldAnimation.addListener(_handleTick);
}
@override
void dispose() {
_scaffoldAnimation.removeListener(_handleTick);
super.dispose();
}
void _handleTick() {
setState(() {
// The animation's state is our build state, and it changed already.
});
}
bool _getEffectiveCenterTitle(ThemeData theme) { bool _getEffectiveCenterTitle(ThemeData theme) {
if (config.centerTitle != null) if (config.centerTitle != null)
return config.centerTitle; return config.centerTitle;
...@@ -91,77 +71,62 @@ class _FlexibleSpaceBarState extends State<FlexibleSpaceBar> { ...@@ -91,77 +71,62 @@ class _FlexibleSpaceBarState extends State<FlexibleSpaceBar> {
return null; return null;
} }
@override Widget _buildContent(BuildContext context, BoxConstraints constraints) {
Widget build(BuildContext context) { final Size size = constraints.biggest;
final double statusBarHeight = MediaQuery.of(context).padding.top; final double statusBarHeight = MediaQuery.of(context).padding.top;
final ScaffoldState scaffold = Scaffold.of(context);
if (scaffold != null) { final double currentHeight = size.height;
_scaffoldAnimation.parent ??= scaffold.appBarAnimation; final double maxHeight = statusBarHeight + AppBar.getExpandedHeightFor(context);
_lastAppBarHeight = scaffold.appBarHeight; final double minHeight = statusBarHeight + kToolBarHeight;
} final double deltaHeight = maxHeight - minHeight;
final double appBarHeight = (_lastAppBarHeight ?? kToolBarHeight) + statusBarHeight;
final double toolBarHeight = kToolBarHeight + statusBarHeight; // 0.0 -> Expanded
// 1.0 -> Collapsed to toolbar
final double t = (1.0 - (currentHeight - minHeight) / deltaHeight).clamp(0.0, 1.0);
final List<Widget> children = <Widget>[]; final List<Widget> children = <Widget>[];
// background image // background image
if (config.background != null && scaffold != null) { if (config.background != null) {
final double fadeStart = (appBarHeight - toolBarHeight * 2.0) / appBarHeight; final double fadeStart = math.max(0.0, 1.0 - kToolBarHeight / deltaHeight);
final double fadeEnd = (appBarHeight - toolBarHeight) / appBarHeight; final double fadeEnd = 1.0;
final CurvedAnimation opacityCurve = new CurvedAnimation( final double opacity = 1.0 - new Interval(fadeStart, fadeEnd).transform(t);
parent: _scaffoldAnimation, final double parallax = new Tween<double>(begin: 0.0, end: deltaHeight / 4.0).lerp(t);
curve: new Interval(math.max(0.0, fadeStart), math.min(fadeEnd, 1.0))
);
final double parallax = new Tween<double>(begin: 0.0, end: appBarHeight / 4.0).evaluate(_scaffoldAnimation);
final double opacity = new Tween<double>(begin: 1.0, end: 0.0).evaluate(opacityCurve);
if (opacity > 0.0) { if (opacity > 0.0) {
children.add(new Positioned( children.add(new Positioned(
top: -parallax, top: -parallax,
left: 0.0, left: 0.0,
right: 0.0, right: 0.0,
height: maxHeight,
child: new Opacity( child: new Opacity(
opacity: opacity, opacity: opacity,
child: new SizedBox( child: config.background
height: appBarHeight + statusBarHeight,
child: config.background
)
) )
)); ));
} }
} }
// title
if (config.title != null) { if (config.title != null) {
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
final double fadeStart = (appBarHeight - toolBarHeight) / appBarHeight; final double opacity = (1.0 - (minHeight - currentHeight) / (kToolBarHeight - statusBarHeight)).clamp(0.0, 1.0);
final double fadeEnd = (appBarHeight - toolBarHeight / 2.0) / appBarHeight; if (opacity > 0.0) {
final CurvedAnimation opacityCurve = new CurvedAnimation(
parent: _scaffoldAnimation,
curve: new Interval(fadeStart, fadeEnd)
);
final int alpha = new Tween<double>(begin: 255.0, end: 0.0).evaluate(opacityCurve).toInt();
if (alpha > 0) {
TextStyle titleStyle = theme.primaryTextTheme.title; TextStyle titleStyle = theme.primaryTextTheme.title;
titleStyle = titleStyle.copyWith( titleStyle = titleStyle.copyWith(
color: titleStyle.color.withAlpha(alpha) color: titleStyle.color.withOpacity(opacity)
);
final double yAlignStart = 1.0;
final double yAlignEnd = (statusBarHeight + kToolBarHeight / 2.0) / toolBarHeight;
final double scaleAndAlignEnd = (appBarHeight - toolBarHeight) / appBarHeight;
final CurvedAnimation scaleAndAlignCurve = new CurvedAnimation(
parent: _scaffoldAnimation,
curve: new Interval(0.0, scaleAndAlignEnd)
); );
final bool effectiveCenterTitle = _getEffectiveCenterTitle(theme); final bool effectiveCenterTitle = _getEffectiveCenterTitle(theme);
final double scaleValue = new Tween<double>(begin: 1.5, end: 1.0).lerp(t);
final Matrix4 scaleTransform = new Matrix4.identity()
..scale(scaleValue, scaleValue, 1.0);
final FractionalOffset titleAlignment = effectiveCenterTitle ? FractionalOffset.bottomCenter : FractionalOffset.bottomLeft; final FractionalOffset titleAlignment = effectiveCenterTitle ? FractionalOffset.bottomCenter : FractionalOffset.bottomLeft;
children.add(new Container( children.add(new Container(
padding: new EdgeInsets.only(left: effectiveCenterTitle ? 0.0 : 72.0, bottom: 14.0), padding: new EdgeInsets.only(
align: new Tween<FractionalOffset>( left: effectiveCenterTitle ? 0.0 : 72.0,
begin: new FractionalOffset(0.0, yAlignStart), bottom: 16.0
end: new FractionalOffset(0.0, yAlignEnd) ),
).evaluate(scaleAndAlignCurve), child: new Transform(
child: new ScaleTransition(
alignment: titleAlignment, alignment: titleAlignment,
scale: new Tween<double>(begin: 1.5, end: 1.0).animate(scaleAndAlignCurve), transform: scaleTransform,
child: new Align( child: new Align(
alignment: titleAlignment, alignment: titleAlignment,
child: new DefaultTextStyle(style: titleStyle, child: config.title) child: new DefaultTextStyle(style: titleStyle, child: config.title)
...@@ -173,4 +138,9 @@ class _FlexibleSpaceBarState extends State<FlexibleSpaceBar> { ...@@ -173,4 +138,9 @@ class _FlexibleSpaceBarState extends State<FlexibleSpaceBar> {
return new ClipRect(child: new Stack(children: children)); return new ClipRect(child: new Stack(children: children));
} }
@override
Widget build(BuildContext context) {
return new LayoutBuilder(builder: _buildContent);
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment