page_test.dart 16.5 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 6
// @dart = 2.8

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

void main() {
Ian Hickson's avatar
Ian Hickson committed
11
  testWidgets('test iOS page transition (LTR)', (WidgetTester tester) async {
12
    await tester.pumpWidget(
13
      CupertinoApp(
14
        onGenerateRoute: (RouteSettings settings) {
15
          return CupertinoPageRoute<void>(
16 17
            settings: settings,
            builder: (BuildContext context) {
Ian Hickson's avatar
Ian Hickson committed
18
              final String pageNumber = settings.name == '/' ? '1' : '2';
19
              return Center(child: Text('Page $pageNumber'));
20
            },
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
          );
        },
      ),
    );

    final Offset widget1InitialTopLeft = tester.getTopLeft(find.text('Page 1'));

    tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/next');

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

    Offset widget1TransientTopLeft = tester.getTopLeft(find.text('Page 1'));
    Offset widget2TopLeft = tester.getTopLeft(find.text('Page 2'));

    // Page 1 is moving to the left.
37
    expect(widget1TransientTopLeft.dx, lessThan(widget1InitialTopLeft.dx));
38
    // Page 1 isn't moving vertically.
39
    expect(widget1TransientTopLeft.dy, equals(widget1InitialTopLeft.dy));
40
    // iOS transition is horizontal only.
41
    expect(widget1InitialTopLeft.dy, equals(widget2TopLeft.dy));
42
    // Page 2 is coming in from the right.
43
    expect(widget2TopLeft.dx, greaterThan(widget1InitialTopLeft.dx));
44

45 46 47
    // Will need to be changed if the animation curve or duration changes.
    expect(widget1TransientTopLeft.dx, closeTo(130, 1.0));

48 49 50 51 52 53 54 55 56 57 58 59 60 61
    await tester.pumpAndSettle();

    // Page 2 covers page 1.
    expect(find.text('Page 1'), findsNothing);
    expect(find.text('Page 2'), isOnstage);

    tester.state<NavigatorState>(find.byType(Navigator)).pop();
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 100));

    widget1TransientTopLeft = tester.getTopLeft(find.text('Page 1'));
    widget2TopLeft = tester.getTopLeft(find.text('Page 2'));

    // Page 1 is coming back from the left.
62
    expect(widget1TransientTopLeft.dx, lessThan(widget1InitialTopLeft.dx));
63
    // Page 1 isn't moving vertically.
64
    expect(widget1TransientTopLeft.dy, equals(widget1InitialTopLeft.dy));
65
    // iOS transition is horizontal only.
66
    expect(widget1InitialTopLeft.dy, equals(widget2TopLeft.dy));
67
    // Page 2 is leaving towards the right.
68
    expect(widget2TopLeft.dx, greaterThan(widget1InitialTopLeft.dx));
69

70 71 72
    // Will need to be changed if the animation curve or duration changes.
    expect(widget1TransientTopLeft.dx, closeTo(249, 1.0));

73 74 75 76 77 78 79 80
    await tester.pumpAndSettle();

    expect(find.text('Page 1'), isOnstage);
    expect(find.text('Page 2'), findsNothing);

    widget1TransientTopLeft = tester.getTopLeft(find.text('Page 1'));

    // Page 1 is back where it started.
81
    expect(widget1InitialTopLeft, equals(widget1TransientTopLeft));
82 83
  });

