animated_cross_fade_test.dart 12.3 KB
Newer Older
1 2 3 4
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

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

void main() {
  testWidgets('AnimatedCrossFade test', (WidgetTester tester) async {
    await tester.pumpWidget(
13 14
      const Directionality(
        textDirection: TextDirection.ltr,
15 16 17
        child: Center(
          child: AnimatedCrossFade(
            firstChild: SizedBox(
18 19 20
              width: 100.0,
              height: 100.0,
            ),
21
            secondChild: SizedBox(
22 23 24
              width: 200.0,
              height: 200.0,
            ),
25
            duration: Duration(milliseconds: 200),
26
            crossFadeState: CrossFadeState.showFirst,
27
          ),
28 29
        ),
      ),
30 31 32 33 34 35 36 37
    );

    expect(find.byType(FadeTransition), findsNWidgets(2));
    RenderBox box = tester.renderObject(find.byType(AnimatedCrossFade));
    expect(box.size.width, equals(100.0));
    expect(box.size.height, equals(100.0));

    await tester.pumpWidget(
38 39
      const Directionality(
        textDirection: TextDirection.ltr,
40 41 42
        child: Center(
          child: AnimatedCrossFade(
            firstChild: SizedBox(
43 44 45
              width: 100.0,
              height: 100.0,
            ),
46
            secondChild: SizedBox(
47 48 49
              width: 200.0,
              height: 200.0,
            ),
50
            duration: Duration(milliseconds: 200),
51
            crossFadeState: CrossFadeState.showSecond,
52
          ),
53 54
        ),
      ),
55 56 57 58 59 60 61 62 63
    );

    await tester.pump(const Duration(milliseconds: 100));

    expect(find.byType(FadeTransition), findsNWidgets(2));
    box = tester.renderObject(find.byType(AnimatedCrossFade));
    expect(box.size.width, equals(150.0));
    expect(box.size.height, equals(150.0));
  });
64 65 66

  testWidgets('AnimatedCrossFade test showSecond', (WidgetTester tester) async {
    await tester.pumpWidget(
67 68
      const Directionality(
        textDirection: TextDirection.ltr,
69 70 71
        child: Center(
          child: AnimatedCrossFade(
            firstChild: SizedBox(
72 73 74
              width: 100.0,
              height: 100.0,
            ),
75
            secondChild: SizedBox(
76 77 78
              width: 200.0,
              height: 200.0,
            ),
79
            duration: Duration(milliseconds: 200),
80
            crossFadeState: CrossFadeState.showSecond,
81
          ),
82 83
        ),
      ),
84 85 86
    );

    expect(find.byType(FadeTransition), findsNWidgets(2));
87
    final RenderBox box = tester.renderObject(find.byType(AnimatedCrossFade));
88 89 90
    expect(box.size.width, equals(200.0));
    expect(box.size.height, equals(200.0));
  });
91

92
  testWidgets('AnimatedCrossFade alignment (VISUAL)', (WidgetTester tester) async {
93 94
    final Key firstKey = UniqueKey();
    final Key secondKey = UniqueKey();
95 96

    await tester.pumpWidget(
97
      Directionality(
98
        textDirection: TextDirection.ltr,
99 100
        child: Center(
          child: AnimatedCrossFade(
101
            alignment: Alignment.bottomRight,
102
            firstChild: SizedBox(
103 104 105 106
              key: firstKey,
              width: 100.0,
              height: 100.0,
            ),
107
            secondChild: SizedBox(
108 109 110 111 112 113
              key: secondKey,
              width: 200.0,
              height: 200.0,
            ),
            duration: const Duration(milliseconds: 200),
            crossFadeState: CrossFadeState.showFirst,
114
          ),
115 116
        ),
      ),
117 118 119
    );

    await tester.pumpWidget(
120
      Directionality(
121
        textDirection: TextDirection.ltr,
122 123
        child: Center(
          child: AnimatedCrossFade(
124
            alignment: Alignment.bottomRight,
125
            firstChild: SizedBox(
126 127 128 129
              key: firstKey,
              width: 100.0,
              height: 100.0,
            ),
130
            secondChild: SizedBox(
131 132 133 134 135 136
              key: secondKey,
              width: 200.0,
              height: 200.0,
            ),
            duration: const Duration(milliseconds: 200),
            crossFadeState: CrossFadeState.showSecond,
137
          ),
138 139 140 141 142 143 144 145 146 147 148 149 150
        ),
      ),
    );

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

  testWidgets('AnimatedCrossFade alignment (LTR)', (WidgetTester tester) async {
151 152
    final Key firstKey = UniqueKey();
    final Key secondKey = UniqueKey();
153 154

    await tester.pumpWidget(
155
      Directionality(
156
        textDirection: TextDirection.ltr,
157 158
        child: Center(
          child: AnimatedCrossFade(
159
            alignment: AlignmentDirectional.bottomEnd,
160
            firstChild: SizedBox(
161 162 163 164
              key: firstKey,
              width: 100.0,
              height: 100.0,
            ),
165
            secondChild: SizedBox(
166 167 168 169 170 171 172 173 174 175 176 177
              key: secondKey,
              width: 200.0,
              height: 200.0,
            ),
            duration: const Duration(milliseconds: 200),
            crossFadeState: CrossFadeState.showFirst,
          ),
        ),
      ),
    );

    await tester.pumpWidget(
178
      Directionality(
179
        textDirection: TextDirection.ltr,
180 181
        child: Center(
          child: AnimatedCrossFade(
182
            alignment: AlignmentDirectional.bottomEnd,
183
            firstChild: SizedBox(
184 185 186 187
              key: firstKey,
              width: 100.0,
              height: 100.0,
            ),
188
            secondChild: SizedBox(
189 190 191 192 193 194 195 196 197
              key: secondKey,
              width: 200.0,
              height: 200.0,
            ),
            duration: const Duration(milliseconds: 200),
            crossFadeState: CrossFadeState.showSecond,
          ),
        ),
      ),
198 199 200 201 202 203 204 205 206 207
    );

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

208
  testWidgets('AnimatedCrossFade alignment (RTL)', (WidgetTester tester) async {
209 210
    final Key firstKey = UniqueKey();
    final Key secondKey = UniqueKey();
211 212

    await tester.pumpWidget(
213
      Directionality(
214
        textDirection: TextDirection.rtl,
215 216
        child: Center(
          child: AnimatedCrossFade(
217
            alignment: AlignmentDirectional.bottomEnd,
218
            firstChild: SizedBox(
219 220 221 222
              key: firstKey,
              width: 100.0,
              height: 100.0,
            ),
223
            secondChild: SizedBox(
224 225 226 227 228 229 230 231 232 233 234 235
              key: secondKey,
              width: 200.0,
              height: 200.0,
            ),
            duration: const Duration(milliseconds: 200),
            crossFadeState: CrossFadeState.showFirst,
          ),
        ),
      ),
    );

    await tester.pumpWidget(
236
      Directionality(
237
        textDirection: TextDirection.rtl,
238 239
        child: Center(
          child: AnimatedCrossFade(
240
            alignment: AlignmentDirectional.bottomEnd,
241
            firstChild: SizedBox(
242 243 244 245
              key: firstKey,
              width: 100.0,
              height: 100.0,
            ),
246
            secondChild: SizedBox(
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
              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(325.0, 175.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(325.0, 175.0));
  });

266
  Widget crossFadeWithWatcher({bool towardsSecond = false}) {
267
    return Directionality(
268
      textDirection: TextDirection.ltr,
269
      child: AnimatedCrossFade(
270
        firstChild: const _TickerWatchingWidget(),
271
        secondChild: Container(),
272 273 274
        crossFadeState: towardsSecond ? CrossFadeState.showSecond : CrossFadeState.showFirst,
        duration: const Duration(milliseconds: 50),
      ),
275 276 277 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 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
    );
  }

  testWidgets('AnimatedCrossFade preserves widget state', (WidgetTester tester) async {
    await tester.pumpWidget(crossFadeWithWatcher());

    _TickerWatchingWidgetState findState() => tester.state(find.byType(_TickerWatchingWidget));
    final _TickerWatchingWidgetState state = findState();

    await tester.pumpWidget(crossFadeWithWatcher(towardsSecond: true));
    for (int i = 0; i < 3; i += 1) {
      await tester.pump(const Duration(milliseconds: 25));
      expect(findState(), same(state));
    }
  });

  testWidgets('AnimatedCrossFade switches off TickerMode and semantics on faded out widget', (WidgetTester tester) async {
    ExcludeSemantics findSemantics() {
      return tester.widget(find.descendant(
        of: find.byKey(const ValueKey<CrossFadeState>(CrossFadeState.showFirst)),
        matching: find.byType(ExcludeSemantics),
      ));
    }

    await tester.pumpWidget(crossFadeWithWatcher());

    final _TickerWatchingWidgetState state = tester.state(find.byType(_TickerWatchingWidget));
    expect(state.ticker.muted, false);
    expect(findSemantics().excluding, false);

    await tester.pumpWidget(crossFadeWithWatcher(towardsSecond: true));
    for (int i = 0; i < 2; i += 1) {
      await tester.pump(const Duration(milliseconds: 25));
      // Animations are kept alive in the middle of cross-fade
      expect(state.ticker.muted, false);
      // Semantics are turned off immediately on the widget that's fading out
      expect(findSemantics().excluding, true);
    }

    // In the final state both animations and semantics should be off on the
    // widget that's faded out.
    await tester.pump(const Duration(milliseconds: 25));
    expect(state.ticker.muted, true);
    expect(findSemantics().excluding, true);
  });
320 321

  testWidgets('AnimatedCrossFade.layoutBuilder', (WidgetTester tester) async {
322 323 324
    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.ltr,
325 326 327
        child: AnimatedCrossFade(
          firstChild: Text('AAA', textDirection: TextDirection.ltr),
          secondChild: Text('BBB', textDirection: TextDirection.ltr),
328
          crossFadeState: CrossFadeState.showFirst,
329
          duration: Duration(milliseconds: 50),
330 331 332
        ),
      ),
    );
333 334
    expect(find.text('AAA'), findsOneWidget);
    expect(find.text('BBB'), findsOneWidget);
335
    await tester.pumpWidget(
336
      Directionality(
337
        textDirection: TextDirection.ltr,
338
        child: AnimatedCrossFade(
339 340 341 342 343 344 345 346
          firstChild: const Text('AAA', textDirection: TextDirection.ltr),
          secondChild: const Text('BBB', textDirection: TextDirection.ltr),
          crossFadeState: CrossFadeState.showFirst,
          duration: const Duration(milliseconds: 50),
          layoutBuilder: (Widget a, Key aKey, Widget b, Key bKey) => a,
        ),
      ),
    );
347 348
    expect(find.text('AAA'), findsOneWidget);
    expect(find.text('BBB'), findsNothing);
349
    await tester.pumpWidget(
350
      Directionality(
351
        textDirection: TextDirection.ltr,
352
        child: AnimatedCrossFade(
353 354 355 356 357 358 359 360
          firstChild: const Text('AAA', textDirection: TextDirection.ltr),
          secondChild: const Text('BBB', textDirection: TextDirection.ltr),
          crossFadeState: CrossFadeState.showSecond,
          duration: const Duration(milliseconds: 50),
          layoutBuilder: (Widget a, Key aKey, Widget b, Key bKey) => a,
        ),
      ),
    );
361 362 363
    expect(find.text('BBB'), findsOneWidget);
    expect(find.text('AAA'), findsNothing);
  });
364 365 366 367 368 369
}

class _TickerWatchingWidget extends StatefulWidget {
  const _TickerWatchingWidget();

  @override
370
  State<StatefulWidget> createState() => _TickerWatchingWidgetState();
371 372 373 374 375 376 377 378 379 380 381 382
}

class _TickerWatchingWidgetState extends State<_TickerWatchingWidget> with SingleTickerProviderStateMixin {
  Ticker ticker;

  @override
  void initState() {
    super.initState();
    ticker = createTicker((_) {})..start();
  }

  @override
383
  Widget build(BuildContext context) => Container();
384 385 386 387 388 389

  @override
  void dispose() {
    ticker.dispose();
    super.dispose();
  }
390
}