page_transitions_test.dart 12.1 KB
Newer Older
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 7
import 'package:flutter/material.dart';

8 9
class TestOverlayRoute extends OverlayRoute<void> {
  TestOverlayRoute({ RouteSettings settings }) : super(settings: settings);
10
  @override
11
  Iterable<OverlayEntry> createOverlayEntries() sync* {
12
    yield OverlayEntry(builder: _build);
13
  }
14
  Widget _build(BuildContext context) => const Text('Overlay');
15 16
}

17
class PersistentBottomSheetTest extends StatefulWidget {
18
  const PersistentBottomSheetTest({ Key key }) : super(key: key);
19 20

  @override
21
  PersistentBottomSheetTestState createState() => PersistentBottomSheetTestState();
22 23 24
}

class PersistentBottomSheetTestState extends State<PersistentBottomSheetTest> {
25
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
26 27 28 29

  bool setStateCalled = false;

  void showBottomSheet() {
30
    _scaffoldKey.currentState.showBottomSheet<void>((BuildContext context) {
31
      return const Text('bottomSheet');
32
    })
33
    .closed.whenComplete(() {
34 35 36 37 38 39 40 41
      setState(() {
        setStateCalled = true;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
42
    return Scaffold(
43
      key: _scaffoldKey,
44
      body: const Text('Sheet'),
45 46 47 48
    );
  }
}

49
void main() {
50
  testWidgets('Check onstage/offstage handling around transitions', (WidgetTester tester) async {
51 52
    final GlobalKey containerKey1 = GlobalKey();
    final GlobalKey containerKey2 = GlobalKey();
53
    final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
54 55
      '/': (_) => Container(key: containerKey1, child: const Text('Home')),
      '/settings': (_) => Container(key: containerKey2, child: const Text('Settings')),
56
    };
57

58
    await tester.pumpWidget(MaterialApp(routes: routes));
59

60
    expect(find.text('Home'), isOnstage);
61 62
    expect(find.text('Settings'), findsNothing);
    expect(find.text('Overlay'), findsNothing);
63

64 65 66
    expect(Navigator.canPop(containerKey1.currentContext), isFalse);
    Navigator.pushNamed(containerKey1.currentContext, '/settings');
    expect(Navigator.canPop(containerKey1.currentContext), isTrue);
67

68
    await tester.pump();
69

70 71
    expect(find.text('Home'), isOnstage);
    expect(find.text('Settings', skipOffstage: false), isOffstage);
72
    expect(find.text('Overlay'), findsNothing);
73

74
    await tester.pump(const Duration(milliseconds: 16));
75

76 77
    expect(find.text('Home'), isOnstage);
    expect(find.text('Settings'), isOnstage);
78
    expect(find.text('Overlay'), findsNothing);
79

80
    await tester.pump(const Duration(seconds: 1));
81

82
    expect(find.text('Home'), findsNothing);
83
    expect(find.text('Settings'), isOnstage);
84
    expect(find.text('Overlay'), findsNothing);
85

86
    Navigator.push(containerKey2.currentContext, TestOverlayRoute());
87

88
    await tester.pump();
89

90
    expect(find.text('Home'), findsNothing);
91 92
    expect(find.text('Settings'), isOnstage);
    expect(find.text('Overlay'), isOnstage);
93

94
    await tester.pump(const Duration(seconds: 1));
95

96
    expect(find.text('Home'), findsNothing);
97 98
    expect(find.text('Settings'), isOnstage);
    expect(find.text('Overlay'), isOnstage);
99

100 101
    expect(Navigator.canPop(containerKey2.currentContext), isTrue);
    Navigator.pop(containerKey2.currentContext);
102
    await tester.pump();
103

104
    expect(find.text('Home'), findsNothing);
105
    expect(find.text('Settings'), isOnstage);
106
    expect(find.text('Overlay'), findsNothing);
107

108
    await tester.pump(const Duration(seconds: 1));
109

110
    expect(find.text('Home'), findsNothing);
111
    expect(find.text('Settings'), isOnstage);
112
    expect(find.text('Overlay'), findsNothing);
113

114 115
    expect(Navigator.canPop(containerKey2.currentContext), isTrue);
    Navigator.pop(containerKey2.currentContext);
116
    await tester.pump();
Hans Muller's avatar
Hans Muller committed
117
    await tester.pump();
118

119 120
    expect(find.text('Home'), isOnstage);
    expect(find.text('Settings'), isOnstage);
121
    expect(find.text('Overlay'), findsNothing);
122

123
    await tester.pump(const Duration(seconds: 1));
124

125
    expect(find.text('Home'), isOnstage);
126 127
    expect(find.text('Settings'), findsNothing);
    expect(find.text('Overlay'), findsNothing);
128

129
    expect(Navigator.canPop(containerKey1.currentContext), isFalse);
130 131
  });

132
  testWidgets('Check back gesture disables Heroes', (WidgetTester tester) async {
133 134
    final GlobalKey containerKey1 = GlobalKey();
    final GlobalKey containerKey2 = GlobalKey();
135 136
    const String kHeroTag = 'hero';
    final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
137
      '/': (_) => Scaffold(
138
        key: containerKey1,
139
        body: Container(
140
          color: const Color(0xff00ffff),
141
          child: const Hero(
142
            tag: kHeroTag,
143 144 145
            child: Text('Home'),
          ),
        ),
146
      ),
147
      '/settings': (_) => Scaffold(
148
        key: containerKey2,
149
        body: Container(
150
          padding: const EdgeInsets.all(100.0),
151
          color: const Color(0xffff00ff),
152
          child: const Hero(
153
            tag: kHeroTag,
154 155 156
            child: Text('Settings'),
          ),
        ),
157 158 159
      ),
    };

160
    await tester.pumpWidget(MaterialApp(
161
      routes: routes,
162
      theme: ThemeData(platform: TargetPlatform.iOS),
163 164 165 166 167 168 169 170 171 172
    ));

    Navigator.pushNamed(containerKey1.currentContext, '/settings');

    await tester.pump();
    await tester.pump(const Duration(milliseconds: 16));

    expect(find.text('Settings'), isOnstage);

    // Settings text is heroing to its new location
173 174 175 176 177
    Offset settingsOffset = tester.getTopLeft(find.text('Settings'));
    expect(settingsOffset.dx, greaterThan(0.0));
    expect(settingsOffset.dx, lessThan(100.0));
    expect(settingsOffset.dy, greaterThan(0.0));
    expect(settingsOffset.dy, lessThan(100.0));
178 179 180 181 182 183 184

    await tester.pump(const Duration(seconds: 1));

    expect(find.text('Home'), findsNothing);
    expect(find.text('Settings'), isOnstage);

    // Drag from left edge to invoke the gesture.
185
    final TestGesture gesture = await tester.startGesture(const Offset(5.0, 100.0));
186
    await gesture.moveBy(const Offset(50.0, 0.0));
187 188 189 190 191 192 193
    await tester.pump();

    // Home is now visible.
    expect(find.text('Home'), isOnstage);
    expect(find.text('Settings'), isOnstage);

    // Home page is sliding in from the left, no heroes.
194 195 196
    final Offset homeOffset = tester.getTopLeft(find.text('Home'));
    expect(homeOffset.dx, lessThan(0.0));
    expect(homeOffset.dy, 0.0);
197 198 199

    // Settings page is sliding off to the right, no heroes.
    settingsOffset = tester.getTopLeft(find.text('Settings'));
200 201
    expect(settingsOffset.dx, greaterThan(100.0));
    expect(settingsOffset.dy, 100.0);
202
  });
203

204
  testWidgets('Check back gesture doesn\'t start during transitions', (WidgetTester tester) async {
205 206
    final GlobalKey containerKey1 = GlobalKey();
    final GlobalKey containerKey2 = GlobalKey();
207
    final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
208 209
      '/': (_) => Scaffold(key: containerKey1, body: const Text('Home')),
      '/settings': (_) => Scaffold(key: containerKey2, body: const Text('Settings')),
210 211
    };

212
    await tester.pumpWidget(MaterialApp(
213
      routes: routes,
214
      theme: ThemeData(platform: TargetPlatform.iOS),
215 216 217 218 219 220 221 222 223 224 225 226 227
    ));

    Navigator.pushNamed(containerKey1.currentContext, '/settings');

    await tester.pump();
    await tester.pump(const Duration(milliseconds: 100));

    // We are mid-transition, both pages are on stage.
    expect(find.text('Home'), isOnstage);
    expect(find.text('Settings'), isOnstage);

    // Drag from left edge to invoke the gesture. (near bottom so we grab
    // the Settings page as it comes up).
228
    TestGesture gesture = await tester.startGesture(const Offset(5.0, 550.0));
229
    await gesture.moveBy(const Offset(500.0, 0.0));
230 231 232 233 234 235 236 237 238 239
    await gesture.up();
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 1000));

    // The original forward navigation should have completed, instead of the
    // back gesture, since we were mid transition.
    expect(find.text('Home'), findsNothing);
    expect(find.text('Settings'), isOnstage);

    // Try again now that we're settled.
240
    gesture = await tester.startGesture(const Offset(5.0, 550.0));
241
    await gesture.moveBy(const Offset(500.0, 0.0));
242 243 244 245 246 247 248
    await gesture.up();
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 1000));

    expect(find.text('Home'), isOnstage);
    expect(find.text('Settings'), findsNothing);
  });
