animated_size_test.dart 11.1 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
7
import 'package:flutter_test/flutter_test.dart';
8 9 10 11 12

class TestPaintingContext implements PaintingContext {
  final List<Invocation> invocations = <Invocation>[];

  @override
13 14 15
  void noSuchMethod(Invocation invocation) {
    invocations.add(invocation);
  }
16 17 18
}

void main() {
19 20 21
  group('AnimatedSize', () {
    testWidgets('animates forwards then backwards with stable-sized children', (WidgetTester tester) async {
      await tester.pumpWidget(
22
        const Center(
23
          child: AnimatedSize(
24 25
            duration: Duration(milliseconds: 200),
            child: SizedBox(
26 27 28
              width: 100.0,
              height: 100.0,
            ),
29 30
          ),
        ),
31 32 33 34 35 36 37
      );

      RenderBox box = tester.renderObject(find.byType(AnimatedSize));
      expect(box.size.width, equals(100.0));
      expect(box.size.height, equals(100.0));

      await tester.pumpWidget(
38
        const Center(
39
          child: AnimatedSize(
40 41
            duration: Duration(milliseconds: 200),
            child: SizedBox(
42 43 44
              width: 200.0,
              height: 200.0,
            ),
45 46
          ),
        ),
47 48 49 50 51 52 53
      );

      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));

54
      TestPaintingContext context = TestPaintingContext();
55 56 57 58 59 60 61 62 63
      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(
64
        const Center(
65
          child: AnimatedSize(
66 67
            duration: Duration(milliseconds: 200),
            child: SizedBox(
68 69 70 71 72 73 74 75 76 77 78 79
              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));

80
      context = TestPaintingContext();
81 82 83 84 85 86 87 88 89 90 91
      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(
92
        const Center(
93
          child: SizedBox (
94
            width: 100.0,
95
            height: 100.0,
96
            child: AnimatedSize(
97 98
              duration: Duration(milliseconds: 200),
              child: SizedBox(
99 100 101 102
                width: 100.0,
                height: 100.0,
              ),
            ),
103 104
          ),
        ),
105 106 107 108 109 110 111 112
      );

      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(
113
        const Center(
114
          child: SizedBox (
115 116
            width: 100.0,
            height: 100.0,
117
            child: AnimatedSize(
118 119
              duration: Duration(milliseconds: 200),
              child: SizedBox(
120 121 122 123 124 125 126 127 128 129 130 131 132 133
                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));
    });
134

135
    testWidgets('tracks unstable child, then resumes animation when child stabilizes', (WidgetTester tester) async {
136
      Future<void> pumpMillis(int millis) async {
137
        await tester.pump(Duration(milliseconds: millis));
138 139
      }

140
      void verify({ double? size, RenderAnimatedSizeState? state }) {
141 142 143 144 145 146 147 148 149 150 151 152
        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(
153 154
        Center(
          child: AnimatedSize(
155
            duration: const Duration(milliseconds: 200),
156
            child: AnimatedContainer(
157
              duration: const Duration(milliseconds: 100),
158
              width: 100.0,
159 160 161 162
              height: 100.0,
            ),
          ),
        ),
163 164 165 166 167 168
      );

      verify(size: 100.0, state: RenderAnimatedSizeState.stable);

      // Animate child size from 100 to 200 slowly (100ms).
      await tester.pumpWidget(
169 170
        Center(
          child: AnimatedSize(
171
            duration: const Duration(milliseconds: 200),
172
            child: AnimatedContainer(
173
              duration: const Duration(milliseconds: 100),
174
              width: 200.0,
175 176 177 178
              height: 200.0,
            ),
          ),
        ),
179
      );
180

181 182 183
      // Make sure animation proceeds at child's pace, with AnimatedSize
      // tightly tracking the child's size.
      verify(state: RenderAnimatedSizeState.stable);
184
      await pumpMillis(1); // register change
185 186 187 188 189
      verify(state: RenderAnimatedSizeState.changed);
      await pumpMillis(49);
      verify(size: 150.0, state: RenderAnimatedSizeState.unstable);
      await pumpMillis(50);
      verify(size: 200.0, state: RenderAnimatedSizeState.unstable);
190

191 192 193 194 195 196
      // Stabilize size
      await pumpMillis(50);
      verify(size: 200.0, state: RenderAnimatedSizeState.stable);

      // Quickly (in 1ms) change size back to 100
      await tester.pumpWidget(
197 198
        Center(
          child: AnimatedSize(
199
            duration: const Duration(milliseconds: 200),
200
            child: AnimatedContainer(
201 202 203 204
              duration: const Duration(milliseconds: 1),
              width: 100.0,
              height: 100.0,
            ),
205 206
          ),
        ),
207 208 209
      );

      verify(size: 200.0, state: RenderAnimatedSizeState.stable);
210
      await pumpMillis(1); // register change
211 212 213 214 215 216 217 218 219 220
      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(
221 222 223
          child: AnimatedSize(
            duration: Duration(milliseconds: 200),
            child: SizedBox(
224 225 226
              width: 100.0,
              height: 100.0,
            ),
227 228
          ),
        ),
229
      );
230

231
      await tester.pumpWidget(
232
        const Center(
233
          child: AnimatedSize(
234 235
            duration: Duration(milliseconds: 200),
            child: SizedBox(
236 237 238
              width: 200.0,
              height: 100.0,
            ),
239 240
          ),
        ),
241 242 243 244 245 246 247 248 249 250
      );

      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(
251
        const Center(
252
          child: AnimatedSize(
253 254
            duration: Duration(milliseconds: 200),
            child: SizedBox(
255 256 257
              width: 100.0,
              height: 100.0,
            ),
258 259
          ),
        ),
260
      );
261

262 263 264 265 266 267 268 269 270
      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));
      }
    });
271 272 273

    testWidgets('can set and update clipBehavior', (WidgetTester tester) async {
      await tester.pumpWidget(
274
        const Center(
275
          child: AnimatedSize(
276 277
            duration: Duration(milliseconds: 200),
            child: SizedBox(
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
              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),
              clipBehavior: clip,
              child: const SizedBox(
                width: 100.0,
                height: 100.0,
              ),
            ),
          ),
        );
        expect(renderObject.clipBehavior, clip);
      }
    });
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351

    testWidgets('works wrapped in IntrinsicHeight and Wrap', (WidgetTester tester) async {
      Future<void> pumpWidget(Size size, [Duration? duration]) async {
        return tester.pumpWidget(
          Center(
            child: IntrinsicHeight(
              child: Wrap(
                textDirection: TextDirection.ltr,
                children: <Widget>[
                  AnimatedSize(
                    duration: const Duration(milliseconds: 200),
                    curve: Curves.easeInOutBack,
                    child: SizedBox(
                      width: size.width,
                      height: size.height,
                    ),
                  ),
                ],
              ),
            ),
          ),
          duration,
        );
      }

      await pumpWidget(const Size(100, 100));
      expect(tester.renderObject<RenderBox>(find.byType(IntrinsicHeight)).size, const Size(100, 100));

      await pumpWidget(const Size(150, 200));
      expect(tester.renderObject<RenderBox>(find.byType(IntrinsicHeight)).size, const Size(100, 100));

      // Each pump triggers verification of dry layout.
      for (int total = 0; total < 200; total += 10) {
        await tester.pump(const Duration(milliseconds: 10));
      }
      expect(tester.renderObject<RenderBox>(find.byType(IntrinsicHeight)).size, const Size(150, 200));

      // Change every pump
      await pumpWidget(const Size(100, 100));
      expect(tester.renderObject<RenderBox>(find.byType(IntrinsicHeight)).size, const Size(150, 200));

      await pumpWidget(const Size(111, 111), const Duration(milliseconds: 10));
      expect(tester.renderObject<RenderBox>(find.byType(IntrinsicHeight)).size, const Size(111, 111));

      await pumpWidget(const Size(222, 222), const Duration(milliseconds: 10));
      expect(tester.renderObject<RenderBox>(find.byType(IntrinsicHeight)).size, const Size(222, 222));
    });
352
  });
353
}