build_scope_test.dart 5.65 KB
Newer Older
Hixie's avatar
Hixie committed
1 2 3 4
// 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.

Adam Barth's avatar
Adam Barth committed
5
import 'package:flutter_test/flutter_test.dart';
6
import 'package:flutter/widgets.dart';
7

Adam Barth's avatar
Adam Barth committed
8
import 'test_widgets.dart';
9

10
class ProbeWidget extends StatefulWidget {
11
  @override
12
  ProbeWidgetState createState() => ProbeWidgetState();
13 14 15 16 17
}

class ProbeWidgetState extends State<ProbeWidget> {
  static int buildCount = 0;

18
  @override
19 20
  void initState() {
    super.initState();
21
    setState(() { });
22 23
  }

24
  @override
25
  void didUpdateWidget(ProbeWidget oldWidget) {
26
    super.didUpdateWidget(oldWidget);
27
    setState(() { });
28 29
  }

30
  @override
31
  Widget build(BuildContext context) {
32
    setState(() { });
33
    buildCount++;
34
    return Container();
35 36 37
  }
}

38
class BadWidget extends StatelessWidget {
39
  const BadWidget(this.parentState);
40

41
  final BadWidgetParentState parentState;
42

43
  @override
44
  Widget build(BuildContext context) {
45
    parentState._markNeedsBuild();
46
    return Container();
47 48 49
  }
}

50
class BadWidgetParent extends StatefulWidget {
51
  @override
52
  BadWidgetParentState createState() => BadWidgetParentState();
53 54 55
}

class BadWidgetParentState extends State<BadWidgetParent> {
56 57 58 59 60 61 62
  void _markNeedsBuild() {
    setState(() {
      // Our state didn't really change, but we're doing something pathological
      // here to trigger an interesting scenario to test.
    });
  }

63
  @override
64
  Widget build(BuildContext context) {
65
    return BadWidget(this);
66 67 68
  }
}

69
class BadDisposeWidget extends StatefulWidget {
70
  @override
71
  BadDisposeWidgetState createState() => BadDisposeWidgetState();
72 73 74
}

class BadDisposeWidgetState extends State<BadDisposeWidget> {
75
  @override
76
  Widget build(BuildContext context) {
77
    return Container();
78 79
  }

80
  @override
81
  void dispose() {
82
    setState(() { /* This is invalid behavior. */ });
83 84 85 86
    super.dispose();
  }
}

87
class StatefulWrapper extends StatefulWidget {
88
  const StatefulWrapper({
89 90 91 92 93 94 95
    Key key,
    this.child,
  }) : super(key: key);

  final Widget child;

  @override
96
  StatefulWrapperState createState() => StatefulWrapperState();
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
}

class StatefulWrapperState extends State<StatefulWrapper> {

  void trigger() {
    setState(() { built = null; });
  }

  int built;
  int oldBuilt;

  static int buildId = 0;

  @override
  Widget build(BuildContext context) {
    buildId += 1;
    built = buildId;
114
    return widget.child;
115 116 117 118
  }
}

class Wrapper extends StatelessWidget {
119
  const Wrapper({
120 121 122 123 124 125 126 127 128 129 130 131
    Key key,
    this.child,
  }) : super(key: key);

  final Widget child;

  @override
  Widget build(BuildContext context) {
    return child;
  }
}

132
void main() {
133
  testWidgets('Legal times for setState', (WidgetTester tester) async {
134
    final GlobalKey flipKey = GlobalKey();
135
    expect(ProbeWidgetState.buildCount, equals(0));
136
    await tester.pumpWidget(ProbeWidget());
137
    expect(ProbeWidgetState.buildCount, equals(1));
138
    await tester.pumpWidget(ProbeWidget());
139
    expect(ProbeWidgetState.buildCount, equals(2));
140
    await tester.pumpWidget(FlipWidget(
141
      key: flipKey,
142
      left: Container(),
143
      right: ProbeWidget(),
144 145
    ));
    expect(ProbeWidgetState.buildCount, equals(2));
146
    final FlipWidgetState flipState1 = flipKey.currentState;
147
    flipState1.flip();
148
    await tester.pump();
149
    expect(ProbeWidgetState.buildCount, equals(3));
150
    final FlipWidgetState flipState2 = flipKey.currentState;
151
    flipState2.flip();
152
    await tester.pump();
153
    expect(ProbeWidgetState.buildCount, equals(3));
154
    await tester.pumpWidget(Container());
155
    expect(ProbeWidgetState.buildCount, equals(3));
156 157
  });

158
  testWidgets('Setting parent state during build is forbidden', (WidgetTester tester) async {
159
    await tester.pumpWidget(BadWidgetParent());
160
    expect(tester.takeException(), isFlutterError);
161
    await tester.pumpWidget(Container());
162 163
  });

164
  testWidgets('Setting state during dispose is forbidden', (WidgetTester tester) async {
165
    await tester.pumpWidget(BadDisposeWidget());
166
    expect(tester.takeException(), isNull);
167
    await tester.pumpWidget(Container());
168
    expect(tester.takeException(), isNotNull);
169
  });
170 171

  testWidgets('Dirty element list sort order', (WidgetTester tester) async {
172 173
    final GlobalKey key1 = GlobalKey(debugLabel: 'key1');
    final GlobalKey key2 = GlobalKey(debugLabel: 'key2');
174 175 176

    bool didMiddle = false;
    Widget middle;
177
    final List<StateSetter> setStates = <StateSetter>[];
178 179
    Widget builder(BuildContext context, StateSetter setState) {
      setStates.add(setState);
180
      final bool returnMiddle = !didMiddle;
181
      didMiddle = true;
182 183 184 185
      return Wrapper(
        child: Wrapper(
          child: StatefulWrapper(
            child: returnMiddle ? middle : Container(),
186 187 188 189
          ),
        ),
      );
    }
190 191
    final Widget part1 = Wrapper(
      child: KeyedSubtree(
192
        key: key1,
193
        child: StatefulBuilder(
194 195 196 197
          builder: builder,
        ),
      ),
    );
198 199
    final Widget part2 = Wrapper(
      child: KeyedSubtree(
200
        key: key2,
201
        child: StatefulBuilder(
202 203 204 205 206 207 208 209
          builder: builder,
        ),
      ),
    );

    middle = part2;
    await tester.pumpWidget(part1);

210
    for (StatefulWrapperState state in tester.stateList<StatefulWrapperState>(find.byType(StatefulWrapper))) {
211 212 213 214 215 216 217 218 219 220 221 222
      expect(state.built, isNotNull);
      state.oldBuilt = state.built;
      state.trigger();
    }
    for (StateSetter setState in setStates)
      setState(() { });

    StatefulWrapperState.buildId = 0;
    middle = part1;
    didMiddle = false;
    await tester.pumpWidget(part2);

223
    for (StatefulWrapperState state in tester.stateList<StatefulWrapperState>(find.byType(StatefulWrapper))) {
224 225 226 227 228
      expect(state.built, isNotNull);
      expect(state.built, isNot(equals(state.oldBuilt)));
    }

  });
229
}