249 250 251

  // Tests bug https://github.com/flutter/flutter/issues/6451
  testWidgets('Check back gesture with a persistent bottom sheet showing', (WidgetTester tester) async {
252 253
    final GlobalKey containerKey1 = GlobalKey();
    final GlobalKey containerKey2 = GlobalKey();
254
    final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
255 256
      '/': (_) => Scaffold(key: containerKey1, body: const Text('Home')),
      '/sheet': (_) => PersistentBottomSheetTest(key: containerKey2),
257 258
    };

259
    await tester.pumpWidget(MaterialApp(
260
      routes: routes,
261
      theme: ThemeData(platform: TargetPlatform.iOS),
262 263 264 265 266 267 268 269 270 271
    ));

    Navigator.pushNamed(containerKey1.currentContext, '/sheet');

    await tester.pump();
    await tester.pump(const Duration(seconds: 1));

    expect(find.text('Home'), findsNothing);
    expect(find.text('Sheet'), isOnstage);

272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
    // Drag from left edge to invoke the gesture. We should go back.
    TestGesture gesture = await tester.startGesture(const Offset(5.0, 100.0));
    await gesture.moveBy(const Offset(500.0, 0.0));
    await gesture.up();
    await tester.pump();
    await tester.pump(const Duration(seconds: 1));

    Navigator.pushNamed(containerKey1.currentContext, '/sheet');

    await tester.pump();
    await tester.pump(const Duration(seconds: 1));

    expect(find.text('Home'), findsNothing);
    expect(find.text('Sheet'), isOnstage);

287
    // Show the bottom sheet.
288
    final PersistentBottomSheetTestState sheet = containerKey2.currentState;
289 290 291 292
    sheet.showBottomSheet();

    await tester.pump(const Duration(seconds: 1));

293 294
    // Drag from left edge to invoke the gesture. Nothing should happen.
    gesture = await tester.startGesture(const Offset(5.0, 100.0));
295
    await gesture.moveBy(const Offset(500.0, 0.0));
296 297 298 299
    await gesture.up();
    await tester.pump();
    await tester.pump(const Duration(seconds: 1));

300 301
    expect(find.text('Home'), findsNothing);
    expect(find.text('Sheet'), isOnstage);
302

303 304
    // Sheet did not call setState (since the gesture did nothing).
    expect(sheet.setStateCalled, isFalse);
305
  });
