blob: 356cf0debf813ad6ed2792d75205397434c85102 [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
class TestPaintingContext implements PaintingContext {
final List<Invocation> invocations = <Invocation>[];
@override
void noSuchMethod(Invocation invocation) {
invocations.add(invocation);
}
}
void main() {
group('AnimatedSize', () {
testWidgets('animates forwards then backwards with stable-sized children', (WidgetTester tester) async {
await tester.pumpWidget(
Center(
child: AnimatedSize(
duration: const Duration(milliseconds: 200),
vsync: tester,
child: const SizedBox(
width: 100.0,
height: 100.0,
),
),
),
);
RenderBox box = tester.renderObject(find.byType(AnimatedSize));
expect(box.size.width, equals(100.0));
expect(box.size.height, equals(100.0));
await tester.pumpWidget(
Center(
child: AnimatedSize(
duration: const Duration(milliseconds: 200),
vsync: tester,
child: const SizedBox(
width: 200.0,
height: 200.0,
),
),
),
);
await tester.pump(const Duration(milliseconds: 100));
box = tester.renderObject(find.byType(AnimatedSize));
expect(box.size.width, equals(150.0));
expect(box.size.height, equals(150.0));
TestPaintingContext context = TestPaintingContext();
box.paint(context, Offset.zero);
expect(context.invocations.first.memberName, equals(#pushClipRect));
await tester.pump(const Duration(milliseconds: 100));
box = tester.renderObject(find.byType(AnimatedSize));
expect(box.size.width, equals(200.0));
expect(box.size.height, equals(200.0));
await tester.pumpWidget(
Center(
child: AnimatedSize(
duration: const Duration(milliseconds: 200),
vsync: tester,
child: const SizedBox(
width: 100.0,
height: 100.0,
),
),
),
);
await tester.pump(const Duration(milliseconds: 100));
box = tester.renderObject(find.byType(AnimatedSize));
expect(box.size.width, equals(150.0));
expect(box.size.height, equals(150.0));
context = TestPaintingContext();
box.paint(context, Offset.zero);
expect(context.invocations.first.memberName, equals(#paintChild));
await tester.pump(const Duration(milliseconds: 100));
box = tester.renderObject(find.byType(AnimatedSize));
expect(box.size.width, equals(100.0));
expect(box.size.height, equals(100.0));
});
testWidgets('clamps animated size to constraints', (WidgetTester tester) async {
await tester.pumpWidget(
Center(
child: SizedBox (
width: 100.0,
height: 100.0,
child: AnimatedSize(
duration: const Duration(milliseconds: 200),
vsync: tester,
child: const SizedBox(
width: 100.0,
height: 100.0,
),
),
),
),
);
RenderBox box = tester.renderObject(find.byType(AnimatedSize));
expect(box.size.width, equals(100.0));
expect(box.size.height, equals(100.0));
// Attempt to animate beyond the outer SizedBox.
await tester.pumpWidget(
Center(
child: SizedBox (
width: 100.0,
height: 100.0,
child: AnimatedSize(
duration: const Duration(milliseconds: 200),
vsync: tester,
child: const SizedBox(
width: 200.0,
height: 200.0,
),
),
),
),
);
// Verify that animated size is the same as the outer SizedBox.
await tester.pump(const Duration(milliseconds: 100));
box = tester.renderObject(find.byType(AnimatedSize));
expect(box.size.width, equals(100.0));
expect(box.size.height, equals(100.0));
});
testWidgets('tracks unstable child, then resumes animation when child stabilizes', (WidgetTester tester) async {
Future<void> pumpMillis(int millis) async {
await tester.pump(Duration(milliseconds: millis));
}
void verify({ double? size, RenderAnimatedSizeState? state }) {
assert(size != null || state != null);
final RenderAnimatedSize box = tester.renderObject(find.byType(AnimatedSize));
if (size != null) {
expect(box.size.width, size);
expect(box.size.height, size);
}
if (state != null) {
expect(box.state, state);
}
}
await tester.pumpWidget(
Center(
child: AnimatedSize(
duration: const Duration(milliseconds: 200),
vsync: tester,
child: AnimatedContainer(
duration: const Duration(milliseconds: 100),
width: 100.0,
height: 100.0,
),
),
),
);
verify(size: 100.0, state: RenderAnimatedSizeState.stable);
// Animate child size from 100 to 200 slowly (100ms).
await tester.pumpWidget(
Center(
child: AnimatedSize(
duration: const Duration(milliseconds: 200),
vsync: tester,
child: AnimatedContainer(
duration: const Duration(milliseconds: 100),
width: 200.0,
height: 200.0,
),
),
),
);
// Make sure animation proceeds at child's pace, with AnimatedSize
// tightly tracking the child's size.
verify(state: RenderAnimatedSizeState.stable);
await pumpMillis(1); // register change
verify(state: RenderAnimatedSizeState.changed);
await pumpMillis(49);
verify(size: 150.0, state: RenderAnimatedSizeState.unstable);
await pumpMillis(50);
verify(size: 200.0, state: RenderAnimatedSizeState.unstable);
// Stabilize size
await pumpMillis(50);
verify(size: 200.0, state: RenderAnimatedSizeState.stable);
// Quickly (in 1ms) change size back to 100
await tester.pumpWidget(
Center(
child: AnimatedSize(
duration: const Duration(milliseconds: 200),
vsync: tester,
child: AnimatedContainer(
duration: const Duration(milliseconds: 1),
width: 100.0,
height: 100.0,
),
),
),
);
verify(size: 200.0, state: RenderAnimatedSizeState.stable);
await pumpMillis(1); // register change
verify(state: RenderAnimatedSizeState.changed);
await pumpMillis(100);
verify(size: 150.0, state: RenderAnimatedSizeState.stable);
await pumpMillis(100);
verify(size: 100.0, state: RenderAnimatedSizeState.stable);
});
testWidgets('resyncs its animation controller', (WidgetTester tester) async {
await tester.pumpWidget(
const Center(
child: AnimatedSize(
duration: Duration(milliseconds: 200),
vsync: TestVSync(),
child: SizedBox(
width: 100.0,
height: 100.0,
),
),
),
);
await tester.pumpWidget(
Center(
child: AnimatedSize(
duration: const Duration(milliseconds: 200),
vsync: tester,
child: const SizedBox(
width: 200.0,
height: 100.0,
),
),
),
);
await tester.pump(const Duration(milliseconds: 100));
final RenderBox box = tester.renderObject(find.byType(AnimatedSize));
expect(box.size.width, equals(150.0));
});
testWidgets('does not run animation unnecessarily', (WidgetTester tester) async {
await tester.pumpWidget(
Center(
child: AnimatedSize(
duration: const Duration(milliseconds: 200),
vsync: tester,
child: const SizedBox(
width: 100.0,
height: 100.0,
),
),
),
);
for (int i = 0; i < 20; i++) {
final RenderAnimatedSize box = tester.renderObject(find.byType(AnimatedSize));
expect(box.size.width, 100.0);
expect(box.size.height, 100.0);
expect(box.state, RenderAnimatedSizeState.stable);
expect(box.isAnimating, false);
await tester.pump(const Duration(milliseconds: 10));
}
});
testWidgets('can set and update clipBehavior', (WidgetTester tester) async {
await tester.pumpWidget(
Center(
child: AnimatedSize(
duration: const Duration(milliseconds: 200),
vsync: tester,
child: const SizedBox(
width: 100.0,
height: 100.0,
),
),
),
);
// By default, clipBehavior should be Clip.hardEdge
final RenderAnimatedSize renderObject = tester.renderObject(find.byType(AnimatedSize));
expect(renderObject.clipBehavior, equals(Clip.hardEdge));
for(final Clip clip in Clip.values) {
await tester.pumpWidget(
Center(
child: AnimatedSize(
duration: const Duration(milliseconds: 200),
vsync: tester,
clipBehavior: clip,
child: const SizedBox(
width: 100.0,
height: 100.0,
),
),
),
);
expect(renderObject.clipBehavior, clip);
}
});
});
}