Ian Hickson's avatar
Ian Hickson committed
84 85
  testWidgets('test iOS page transition (RTL)', (WidgetTester tester) async {
    await tester.pumpWidget(
86
      CupertinoApp(
87
        localizationsDelegates: const <LocalizationsDelegate<dynamic>>[
88
          RtlOverrideWidgetsDelegate(),
Ian Hickson's avatar
Ian Hickson committed
89 90
        ],
        onGenerateRoute: (RouteSettings settings) {
91
          return CupertinoPageRoute<void>(
Ian Hickson's avatar
Ian Hickson committed
92 93 94
            settings: settings,
            builder: (BuildContext context) {
              final String pageNumber = settings.name == '/' ? '1' : '2';
95
              return Center(child: Text('Page $pageNumber'));
96
            },
Ian Hickson's avatar
Ian Hickson committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
          );
        },
      ),
    );
    await tester.pump(); // to load the localization, since it doesn't use a synchronous future

    final Offset widget1InitialTopLeft = tester.getTopLeft(find.text('Page 1'));

    tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/next');

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

    Offset widget1TransientTopLeft = tester.getTopLeft(find.text('Page 1'));
    Offset widget2TopLeft = tester.getTopLeft(find.text('Page 2'));

    // Page 1 is moving to the right.
    expect(widget1TransientTopLeft.dx, greaterThan(widget1InitialTopLeft.dx));
    // Page 1 isn't moving vertically.
    expect(widget1TransientTopLeft.dy, equals(widget1InitialTopLeft.dy));
    // iOS transition is horizontal only.
    expect(widget1InitialTopLeft.dy, equals(widget2TopLeft.dy));
    // Page 2 is coming in from the left.
    expect(widget2TopLeft.dx, lessThan(widget1InitialTopLeft.dx));

    await tester.pumpAndSettle();

    // Page 2 covers page 1.
    expect(find.text('Page 1'), findsNothing);
    expect(find.text('Page 2'), isOnstage);

    tester.state<NavigatorState>(find.byType(Navigator)).pop();
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 100));

    widget1TransientTopLeft = tester.getTopLeft(find.text('Page 1'));
    widget2TopLeft = tester.getTopLeft(find.text('Page 2'));

    // Page 1 is coming back from the right.
    expect(widget1TransientTopLeft.dx, greaterThan(widget1InitialTopLeft.dx));
    // Page 1 isn't moving vertically.
    expect(widget1TransientTopLeft.dy, equals(widget1InitialTopLeft.dy));
    // iOS transition is horizontal only.
    expect(widget1InitialTopLeft.dy, equals(widget2TopLeft.dy));
    // Page 2 is leaving towards the left.
    expect(widget2TopLeft.dx, lessThan(widget1InitialTopLeft.dx));

    await tester.pumpAndSettle();

    expect(find.text('Page 1'), isOnstage);
    expect(find.text('Page 2'), findsNothing);

    widget1TransientTopLeft = tester.getTopLeft(find.text('Page 1'));

    // Page 1 is back where it started.
    expect(widget1InitialTopLeft, equals(widget1TransientTopLeft));
  });

155 156
  testWidgets('test iOS fullscreen dialog transition', (WidgetTester tester) async {
    await tester.pumpWidget(
157 158
      const CupertinoApp(
        home: Center(child: Text('Page 1')),
159 160 161 162 163
      ),
    );

    final Offset widget1InitialTopLeft = tester.getTopLeft(find.text('Page 1'));

164
    tester.state<NavigatorState>(find.byType(Navigator)).push(CupertinoPageRoute<void>(
165
      builder: (BuildContext context) {
166
        return const Center(child: Text('Page 2'));
167 168 169 170 171 172 173 174 175 176 177
      },
      fullscreenDialog: true,
    ));

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

    Offset widget1TransientTopLeft = tester.getTopLeft(find.text('Page 1'));
    Offset widget2TopLeft = tester.getTopLeft(find.text('Page 2'));

    // Page 1 doesn't move.
178
    expect(widget1TransientTopLeft, equals(widget1InitialTopLeft));
179
    // Fullscreen dialogs transitions vertically only.
180
    expect(widget1InitialTopLeft.dx, equals(widget2TopLeft.dx));
181
    // Page 2 is coming in from the bottom.
182
    expect(widget2TopLeft.dy, greaterThan(widget1InitialTopLeft.dy));
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197

    await tester.pumpAndSettle();

    // Page 2 covers page 1.
    expect(find.text('Page 1'), findsNothing);
    expect(find.text('Page 2'), isOnstage);

    tester.state<NavigatorState>(find.byType(Navigator)).pop();
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 100));

    widget1TransientTopLeft = tester.getTopLeft(find.text('Page 1'));
    widget2TopLeft = tester.getTopLeft(find.text('Page 2'));

    // Page 1 doesn't move.
198
    expect(widget1TransientTopLeft, equals(widget1InitialTopLeft));
199
    // Fullscreen dialogs transitions vertically only.
200
    expect(widget1InitialTopLeft.dx, equals(widget2TopLeft.dx));
201
    // Page 2 is leaving towards the bottom.
202
    expect(widget2TopLeft.dy, greaterThan(widget1InitialTopLeft.dy));
203 204 205 206 207 208 209 210 211

    await tester.pumpAndSettle();

    expect(find.text('Page 1'), isOnstage);
    expect(find.text('Page 2'), findsNothing);

    widget1TransientTopLeft = tester.getTopLeft(find.text('Page 1'));

    // Page 1 is back where it started.
212
    expect(widget1InitialTopLeft, equals(widget1TransientTopLeft));
213
  });
214