306 307 308

  testWidgets('Test completed future', (WidgetTester tester) async {
    final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
309 310
      '/': (_) => const Center(child: Text('home')),
      '/next': (_) => const Center(child: Text('next')),
311 312
    };

313
    await tester.pumpWidget(MaterialApp(routes: routes));
314

315
    final PageRoute<void> route = MaterialPageRoute<void>(
316
      settings: const RouteSettings(name: '/page'),
317
      builder: (BuildContext context) => const Center(child: Text('page')),
318 319 320
    );

    int popCount = 0;
321 322
    route.popped.whenComplete(() {
      popCount += 1;
323 324 325
    });

    int completeCount = 0;
326 327
    route.completed.whenComplete(() {
      completeCount += 1;
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
    });

    expect(popCount, 0);
    expect(completeCount, 0);

    Navigator.push(tester.element(find.text('home')), route);

    expect(popCount, 0);
    expect(completeCount, 0);

    await tester.pump();

    expect(popCount, 0);
    expect(completeCount, 0);

    await tester.pump(const Duration(milliseconds: 100));

    expect(popCount, 0);
    expect(completeCount, 0);

    await tester.pump(const Duration(milliseconds: 100));

    expect(popCount, 0);
    expect(completeCount, 0);

    await tester.pump(const Duration(seconds: 1));

    expect(popCount, 0);
    expect(completeCount, 0);

    Navigator.pop(tester.element(find.text('page')));

    expect(popCount, 0);
    expect(completeCount, 0);

    await tester.pump();

    expect(popCount, 1);
    expect(completeCount, 0);

    await tester.pump(const Duration(milliseconds: 100));

    expect(popCount, 1);
    expect(completeCount, 0);

    await tester.pump(const Duration(milliseconds: 100));

    expect(popCount, 1);
    expect(completeCount, 0);

    await tester.pump(const Duration(seconds: 1));

    expect(popCount, 1);
    expect(completeCount, 1);
  });
383
}