Make AnimatedCrossFade's alignment configurable (#9973)

The default is topCenter, which is sensible, but someone might want to use a
bottomCenter alignment, for example.
This commit is contained in:
Adam Barth 2017-05-11 17:47:19 -07:00 committed by GitHub
parent 7160ecfb07
commit b9f4e5dac7
3 changed files with 78 additions and 13 deletions

View file

@ -297,7 +297,7 @@ class RenderStack extends RenderBox
/// top left corners.
RenderStack({
List<RenderBox> children,
FractionalOffset alignment: FractionalOffset.center,
FractionalOffset alignment: FractionalOffset.topLeft,
StackFit fit: StackFit.loose,
Overflow overflow: Overflow.clip
}) : _alignment = alignment,

View file

@ -51,6 +51,7 @@ class AnimatedCrossFade extends StatefulWidget {
this.firstCurve: Curves.linear,
this.secondCurve: Curves.linear,
this.sizeCurve: Curves.linear,
this.alignment: FractionalOffset.topCenter,
@required this.crossFadeState,
@required this.duration
}) : assert(firstCurve != null),
@ -77,14 +78,25 @@ class AnimatedCrossFade extends StatefulWidget {
final Duration duration;
/// The fade curve of the first child.
///
/// Defaults to [Curves.linear].
final Curve firstCurve;
/// The fade curve of the second child.
///
/// Defaults to [Curves.linear].
final Curve secondCurve;
/// The curve of the animation between the two children's sizes.
///
/// Defaults to [Curves.linear].
final Curve sizeCurve;
/// How the children should be aligned while the size is animating.
///
/// Defaults to [FractionalOffset.topCenter].
final FractionalOffset alignment;
@override
_AnimatedCrossFadeState createState() => new _AnimatedCrossFadeState();
}
@ -152,7 +164,7 @@ class _AnimatedCrossFadeState extends State<AnimatedCrossFade> with TickerProvid
children = <Widget>[
new FadeTransition(
opacity: _secondAnimation,
child: widget.secondChild
child: widget.secondChild,
),
new Positioned(
// TODO(dragostis): Add a way to crop from top right for
@ -162,15 +174,15 @@ class _AnimatedCrossFadeState extends State<AnimatedCrossFade> with TickerProvid
right: 0.0,
child: new FadeTransition(
opacity: _firstAnimation,
child: widget.firstChild
)
)
child: widget.firstChild,
),
),
];
} else {
children = <Widget>[
new FadeTransition(
opacity: _firstAnimation,
child: widget.firstChild
child: widget.firstChild,
),
new Positioned(
// TODO(dragostis): Add a way to crop from top right for
@ -180,24 +192,24 @@ class _AnimatedCrossFadeState extends State<AnimatedCrossFade> with TickerProvid
right: 0.0,
child: new FadeTransition(
opacity: _secondAnimation,
child: widget.secondChild
)
)
child: widget.secondChild,
),
),
];
}
return new ClipRect(
child: new AnimatedSize(
key: new ValueKey<Key>(widget.key),
alignment: FractionalOffset.topCenter,
alignment: widget.alignment,
duration: widget.duration,
curve: widget.sizeCurve,
vsync: this,
child: new Stack(
overflow: Overflow.visible,
children: children
)
)
children: children,
),
),
);
}
}

View file

@ -78,4 +78,57 @@ void main() {
expect(box.size.width, equals(200.0));
expect(box.size.height, equals(200.0));
});
testWidgets('AnimatedCrossFade alignment', (WidgetTester tester) async {
final Key firstKey = new UniqueKey();
final Key secondKey = new UniqueKey();
await tester.pumpWidget(
new Center(
child: new AnimatedCrossFade(
alignment: FractionalOffset.bottomRight,
firstChild: new SizedBox(
key: firstKey,
width: 100.0,
height: 100.0
),
secondChild: new SizedBox(
key: secondKey,
width: 200.0,
height: 200.0
),
duration: const Duration(milliseconds: 200),
crossFadeState: CrossFadeState.showFirst
)
)
);
await tester.pumpWidget(
new Center(
child: new AnimatedCrossFade(
alignment: FractionalOffset.bottomRight,
firstChild: new SizedBox(
key: firstKey,
width: 100.0,
height: 100.0
),
secondChild: new SizedBox(
key: secondKey,
width: 200.0,
height: 200.0
),
duration: const Duration(milliseconds: 200),
crossFadeState: CrossFadeState.showSecond
)
)
);
await tester.pump(const Duration(milliseconds: 100));
final RenderBox box1 = tester.renderObject(find.byKey(firstKey));
final RenderBox box2 = tester.renderObject(find.byKey(secondKey));
expect(box1.localToGlobal(Offset.zero), const Offset(275.0, 175.0));
expect(box2.localToGlobal(Offset.zero), const Offset(275.0, 175.0));
});
}