inherited_test.dart 12.4 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2015 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.

import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';

8 9
import 'test_widgets.dart';

10
class TestInherited extends InheritedWidget {
11
  const TestInherited({ Key key, Widget child, this.shouldNotify = true })
12 13 14 15 16 17 18 19 20 21
    : super(key: key, child: child);

  final bool shouldNotify;

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    return shouldNotify;
  }
}

22
class ValueInherited extends InheritedWidget {
23
  const ValueInherited({ Key key, Widget child, this.value })
24 25 26 27 28 29 30 31
    : super(key: key, child: child);

  final int value;

  @override
  bool updateShouldNotify(ValueInherited oldWidget) => value != oldWidget.value;
}

32
class ExpectFail extends StatefulWidget {
33
  const ExpectFail(this.onError);
34 35 36
  final VoidCallback onError;

  @override
37
  ExpectFailState createState() => ExpectFailState();
38 39 40 41 42 43 44 45 46
}

class ExpectFailState extends State<ExpectFail> {
  @override
  void initState() {
    super.initState();
    try {
      context.inheritFromWidgetOfExactType(TestInherited); // should fail
    } catch (e) {
47
      widget.onError();
48 49 50 51
    }
  }

  @override
52
  Widget build(BuildContext context) => Container();
53 54
}