Ian Hickson's avatar
Ian Hickson committed
215
  testWidgets('test only edge swipes work (LTR)', (WidgetTester tester) async {
216
    await tester.pumpWidget(
217
      CupertinoApp(
218
        onGenerateRoute: (RouteSettings settings) {
219
          return CupertinoPageRoute<void>(
220 221
            settings: settings,
            builder: (BuildContext context) {
Ian Hickson's avatar
Ian Hickson committed
222
              final String pageNumber = settings.name == '/' ? '1' : '2';
223
              return Center(child: Text('Page $pageNumber'));
224
            },
225 226 227 228 229 230 231 232
          );
        },
      ),
    );

    tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/next');

    await tester.pump();
233
    await tester.pump(const Duration(seconds: 1));
234 235 236 237 238 239 240 241 242 243 244 245 246 247

    // Page 2 covers page 1.
    expect(find.text('Page 1'), findsNothing);
    expect(find.text('Page 2'), isOnstage);

    // Drag from the middle to the right.
    TestGesture gesture = await tester.startGesture(const Offset(200.0, 200.0));
    await gesture.moveBy(const Offset(300.0, 0.0));
    await tester.pump();

    // Nothing should happen.
    expect(find.text('Page 1'), findsNothing);
    expect(find.text('Page 2'), isOnstage);

Ian Hickson's avatar
Ian Hickson committed
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
    // Drag from the right to the left.
    gesture = await tester.startGesture(const Offset(795.0, 200.0));
    await gesture.moveBy(const Offset(-300.0, 0.0));
    await tester.pump();

    // Nothing should happen.
    expect(find.text('Page 1'), findsNothing);
    expect(find.text('Page 2'), isOnstage);

    // Drag from the right to the further right.
    gesture = await tester.startGesture(const Offset(795.0, 200.0));
    await gesture.moveBy(const Offset(300.0, 0.0));
    await tester.pump();

    // Nothing should happen.
    expect(find.text('Page 1'), findsNothing);
    expect(find.text('Page 2'), isOnstage);

    // Now drag from the left edge.
    gesture = await tester.startGesture(const Offset(5.0, 200.0));
    await gesture.moveBy(const Offset(300.0, 0.0));
    await tester.pump();

    // Page 1 is now visible.
    expect(find.text('Page 1'), isOnstage);
    expect(find.text('Page 2'), isOnstage);
  });

276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
  testWidgets('test edge swipes work with media query padding (LTR)', (WidgetTester tester) async {
    await tester.pumpWidget(
      CupertinoApp(
        builder: (BuildContext context, Widget navigator) {
          return MediaQuery(
            data: const MediaQueryData(padding: EdgeInsets.only(left: 40)),
            child: navigator,
          );
        },
        home: const Placeholder(),
      ),
    );

    tester.state<NavigatorState>(find.byType(Navigator)).push(
      CupertinoPageRoute<void>(
        builder: (BuildContext context) => const Center(child: Text('Page 1')),
      ),
    );

    await tester.pump();
296
    await tester.pump(const Duration(seconds: 2));
297 298 299 300 301 302 303

    tester.state<NavigatorState>(find.byType(Navigator)).push(
      CupertinoPageRoute<void>(
        builder: (BuildContext context) => const Center(child: Text('Page 2')),
      ),
    );
    await tester.pump();
304
    await tester.pumpAndSettle();
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342

    expect(find.text('Page 1'), findsNothing);
    expect(find.text('Page 2'), isOnstage);

    // Now drag from the left edge.
    final TestGesture gesture = await tester.startGesture(const Offset(35.0, 200.0));
    await gesture.moveBy(const Offset(300.0, 0.0));
    await tester.pump();
    await tester.pumpAndSettle();

    // Page 1 is now visible.
    expect(find.text('Page 1'), isOnstage);
    expect(find.text('Page 2'), isOnstage);
  });

  testWidgets('test edge swipes work with media query padding (RLT)', (WidgetTester tester) async {
    await tester.pumpWidget(
      CupertinoApp(
        builder: (BuildContext context, Widget navigator) {
          return Directionality(
            textDirection: TextDirection.rtl,
            child: MediaQuery(
              data: const MediaQueryData(padding: EdgeInsets.only(right: 40)),
              child: navigator,
            ),
          );
        },
        home: const Placeholder(),
      ),
    );

    tester.state<NavigatorState>(find.byType(Navigator)).push(
      CupertinoPageRoute<void>(
        builder: (BuildContext context) => const Center(child: Text('Page 1')),
      ),
    );

    await tester.pump();
343
    await tester.pumpAndSettle();
344 345 346 347 348 349 350 351

    tester.state<NavigatorState>(find.byType(Navigator)).push(
      CupertinoPageRoute<void>(
        builder: (BuildContext context) => const Center(child: Text('Page 2')),
      ),
    );

    await tester.pump();
352
    await tester.pumpAndSettle();
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367

    expect(find.text('Page 1'), findsNothing);
    expect(find.text('Page 2'), isOnstage);

    // Now drag from the left edge.
    final TestGesture gesture = await tester.startGesture(const Offset(765.0, 200.0));
    await gesture.moveBy(const Offset(-300.0, 0.0));
    await tester.pump();
    await tester.pumpAndSettle();

    // Page 1 is now visible.
    expect(find.text('Page 1'), isOnstage);
    expect(find.text('Page 2'), isOnstage);
  });

