reparent_state_with_layout_builder_test.dart 4.57 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 6
import 'dart:ui' as ui show window;

7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart' hide TypeMatcher;

// This is a regression test for https://github.com/flutter/flutter/issues/5840.

class Bar extends StatefulWidget {
  @override
  BarState createState() => new BarState();
}

class BarState extends State<Bar> {
  final GlobalKey _fooKey = new GlobalKey();

  bool _mode = false;

  void trigger() {
    setState(() {
      _mode = !_mode;
    });
  }

  @override
  Widget build(BuildContext context) {
    if (_mode) {
      return new SizedBox(
        child: new LayoutBuilder(
33 34 35
          builder: (BuildContext context, BoxConstraints constraints) {
            return new StatefulCreationCounter(key: _fooKey);
          },
36 37 38 39
        ),
      );
    } else {
      return new LayoutBuilder(
40 41 42
        builder: (BuildContext context, BoxConstraints constraints) {
          return new StatefulCreationCounter(key: _fooKey);
        },
43 44 45 46 47 48
      );
    }
  }
}

class StatefulCreationCounter extends StatefulWidget {
49
  const StatefulCreationCounter({ Key key }) : super(key: key);
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

  @override
  StatefulCreationCounterState createState() => new StatefulCreationCounterState();
}

class StatefulCreationCounterState extends State<StatefulCreationCounter> {
  static int creationCount = 0;

  @override
  void initState() {
    super.initState();
    creationCount += 1;
  }

  @override
  Widget build(BuildContext context) => new Container();
}

void main() {
69 70
  testWidgets('reparent state with layout builder',
      (WidgetTester tester) async {
71 72 73
    expect(StatefulCreationCounterState.creationCount, 0);
    await tester.pumpWidget(new Bar());
    expect(StatefulCreationCounterState.creationCount, 1);
74
    final BarState s = tester.state<BarState>(find.byType(Bar));
75 76 77 78
    s.trigger();
    await tester.pump();
    expect(StatefulCreationCounterState.creationCount, 1);
  });
79 80 81

  testWidgets('Clean then reparent with dependencies',
      (WidgetTester tester) async {
82 83

    int layoutBuilderBuildCount = 0;
84 85

    StateSetter keyedSetState;
86 87
    StateSetter layoutBuilderSetState;
    StateSetter childSetState;
88

89
    final GlobalKey key = new GlobalKey();
90
    final Widget keyedWidget = new StatefulBuilder(
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
      key: key,
      builder: (BuildContext context, StateSetter setState) {
        keyedSetState = setState;
        MediaQuery.of(context);
        return new Container();
      },
    );

    Widget layoutBuilderChild = keyedWidget;
    Widget deepChild = new Container();

    await tester.pumpWidget(new MediaQuery(
      data: new MediaQueryData.fromWindow(ui.window),
      child: new Column(
        children: <Widget>[
          new StatefulBuilder(
              builder: (BuildContext context, StateSetter setState) {
            layoutBuilderSetState = setState;
            return new LayoutBuilder(
              builder: (BuildContext context, BoxConstraints constraints) {
111 112
                layoutBuilderBuildCount += 1;
                return layoutBuilderChild; // initially keyedWidget above, but then a new Container
113 114 115 116 117 118 119 120 121 122 123 124
              },
            );
          }),
          new Container(
            child: new Container(
              child: new Container(
                child: new Container(
                  child: new Container(
                    child: new Container(
                      child: new StatefulBuilder(builder:
                          (BuildContext context, StateSetter setState) {
                        childSetState = setState;
125
                        return deepChild; // initially a Container, but then the keyedWidget above
126 127 128 129 130 131 132 133 134 135 136 137
                      }),
                    ),
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    ));
    expect(layoutBuilderBuildCount, 1);

138
    keyedSetState(() { /* Change nothing but add the element to the dirty list. */ });
139 140

    childSetState(() {
141 142
      // The deep child builds in the initial build phase. It takes the child
      // from the LayoutBuilder before the LayoutBuilder has a chance to build.
143 144 145 146
      deepChild = keyedWidget;
    });

    layoutBuilderSetState(() {
147 148
      // The layout builder will build in a separate build scope. This delays
      // the removal of the keyed child until this build scope.
149 150 151 152 153 154 155
      layoutBuilderChild = new Container();
    });

    // The essential part of this test is that this call to pump doesn't throw.
    await tester.pump();
    expect(layoutBuilderBuildCount, 2);
  });
156
}