mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
parent
5a5683ddd7
commit
2bb19a9501
@ -82,6 +82,7 @@ class RenderAnimatedSize extends RenderAligningShiftedBox {
|
||||
super.textDirection,
|
||||
super.child,
|
||||
Clip clipBehavior = Clip.hardEdge,
|
||||
VoidCallback? onEnd,
|
||||
}) : _vsync = vsync,
|
||||
_clipBehavior = clipBehavior {
|
||||
_controller = AnimationController(
|
||||
@ -97,6 +98,7 @@ class RenderAnimatedSize extends RenderAligningShiftedBox {
|
||||
parent: _controller,
|
||||
curve: curve,
|
||||
);
|
||||
_onEnd = onEnd;
|
||||
}
|
||||
|
||||
/// When asserts are enabled, returns the animation controller that is used
|
||||
@ -203,6 +205,19 @@ class RenderAnimatedSize extends RenderAligningShiftedBox {
|
||||
_controller.resync(vsync);
|
||||
}
|
||||
|
||||
/// Called every time an animation completes.
|
||||
///
|
||||
/// This can be useful to trigger additional actions (e.g. another animation)
|
||||
/// at the end of the current animation.
|
||||
VoidCallback? get onEnd => _onEnd;
|
||||
VoidCallback? _onEnd;
|
||||
set onEnd(VoidCallback? value) {
|
||||
if (value == _onEnd) {
|
||||
return;
|
||||
}
|
||||
_onEnd = value;
|
||||
}
|
||||
|
||||
@override
|
||||
void attach(PipelineOwner owner) {
|
||||
super.attach(owner);
|
||||
@ -216,11 +231,13 @@ class RenderAnimatedSize extends RenderAligningShiftedBox {
|
||||
// already, to resume interrupted resizing animation.
|
||||
markNeedsLayout();
|
||||
}
|
||||
_controller.addStatusListener(_animationStatusListener);
|
||||
}
|
||||
|
||||
@override
|
||||
void detach() {
|
||||
_controller.stop();
|
||||
_controller.removeStatusListener(_animationStatusListener);
|
||||
super.detach();
|
||||
}
|
||||
|
||||
@ -363,6 +380,16 @@ class RenderAnimatedSize extends RenderAligningShiftedBox {
|
||||
}
|
||||
}
|
||||
|
||||
void _animationStatusListener(AnimationStatus status) {
|
||||
switch (status) {
|
||||
case AnimationStatus.completed:
|
||||
_onEnd?.call();
|
||||
case AnimationStatus.dismissed:
|
||||
case AnimationStatus.forward:
|
||||
case AnimationStatus.reverse:
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
if (child != null && _hasVisualOverflow && clipBehavior != Clip.none) {
|
||||
|
||||
@ -31,6 +31,7 @@ class AnimatedSize extends StatefulWidget {
|
||||
required this.duration,
|
||||
this.reverseDuration,
|
||||
this.clipBehavior = Clip.hardEdge,
|
||||
this.onEnd,
|
||||
});
|
||||
|
||||
/// The widget below this widget in the tree.
|
||||
@ -78,6 +79,12 @@ class AnimatedSize extends StatefulWidget {
|
||||
/// Defaults to [Clip.hardEdge].
|
||||
final Clip clipBehavior;
|
||||
|
||||
/// Called every time an animation completes.
|
||||
///
|
||||
/// This can be useful to trigger additional actions (e.g. another animation)
|
||||
/// at the end of the current animation.
|
||||
final VoidCallback? onEnd;
|
||||
|
||||
@override
|
||||
State<AnimatedSize> createState() => _AnimatedSizeState();
|
||||
}
|
||||
@ -93,6 +100,7 @@ class _AnimatedSizeState
|
||||
reverseDuration: widget.reverseDuration,
|
||||
vsync: this,
|
||||
clipBehavior: widget.clipBehavior,
|
||||
onEnd: widget.onEnd,
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
@ -107,6 +115,7 @@ class _AnimatedSize extends SingleChildRenderObjectWidget {
|
||||
this.reverseDuration,
|
||||
required this.vsync,
|
||||
this.clipBehavior = Clip.hardEdge,
|
||||
this.onEnd,
|
||||
});
|
||||
|
||||
final AlignmentGeometry alignment;
|
||||
@ -119,6 +128,8 @@ class _AnimatedSize extends SingleChildRenderObjectWidget {
|
||||
|
||||
final Clip clipBehavior;
|
||||
|
||||
final VoidCallback? onEnd;
|
||||
|
||||
@override
|
||||
RenderAnimatedSize createRenderObject(BuildContext context) {
|
||||
return RenderAnimatedSize(
|
||||
@ -129,6 +140,7 @@ class _AnimatedSize extends SingleChildRenderObjectWidget {
|
||||
vsync: vsync,
|
||||
textDirection: Directionality.maybeOf(context),
|
||||
clipBehavior: clipBehavior,
|
||||
onEnd: onEnd,
|
||||
);
|
||||
}
|
||||
|
||||
@ -141,7 +153,8 @@ class _AnimatedSize extends SingleChildRenderObjectWidget {
|
||||
..curve = curve
|
||||
..vsync = vsync
|
||||
..textDirection = Directionality.maybeOf(context)
|
||||
..clipBehavior = clipBehavior;
|
||||
..clipBehavior = clipBehavior
|
||||
..onEnd = onEnd;
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@ -87,6 +87,61 @@ void main() {
|
||||
expect(box.size.height, equals(100.0));
|
||||
});
|
||||
|
||||
testWidgets('calls onEnd when animation is completed', (WidgetTester tester) async {
|
||||
int callCount = 0;
|
||||
void handleEnd() {
|
||||
callCount++;
|
||||
}
|
||||
|
||||
await tester.pumpWidget(
|
||||
Center(
|
||||
child: AnimatedSize(
|
||||
onEnd: handleEnd,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: const SizedBox(
|
||||
width: 100.0,
|
||||
height: 100.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(callCount, equals(0));
|
||||
|
||||
await tester.pumpWidget(
|
||||
Center(
|
||||
child: AnimatedSize(
|
||||
onEnd: handleEnd,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: const SizedBox(
|
||||
width: 200.0,
|
||||
height: 200.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(callCount, equals(0));
|
||||
await tester.pumpAndSettle();
|
||||
expect(callCount, equals(1));
|
||||
|
||||
await tester.pumpWidget(
|
||||
Center(
|
||||
child: AnimatedSize(
|
||||
onEnd: handleEnd,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: const SizedBox(
|
||||
width: 100.0,
|
||||
height: 100.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
expect(callCount, equals(2));
|
||||
});
|
||||
|
||||
testWidgets('clamps animated size to constraints', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const Center(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user