Ian Hickson's avatar
Ian Hickson committed
368 369
  testWidgets('test only edge swipes work (RTL)', (WidgetTester tester) async {
    await tester.pumpWidget(
370
      CupertinoApp(
371
        localizationsDelegates: const <LocalizationsDelegate<dynamic>>[
372
          RtlOverrideWidgetsDelegate(),
Ian Hickson's avatar
Ian Hickson committed
373 374
        ],
        onGenerateRoute: (RouteSettings settings) {
375
          return CupertinoPageRoute<void>(
Ian Hickson's avatar
Ian Hickson committed
376 377 378
            settings: settings,
            builder: (BuildContext context) {
              final String pageNumber = settings.name == '/' ? '1' : '2';
379
              return Center(child: Text('Page $pageNumber'));
380
            },
Ian Hickson's avatar
Ian Hickson committed
381 382 383 384 385 386 387 388 389
          );
        },
      ),
    );
    await tester.pump(); // to load the localization, since it doesn't use a synchronous future

    tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/next');

    await tester.pump();
390
    await tester.pumpAndSettle();
Ian Hickson's avatar
Ian Hickson committed
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405

    // Page 2 covers page 1.
    expect(find.text('Page 1'), findsNothing);
    expect(find.text('Page 2'), isOnstage);

    // Drag from the middle to the left.
    TestGesture gesture = await tester.startGesture(const Offset(200.0, 200.0));
    await gesture.moveBy(const Offset(-300.0, 0.0));
    await tester.pump();

    // Nothing should happen.
    expect(find.text('Page 1'), findsNothing);
    expect(find.text('Page 2'), isOnstage);

    // Drag from the left to the right.
406 407 408 409
    gesture = await tester.startGesture(const Offset(5.0, 200.0));
    await gesture.moveBy(const Offset(300.0, 0.0));
    await tester.pump();

Ian Hickson's avatar
Ian Hickson committed
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
    // Nothing should happen.
    expect(find.text('Page 1'), findsNothing);
    expect(find.text('Page 2'), isOnstage);

    // Drag from the left to the further left.
    gesture = await tester.startGesture(const Offset(5.0, 200.0));
    await gesture.moveBy(const Offset(-300.0, 0.0));
    await tester.pump();

    // Nothing should happen.
    expect(find.text('Page 1'), findsNothing);
    expect(find.text('Page 2'), isOnstage);

    // Now drag from the right edge.
    gesture = await tester.startGesture(const Offset(795.0, 200.0));
    await gesture.moveBy(const Offset(-300.0, 0.0));
    await tester.pump();

428 429 430 431
    // Page 1 is now visible.
    expect(find.text('Page 1'), isOnstage);
    expect(find.text('Page 2'), isOnstage);
  });
432 433 434 435 436 437 438 439 440 441

  testWidgets('test edge swipe then drop back at starting point works', (WidgetTester tester) async {
    await tester.pumpWidget(
      CupertinoApp(
        onGenerateRoute: (RouteSettings settings) {
          return CupertinoPageRoute<void>(
            settings: settings,
            builder: (BuildContext context) {
              final String pageNumber = settings.name == '/' ? '1' : '2';
              return Center(child: Text('Page $pageNumber'));
442
            },
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
          );
        },
      ),
    );

    tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/next');

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

    expect(find.text('Page 1'), findsNothing);
    expect(find.text('Page 2'), isOnstage);

    final TestGesture gesture = await tester.startGesture(const Offset(5, 200));
    await gesture.moveBy(const Offset(300, 0));
    await tester.pump();
    // Bring it exactly back such that there's nothing to animate when releasing.
    await gesture.moveBy(const Offset(-300, 0));
    await gesture.up();
    await tester.pump();

    expect(find.text('Page 1'), findsNothing);
    expect(find.text('Page 2'), isOnstage);
  });
467
}
Ian Hickson's avatar
Ian Hickson committed
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482

class RtlOverrideWidgetsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
  const RtlOverrideWidgetsDelegate();
  @override
  bool isSupported(Locale locale) => true;
  @override
  Future<WidgetsLocalizations> load(Locale locale) async => const RtlOverrideWidgetsLocalization();
  @override
  bool shouldReload(LocalizationsDelegate<WidgetsLocalizations> oldDelegate) => false;
}

class RtlOverrideWidgetsLocalization implements WidgetsLocalizations {
  const RtlOverrideWidgetsLocalization();
  @override
  TextDirection get textDirection => TextDirection.rtl;
483
}