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

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

8
class StateMarker extends StatefulWidget {
9
  const StateMarker({ super.key, this.child });
10

11
  final Widget? child;
12

13
  @override
14
  StateMarkerState createState() => StateMarkerState();
15 16 17
}

class StateMarkerState extends State<StateMarker> {
18
  String? marker;
19

20
  @override
21
  Widget build(BuildContext context) {
22
    if (widget.child != null) {
23
      return widget.child!;
24
    }
25
    return Container();
26 27 28
  }
}

29
class DeactivateLogger extends StatefulWidget {
30
  const DeactivateLogger({ required Key key, required this.log }) : super(key: key);
31 32 33 34

  final List<String> log;

  @override
35
  DeactivateLoggerState createState() => DeactivateLoggerState();
36 37 38 39 40
}

class DeactivateLoggerState extends State<DeactivateLogger> {
  @override
  void deactivate() {
41
    widget.log.add('deactivate');
42 43 44 45 46
    super.deactivate();
  }

  @override
  Widget build(BuildContext context) {
47
    widget.log.add('build');
48
    return Container();
49 50 51
  }
}

52
void main() {
53
  testWidgets('can reparent state', (WidgetTester tester) async {
54 55
    final GlobalKey left = GlobalKey();
    final GlobalKey right = GlobalKey();
56

57
    const StateMarker grandchild = StateMarker();
58
    await tester.pumpWidget(
59
      Stack(
60
        textDirection: TextDirection.ltr,
61
        children: <Widget>[
62
          ColoredBox(
63
            color: Colors.green,
64
            child: StateMarker(key: left),
65
          ),
66
          ColoredBox(
67
            color: Colors.green,
68
            child: StateMarker(
69
              key: right,
70 71
              child: grandchild,
            ),
72
          ),
73
        ],
74
      ),
75
    );
76

77
    final StateMarkerState leftState = left.currentState! as StateMarkerState;
Ian Hickson's avatar
Ian Hickson committed
78
    leftState.marker = 'left';
79
    final StateMarkerState rightState = right.currentState! as StateMarkerState;
Ian Hickson's avatar
Ian Hickson committed
80
    rightState.marker = 'right';
81

82
    final StateMarkerState grandchildState = tester.state(find.byWidget(grandchild));
83
    expect(grandchildState, isNotNull);
Ian Hickson's avatar
Ian Hickson committed
84
    grandchildState.marker = 'grandchild';
85

86
    const StateMarker newGrandchild = StateMarker();
87
    await tester.pumpWidget(
88
      Stack(
89
        textDirection: TextDirection.ltr,
90
        children: <Widget>[
91
          ColoredBox(
92
            color: Colors.green,
93
            child: StateMarker(
94
              key: right,
95 96
              child: newGrandchild,
            ),
97
          ),
98
          ColoredBox(
99
            color: Colors.green,
100
            child: StateMarker(key: left),
101
          ),
102
        ],
103
      ),
104 105 106
    );

    expect(left.currentState, equals(leftState));
Ian Hickson's avatar
Ian Hickson committed
107
    expect(leftState.marker, equals('left'));
108
    expect(right.currentState, equals(rightState));
Ian Hickson's avatar
Ian Hickson committed
109
    expect(rightState.marker, equals('right'));
110

111
    final StateMarkerState newGrandchildState = tester.state(find.byWidget(newGrandchild));
112 113
    expect(newGrandchildState, isNotNull);
    expect(newGrandchildState, equals(grandchildState));
Ian Hickson's avatar
Ian Hickson committed
114
    expect(newGrandchildState.marker, equals('grandchild'));
115

116
    await tester.pumpWidget(
117
      Center(
118
        child: ColoredBox(
119
          color: Colors.green,
120
          child: StateMarker(
121
            key: left,
122 123 124
            child: Container(),
          ),
        ),
125
      ),
126
    );
127

128
    expect(left.currentState, equals(leftState));
Ian Hickson's avatar
Ian Hickson committed
129
    expect(leftState.marker, equals('left'));
130
    expect(right.currentState, isNull);
131 132
  });

133
  testWidgets('can reparent state with multichild widgets', (WidgetTester tester) async {
134 135
    final GlobalKey left = GlobalKey();
    final GlobalKey right = GlobalKey();
136

137
    const StateMarker grandchild = StateMarker();
138
    await tester.pumpWidget(
139
      Stack(
140
        textDirection: TextDirection.ltr,
141
        children: <Widget>[
142 143
          StateMarker(key: left),
          StateMarker(
144
            key: right,
145 146 147
            child: grandchild,
          ),
        ],
148
      ),
149
    );
150

151
    final StateMarkerState leftState = left.currentState! as StateMarkerState;
Ian Hickson's avatar
Ian Hickson committed
152
    leftState.marker = 'left';
153
    final StateMarkerState rightState = right.currentState! as StateMarkerState;
Ian Hickson's avatar
Ian Hickson committed
154
    rightState.marker = 'right';
155

156
    final StateMarkerState grandchildState = tester.state(find.byWidget(grandchild));
157
    expect(grandchildState, isNotNull);
Ian Hickson's avatar
Ian Hickson committed
158
    grandchildState.marker = 'grandchild';
159

160
    const StateMarker newGrandchild = StateMarker();
161
    await tester.pumpWidget(
162
      Stack(
163
        textDirection: TextDirection.ltr,
164
        children: <Widget>[
165
          StateMarker(
166
            key: right,
167
            child: newGrandchild,
168
          ),
169 170
          StateMarker(key: left),
        ],
171
      ),
172 173 174
    );

    expect(left.currentState, equals(leftState));
Ian Hickson's avatar
Ian Hickson committed
175
    expect(leftState.marker, equals('left'));
176
    expect(right.currentState, equals(rightState));
Ian Hickson's avatar
Ian Hickson committed
177
    expect(rightState.marker, equals('right'));
178

179
    final StateMarkerState newGrandchildState = tester.state(find.byWidget(newGrandchild));
180 181
    expect(newGrandchildState, isNotNull);
    expect(newGrandchildState, equals(grandchildState));
Ian Hickson's avatar
Ian Hickson committed
182
    expect(newGrandchildState.marker, equals('grandchild'));
183

184
    await tester.pumpWidget(
185
      Center(
186
        child: ColoredBox(
187
          color: Colors.green,
188
          child: StateMarker(
189
            key: left,
190 191 192
            child: Container(),
          ),
        ),
193
      ),
194
    );
195

196
    expect(left.currentState, equals(leftState));
Ian Hickson's avatar
Ian Hickson committed
197
    expect(leftState.marker, equals('left'));
198
    expect(right.currentState, isNull);
199 200
  });

201
  testWidgets('can with scrollable list', (WidgetTester tester) async {
202
    final GlobalKey key = GlobalKey();
203

204
    await tester.pumpWidget(StateMarker(key: key));
205

206
    final StateMarkerState keyState = key.currentState! as StateMarkerState;
Ian Hickson's avatar
Ian Hickson committed
207
    keyState.marker = 'marked';
208

209
    await tester.pumpWidget(
210
      Directionality(
211
        textDirection: TextDirection.ltr,
212
        child: ListView(
213 214
          itemExtent: 100.0,
          children: <Widget>[
215
            SizedBox(
216 217
              key: const Key('container'),
              height: 100.0,
218
              child: StateMarker(key: key),
219 220
            ),
          ],
221
        ),
222 223
      ),
    );
224

225
    expect(key.currentState, equals(keyState));
Ian Hickson's avatar
Ian Hickson committed
226
    expect(keyState.marker, equals('marked'));
227

228
    await tester.pumpWidget(StateMarker(key: key));
229

230
    expect(key.currentState, equals(keyState));
Ian Hickson's avatar
Ian Hickson committed
231
    expect(keyState.marker, equals('marked'));
232
  });
233

234
  testWidgets('Reparent during update children', (WidgetTester tester) async {
235
    final GlobalKey key = GlobalKey();
236

237
    await tester.pumpWidget(Stack(
238
      textDirection: TextDirection.ltr,
239
      children: <Widget>[
240
        StateMarker(key: key),
241
        const SizedBox(width: 100.0, height: 100.0),
242
      ],
243 244
    ));

245
    final StateMarkerState keyState = key.currentState!as StateMarkerState;
Ian Hickson's avatar
Ian Hickson committed
246
    keyState.marker = 'marked';
247

248
    await tester.pumpWidget(Stack(
249
      textDirection: TextDirection.ltr,
250
      children: <Widget>[
251
        const SizedBox(width: 100.0, height: 100.0),
252
        StateMarker(key: key),
253
      ],
254 255 256
    ));

    expect(key.currentState, equals(keyState));
Ian Hickson's avatar
Ian Hickson committed
257
    expect(keyState.marker, equals('marked'));
258

259
    await tester.pumpWidget(Stack(
260
      textDirection: TextDirection.ltr,
261
      children: <Widget>[
262
        StateMarker(key: key),
263
        const SizedBox(width: 100.0, height: 100.0),
264
      ],
265 266 267
    ));

    expect(key.currentState, equals(keyState));
Ian Hickson's avatar
Ian Hickson committed
268
    expect(keyState.marker, equals('marked'));
269
  });
270

271
  testWidgets('Reparent to child during update children', (WidgetTester tester) async {
272
    final GlobalKey key = GlobalKey();
273

274
    await tester.pumpWidget(Stack(
275
      textDirection: TextDirection.ltr,
276
      children: <Widget>[
277
        const SizedBox(width: 100.0, height: 100.0),
278
        StateMarker(key: key),
279
        const SizedBox(width: 100.0, height: 100.0),
280
      ],
281 282
    ));

283
    final StateMarkerState keyState = key.currentState! as StateMarkerState;
Ian Hickson's avatar
Ian Hickson committed
284
    keyState.marker = 'marked';
285

286
    await tester.pumpWidget(Stack(
287
      textDirection: TextDirection.ltr,
288
      children: <Widget>[
289
        SizedBox(width: 100.0, height: 100.0, child: StateMarker(key: key)),
290
        const SizedBox(width: 100.0, height: 100.0),
291
      ],
292 293 294
    ));

    expect(key.currentState, equals(keyState));
Ian Hickson's avatar
Ian Hickson committed
295
    expect(keyState.marker, equals('marked'));
296

297
    await tester.pumpWidget(Stack(
298
      textDirection: TextDirection.ltr,
299
      children: <Widget>[
300
        const SizedBox(width: 100.0, height: 100.0),
301
        StateMarker(key: key),
302
        const SizedBox(width: 100.0, height: 100.0),
303
      ],
304 305 306
    ));

    expect(key.currentState, equals(keyState));
Ian Hickson's avatar
Ian Hickson committed
307
    expect(keyState.marker, equals('marked'));
308

309
    await tester.pumpWidget(Stack(
310
      textDirection: TextDirection.ltr,
311
      children: <Widget>[
312
        const SizedBox(width: 100.0, height: 100.0),
313
        SizedBox(width: 100.0, height: 100.0, child: StateMarker(key: key)),
314
      ],
315 316 317
    ));

    expect(key.currentState, equals(keyState));
Ian Hickson's avatar
Ian Hickson committed
318
    expect(keyState.marker, equals('marked'));
319

320
    await tester.pumpWidget(Stack(
321
      textDirection: TextDirection.ltr,
322
      children: <Widget>[
323
        const SizedBox(width: 100.0, height: 100.0),
324
        StateMarker(key: key),
325
        const SizedBox(width: 100.0, height: 100.0),
326
      ],
327 328 329
    ));

    expect(key.currentState, equals(keyState));
Ian Hickson's avatar
Ian Hickson committed
330
    expect(keyState.marker, equals('marked'));
331
  });
332

333
  testWidgets('Deactivate implies build', (WidgetTester tester) async {
334
    final GlobalKey key = GlobalKey();
335
    final List<String> log = <String>[];
336
    final DeactivateLogger logger = DeactivateLogger(key: key, log: log);
337

338
    await tester.pumpWidget(
339
      Container(key: UniqueKey(), child: logger),
340
    );
341

342
    expect(log, equals(<String>['build']));
343

344
    await tester.pumpWidget(
345
      Container(key: UniqueKey(), child: logger),
346
    );
347

348
    expect(log, equals(<String>['build', 'deactivate', 'build']));
349
    log.clear();
350

351
    await tester.pump();
352
    expect(log, isEmpty);
353
  });
354 355

  testWidgets('Reparenting with multiple moves', (WidgetTester tester) async {
356 357 358
    final GlobalKey key1 = GlobalKey();
    final GlobalKey key2 = GlobalKey();
    final GlobalKey key3 = GlobalKey();
359 360

    await tester.pumpWidget(
361
      Row(
362
        textDirection: TextDirection.ltr,
363
        children: <Widget>[
364
          StateMarker(
365
            key: key1,
366
            child: StateMarker(
367
              key: key2,
368
              child: StateMarker(
369
                key: key3,
370 371 372 373 374
                child: StateMarker(child: Container(width: 100.0)),
              ),
            ),
          ),
        ],
375
      ),
376 377 378
    );

    await tester.pumpWidget(
379
      Row(
380
        textDirection: TextDirection.ltr,
381
        children: <Widget>[
382
          StateMarker(
383
            key: key2,
384
            child: StateMarker(child: Container(width: 100.0)),
385
          ),
386
          StateMarker(
387
            key: key1,
388
            child: StateMarker(
389
              key: key3,
390 391
              child: StateMarker(child: Container(width: 100.0)),
            ),
392
          ),
393
        ],
394
      ),
395 396
    );
  });
397
}