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

import 'package:flutter/material.dart';
6
import 'package:flutter_test/flutter_test.dart';
7 8 9 10

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

class Bar extends StatefulWidget {
11
  const Bar({ super.key });
12
  @override
13
  BarState createState() => BarState();
14 15 16
}

class BarState extends State<Bar> {
17
  final GlobalKey _fooKey = GlobalKey();
18 19 20 21 22 23 24 25 26 27 28 29

  bool _mode = false;

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

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

class StatefulCreationCounter extends StatefulWidget {
48
  const StatefulCreationCounter({ super.key });
49 50

  @override
51
  StatefulCreationCounterState createState() => StatefulCreationCounterState();
52 53 54 55 56 57 58 59 60 61 62 63
}

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

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

  @override
64
  Widget build(BuildContext context) => Container();
65 66 67
}

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

78
  testWidgets('Clean then reparent with dependencies', (WidgetTester tester) async {
79
    int layoutBuilderBuildCount = 0;
80

81 82 83
    late StateSetter keyedSetState;
    late StateSetter layoutBuilderSetState;
    late StateSetter childSetState;
84

85 86
    final GlobalKey key = GlobalKey();
    final Widget keyedWidget = StatefulBuilder(
87 88 89 90
      key: key,
      builder: (BuildContext context, StateSetter setState) {
        keyedSetState = setState;
        MediaQuery.of(context);
91
        return Container();
92 93 94 95
      },
    );

    Widget layoutBuilderChild = keyedWidget;
96
    Widget deepChild = Container();
97

98
    await tester.pumpWidget(MediaQuery(
99
      data: MediaQueryData.fromView(tester.view),
100
      child: Column(
101
        children: <Widget>[
102
          StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
103
            layoutBuilderSetState = setState;
104
            return LayoutBuilder(
105
              builder: (BuildContext context, BoxConstraints constraints) {
106 107
                layoutBuilderBuildCount += 1;
                return layoutBuilderChild; // initially keyedWidget above, but then a new Container
108 109 110
              },
            );
          }),
111
          ColoredBox(
112
            color: Colors.green,
113
            child: ColoredBox(
114
              color: Colors.green,
115
              child: ColoredBox(
116
                color: Colors.green,
117
                child: ColoredBox(
118
                  color: Colors.green,
119
                  child: ColoredBox(
120
                    color: Colors.green,
121
                    child: ColoredBox(
122
                      color: Colors.green,
123 124 125 126 127 128
                      child: StatefulBuilder(
                        builder: (BuildContext context, StateSetter setState) {
                          childSetState = setState;
                          return deepChild; // initially a Container, but then the keyedWidget above
                        },
                      ),
129 130 131 132 133 134 135 136 137 138 139
                    ),
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    ));
    expect(layoutBuilderBuildCount, 1);

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

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

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

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