55
void main() {
56
  testWidgets('Inherited notifies dependents', (WidgetTester tester) async {
57
    final List<TestInherited> log = <TestInherited>[];
58

59
    final Builder builder = Builder(
60 61
      builder: (BuildContext context) {
        log.add(context.inheritFromWidgetOfExactType(TestInherited));
62
        return Container();
63 64
      }
    );
65

66
    final TestInherited first = TestInherited(child: builder);
67
    await tester.pumpWidget(first);
68

69
    expect(log, equals(<TestInherited>[first]));
70

71
    final TestInherited second = TestInherited(child: builder, shouldNotify: false);
72
    await tester.pumpWidget(second);
73

74
    expect(log, equals(<TestInherited>[first]));
75

76
    final TestInherited third = TestInherited(child: builder, shouldNotify: true);
77
    await tester.pumpWidget(third);
78

79
    expect(log, equals(<TestInherited>[first, third]));
80 81
  });

82
  testWidgets('Update inherited when reparenting state', (WidgetTester tester) async {
83
    final GlobalKey globalKey = GlobalKey();
84
    final List<TestInherited> log = <TestInherited>[];
85 86

    TestInherited build() {
87 88 89
      return TestInherited(
        key: UniqueKey(),
        child: Container(
90
          key: globalKey,
91
          child: Builder(
92 93
            builder: (BuildContext context) {
              log.add(context.inheritFromWidgetOfExactType(TestInherited));
94
              return Container();
95
            }
96
          )
97 98 99
        )
      );
    }
100

101
    final TestInherited first = build();
102
    await tester.pumpWidget(first);
103

104
    expect(log, equals(<TestInherited>[first]));
105

106
    final TestInherited second = build();
107
    await tester.pumpWidget(second);
108

109
    expect(log, equals(<TestInherited>[first, second]));
110
  });
111

112
  testWidgets('Update inherited when removing node', (WidgetTester tester) async {
113 114
    final List<String> log = <String>[];

115
    await tester.pumpWidget(
116 117
      Container(
        child: ValueInherited(
118
          value: 1,
119 120 121 122
          child: Container(
            child: FlipWidget(
              left: Container(
                child: ValueInherited(
123
                  value: 2,
124 125
                  child: Container(
                    child: ValueInherited(
126
                      value: 3,
127 128
                      child: Container(
                        child: Builder(
129
                          builder: (BuildContext context) {
130
                            final ValueInherited v = context.inheritFromWidgetOfExactType(ValueInherited);
131
                            log.add('a: ${v.value}');
Ian Hickson's avatar
Ian Hickson committed
132
                            return const Text('', textDirection: TextDirection.ltr);
133 134 135 136 137 138 139
                          }
                        )
                      )
                    )
                  )
                )
              ),
140 141
              right: Container(
                child: ValueInherited(
142
                  value: 2,
143 144 145
                  child: Container(
                    child: Container(
                      child: Builder(
146
                        builder: (BuildContext context) {
147
                          final ValueInherited v = context.inheritFromWidgetOfExactType(ValueInherited);
148
                          log.add('b: ${v.value}');
Ian Hickson's avatar
Ian Hickson committed
149
                          return const Text('', textDirection: TextDirection.ltr);
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
                        }
                      )
                    )
                  )
                )
              )
            )
          )
        )
      )
    );

    expect(log, equals(<String>['a: 3']));
    log.clear();

165
    await tester.pump();
166 167 168 169 170

    expect(log, equals(<String>[]));
    log.clear();

    flipStatefulWidget(tester);
171
    await tester.pump();
172 173 174 175 176

    expect(log, equals(<String>['b: 2']));
    log.clear();

    flipStatefulWidget(tester);
177
    await tester.pump();
178 179 180 181 182

    expect(log, equals(<String>['a: 3']));
    log.clear();
  });

183
  testWidgets('Update inherited when removing node and child has global key', (WidgetTester tester) async {
184 185 186

    final List<String> log = <String>[];

187
    final Key key = GlobalKey();
188

189
    await tester.pumpWidget(
190 191
      Container(
        child: ValueInherited(
192
          value: 1,
193 194 195 196
          child: Container(
            child: FlipWidget(
              left: Container(
                child: ValueInherited(
197
                  value: 2,
198 199
                  child: Container(
                    child: ValueInherited(
200
                      value: 3,
201
                      child: Container(
202
                        key: key,
203
                        child: Builder(
204
                          builder: (BuildContext context) {
205
                            final ValueInherited v = context.inheritFromWidgetOfExactType(ValueInherited);
206
                            log.add('a: ${v.value}');
Ian Hickson's avatar
Ian Hickson committed
207
                            return const Text('', textDirection: TextDirection.ltr);
208 209 210 211 212 213 214
                          }
                        )
                      )
                    )
                  )
                )
              ),
215 216
              right: Container(
                child: ValueInherited(
217
                  value: 2,
218 219
                  child: Container(
                    child: Container(
220
                      key: key,
221
                      child: Builder(
222
                        builder: (BuildContext context) {
223
                          final ValueInherited v = context.inheritFromWidgetOfExactType(ValueInherited);
224
                          log.add('b: ${v.value}');
Ian Hickson's avatar
Ian Hickson committed
225
                          return const Text('', textDirection: TextDirection.ltr);
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
                        }
                      )
                    )
                  )
                )
              )
            )
          )
        )
      )
    );

    expect(log, equals(<String>['a: 3']));
    log.clear();

241
    await tester.pump();
242 243 244 245 246

    expect(log, equals(<String>[]));
    log.clear();

    flipStatefulWidget(tester);
247
    await tester.pump();
248 249 250 251 252

    expect(log, equals(<String>['b: 2']));
    log.clear();

    flipStatefulWidget(tester);
253
    await tester.pump();
254 255 256 257 258

    expect(log, equals(<String>['a: 3']));
    log.clear();
  });

259
  testWidgets('Update inherited when removing node and child has global key with constant child', (WidgetTester tester) async {
260 261
    final List<int> log = <int>[];

262
    final Key key = GlobalKey();
263

264
    final Widget child = Builder(
265
      builder: (BuildContext context) {
266
        final ValueInherited v = context.inheritFromWidgetOfExactType(ValueInherited);
267
        log.add(v.value);
Ian Hickson's avatar
Ian Hickson committed
268
        return const Text('', textDirection: TextDirection.ltr);
269 270 271
      }
    );

272
    await tester.pumpWidget(
273 274
      Container(
        child: ValueInherited(
275
          value: 1,
276 277 278 279
          child: Container(
            child: FlipWidget(
              left: Container(
                child: ValueInherited(
280
                  value: 2,
281 282
                  child: Container(
                    child: ValueInherited(
283
                      value: 3,
284
                      child: Container(
285 286 287 288 289 290 291
                        key: key,
                        child: child
                      )
                    )
                  )
                )
              ),
292 293
              right: Container(
                child: ValueInherited(
294
                  value: 2,
295 296
                  child: Container(
                    child: Container(
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
                      key: key,
                      child: child
                    )
                  )
                )
              )
            )
          )
        )
      )
    );

    expect(log, equals(<int>[3]));
    log.clear();

312
    await tester.pump();
313 314 315 316 317

    expect(log, equals(<int>[]));
    log.clear();

    flipStatefulWidget(tester);
318
    await tester.pump();
319 320 321 322 323

    expect(log, equals(<int>[2]));
    log.clear();

    flipStatefulWidget(tester);
324
    await tester.pump();
325 326 327 328 329

    expect(log, equals(<int>[3]));
    log.clear();
  });

330
  testWidgets('Update inherited when removing node and child has global key with constant child, minimised', (WidgetTester tester) async {
331 332 333

    final List<int> log = <int>[];

334 335
    final Widget child = Builder(
      key: GlobalKey(),
336
      builder: (BuildContext context) {
337
        final ValueInherited v = context.inheritFromWidgetOfExactType(ValueInherited);
338
        log.add(v.value);
Ian Hickson's avatar
Ian Hickson committed
339
        return const Text('', textDirection: TextDirection.ltr);
340 341 342
      }
    );

343
    await tester.pumpWidget(
344
      ValueInherited(
345
        value: 2,
346 347
        child: FlipWidget(
          left: ValueInherited(
348 349 350 351 352 353 354 355 356 357 358
            value: 3,
            child: child
          ),
          right: child
        )
      )
    );

    expect(log, equals(<int>[3]));
    log.clear();

359
    await tester.pump();
360 361 362 363 364

    expect(log, equals(<int>[]));
    log.clear();

    flipStatefulWidget(tester);
365
    await tester.pump();
366 367 368 369 370

    expect(log, equals(<int>[2]));
    log.clear();

    flipStatefulWidget(tester);
371
    await tester.pump();
372 373 374 375 376

    expect(log, equals(<int>[3]));
    log.clear();
  });

377
  testWidgets('Inherited widget notifies descendants when descendant previously failed to find a match', (WidgetTester tester) async {
378 379
    int inheritedValue = -1;

380 381 382
    final Widget inner = Container(
      key: GlobalKey(),
      child: Builder(
383
        builder: (BuildContext context) {
384
          final ValueInherited widget = context.inheritFromWidgetOfExactType(ValueInherited);
385
          inheritedValue = widget?.value;
386
          return Container();
387 388 389 390
        }
      )
    );

391
    await tester.pumpWidget(
392 393 394 395 396
      inner
    );
    expect(inheritedValue, isNull);

    inheritedValue = -2;
397
    await tester.pumpWidget(
398
      ValueInherited(
399 400 401 402 403 404 405
        value: 3,
        child: inner
      )
    );
    expect(inheritedValue, equals(3));
  });

406
  testWidgets('Inherited widget doesn\'t notify descendants when descendant did not previously fail to find a match and had no dependencies', (WidgetTester tester) async {
407 408
    int buildCount = 0;

409 410 411
    final Widget inner = Container(
      key: GlobalKey(),
      child: Builder(
412 413
        builder: (BuildContext context) {
          buildCount += 1;
414
          return Container();
415 416 417 418
        }
      )
    );

419
    await tester.pumpWidget(
420 421 422 423
      inner
    );
    expect(buildCount, equals(1));

424
    await tester.pumpWidget(
425
      ValueInherited(
426 427 428 429 430 431 432
        value: 3,
        child: inner
      )
    );
    expect(buildCount, equals(1));
  });

433
  testWidgets('Inherited widget does notify descendants when descendant did not previously fail to find a match but did have other dependencies', (WidgetTester tester) async {
434 435
    int buildCount = 0;

436 437 438
    final Widget inner = Container(
      key: GlobalKey(),
      child: TestInherited(
439
        shouldNotify: false,
440
        child: Builder(
441 442 443
          builder: (BuildContext context) {
            context.inheritFromWidgetOfExactType(TestInherited);
            buildCount += 1;
444
            return Container();
445 446 447 448 449
          }
        )
      )
    );

450
    await tester.pumpWidget(
451 452 453 454
      inner
    );
    expect(buildCount, equals(1));

455
    await tester.pumpWidget(
456
      ValueInherited(
457 458 459 460 461 462
        value: 3,
        child: inner
      )
    );
    expect(buildCount, equals(2));
  });
463 464 465 466 467

  testWidgets('initState() dependency on Inherited asserts', (WidgetTester tester) async {
    // This is a regression test for https://github.com/flutter/flutter/issues/5491
    bool exceptionCaught = false;

468
    final TestInherited parent = TestInherited(child: ExpectFail(() {
469 470 471 472 473 474
      exceptionCaught = true;
    }));
    await tester.pumpWidget(parent);

    expect(exceptionCaught, isTrue);
  });
475
}