preface

In the development process, we all have a very important part, that is testing. The same with the Flutter development, we had to test our software when we finished developing the application. There are also a lot of automated software that can be used for testing. Here’s a look at the tests that flutter comes with. We can use this plugin to automate testing of our entire application.

Runtime environment

[√] Flutter (Channel stable, 2.2.3, on Microsoft Windows [Version 10.0.19042.1110], locale zh-CN)
    • Flutter version 2.2.3 at D:\flutter
    • Framework revision f4abaa0735 (6 weeks ago), 2021-07-01 12:46:11 -0700
    • Engine revision 241c87ad80
    • Dart version 2.13.4
    • Pub download mirror https://pub.flutter-io.cn
    • Flutter download mirror https://storage.flutter-io.cn
Copy the code

Install dependencies

First, we need to install the Flutter_driver plug-in. After the installation is complete, we create a new folder in the project (my filename is test_driver to define the folder name). This folder is used to write our test code.

dev_dependencies:
  flutter_driver:
    sdk: flutter
  test: any
Copy the code

The code

I’m going to write a test code, which is I’m going to go through the code and find the component THAT I need, like a button, and I’m going to click on it. How many pages do I need to scroll down to. And then I’ll swipe back, switch to the next page, and so on. According to the process of human thinking, the whole project is simulated.

app.dartTest entry file

import 'package:flutter_driver/driver_extension.dart';
import 'package:dynamic_theme/main.dart' as app;

void main() {
  // This line enables the extension.
  enableFlutterDriverExtension();

  // Call the `main()` function of the app, or call `runApp` with
  // any widget you are interested in testing.
  app.main();
}
Copy the code

app_test.dartExecution requires testing modules

I wrote the entrance file module, which contains the simulated event of page click scrolling. If you need to test the whole module, you can add multiple groups to the main function and test the function on demand.

import 'package:test/test.dart'; import 'entrance.dart'; Void main() {group(' toggle navigation slide ', entrance); }Copy the code

entrance.dartMock test script

import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';

void entrance() {
  late FlutterDriver driver;
  // Connect to the Flutter driver before running any tests.
  setUpAll(() async {
    driver = await FlutterDriver.connect();
  });
  // Close the connection to the driver after the tests have completed.
  tearDownAll(() async {
    await driver.close();
  });
// test('starts at 0', () async {
// Use the `driver.getText` method to verify the counter starts at 0.
// expect(await driver.getText(counterTextFinder), '0');
/ /});

  test('Switch pages', () async {
    await Future.delayed(Duration(seconds: 2));
    await driver.tap(find.byValueKey('tab_3'));
    await Future.delayed(Duration(seconds: 5));
    await driver.tap(find.byValueKey('tab_2'));
    await Future.delayed(Duration(seconds: 5));
    await driver.tap(find.byValueKey('tab_1'));
    await Future.delayed(Duration(seconds: 5));
    await driver.tap(find.byValueKey('tab_0'));
  });

  test('Slide to the bottom', () async {
    await driver.runUnsynchronized(() async {
      final listFinder = find.byValueKey('message_list');
      final itemFinder = find.byValueKey('item_78');
      await driver.scrollUntilVisible(
        listFinder,
        itemFinder,
        dyScroll: 300.0,); }); }); test('Slide page to top', () async {
    await driver.runUnsynchronized(() async {
      final listFinder = find.byValueKey('message_list');
      final itemFinder = find.byValueKey('item_1');
      await driver.scrollUntilVisible(
        listFinder,
        itemFinder,
        dyScroll: 300.0,); }); }); test('Jump Page', () async {
    // First, tap the button.
    await driver.tap(find.byValueKey('jump_list'));

    // Then, verify the counter text is incremented by 1.
    expect(await driver.getText(find.byValueKey('title')), 'NewList- Route pass parameter ');
  });

  test('Back to page', () async {
    await Future.delayed(Duration(seconds: 5));
    final buttonFinder = find.byValueKey('back');
    // First, tap the button.
    await driver.tap(buttonFinder);

    // Then, verify the counter text is incremented by 1.
    // expect(await driver.getText(counterTextFinder), '1');
  });
}
Copy the code

How do we find the components we need?

The test script uses find.byValueKey(‘ name of key ‘) to find the component we need, which we can simulate by clicking, scrolling, double-clicking, and so on. Here I use the find.byValueKey method, which is described below.

Find the scroll list, scroll somewhere

First we need to find the list we need to slide, I find. ByValueKey method to find the slide component, at the beginning of writing business code I have added a key name is Message_list. Do not repeat key to avoid finding component problems.

Business code

EasyRefresh.custom(
  key: Key('message_list'),
  enableControlFinishRefresh: true,
  ...
Copy the code

Test code example

Driver scrollUntilVisible analog sliding, itemFinder sliding to the key parameter: item_78 when this element is not sliding, the next slide 300 pixels at a time.

test('Slide to the bottom', () async {
  await driver.runUnsynchronized(() async {
    final listFinder = find.byValueKey('message_list');
    final itemFinder = find.byValueKey('item_78');
    await driver.scrollUntilVisible(
      listFinder,
      itemFinder,
      dyScroll: 300.0,); }); });Copy the code

driverSupported methods

-   [checkHealth](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/checkHealth.html) ({/ Duration (https://docs-flutt Timeout}) - > er-io.firebaseapp.com/flutter/dart-core/Duration-class.html) [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < [Health] (HTTP: / / https://docs-flutter-io .firebaseapp.com/flutter/flutter_driver/Health-class.html)>

-   Checks the status of the Flutter Driver extension.

-   [clearTimeline](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/clearTimeline.html) ({/ Duration (https://docs-flu tter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout: _kUnusuallyLongTimeout}) - > [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < void >

-   Clears all timeline events recorded up until now. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/clearTimeline.html)

-   [close](https:/ / () - > docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/close.html) [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < void >

-   Closes the underlying connection to the VM service. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/close.html)

-   [enterText](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/enterText.html) ([String] (HTTP: / / https://docs-flutter-io .firebaseapp.com/flutter/dart-core/String-class.html) text, {[Duration] (https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) the timeout}) - > [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < void >

-   Enters `text` into the currently focused text input, such as the [EditableText](https://docs-flutter-io.firebaseapp.com/flutter/widgets/EditableText-class.html) widget. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/enterText.html)

-   [forceGC](https:/ / () - > docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/forceGC.html) [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < void >

-   Force a garbage collection run in the VM.

-   [getRenderTree](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/getRenderTree.html) ({/ Duration (https://docs-flu Timeout}) - > tter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < [RenderTree] (HTTP: / / https://docs-flutte r-io.firebaseapp.com/flutter/flutter_driver/RenderTree-class.html)>

-   Returns a dump of the render tree.

-   [getSemanticsId](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/getSemanticsId.html) ([SerializableFinder] (HTTPS: //docs-flutter-io.firebaseapp.com/flutter/flutter_driver/SerializableFinder-class.html) finder, {[Duration] (https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) the timeout}) - > [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < (int) (HTTP: / / https://docs-flutter-io.fi rebaseapp.com/flutter/dart-core/int-class.html)>

-   Retrieves the semantics node id for the object returned by `finder`, or the nearest ancestor with a semantics node. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/getSemanticsId.html)

-   [getText](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/getText.html) ([SerializableFinder] (HTTP: / / https://docs- flutter-io.firebaseapp.com/flutter/flutter_driver/SerializableFinder-class.html) finder, {[Duration] (https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) the timeout}) - > [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < [String] (HTTP: / / https://docs-flutter-io .firebaseapp.com/flutter/dart-core/String-class.html)>

-   Returns the text in the `Text` widget located by `finder`.

-   [getVmFlags](https:/ / () - > docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/getVmFlags.html) [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < [List] (HTTP: / / https://docs-flutter-io.f Irebaseapp.com/flutter/dart-core/List-class.html) < (Map) (HTTP: / / https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Map-c Lass. HTML) < [String] (https://docs-flutter-io.firebaseapp.com/flutter/dart-core/String-class.html), the dynamic > > >

-   Returns the Flags set in the Dart VM as JSON. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/getVmFlags.html)

-   [requestData](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/requestData.html) ([String] (HTTP: / / https://docs-flutter- io.firebaseapp.com/flutter/dart-core/String-class.html) message, {[Duration] (https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) the timeout}) - > [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < [String] (HTTP: / / https://docs-flutter-io .firebaseapp.com/flutter/dart-core/String-class.html)>

-   Sends a string and returns a string. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/requestData.html)

-   [runUnsynchronized](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/runUnsynchronized.html) < T > ([Future] (HTTP: / / https://doc S-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < T > action (), {[Duration] (https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) the timeout}) - > [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < T >

-   `action` will be executed with the frame sync mechanism disabled. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/runUnsynchronized.html)

-   [screenshot](https:/ / () - > docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/screenshot.html) [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < [List] (HTTP: / / https://docs-flutter-io.f Irebaseapp.com/flutter/dart-core/List-class.html) < (int) (HTTP: / / https://docs-flutter-io.firebaseapp.com/flutter/dart-core/int-c lass.html)>>

-   Take a screenshot. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/screenshot.html)

-   [scroll](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/scroll.html) ([SerializableFinder] (HTTP: / / https://docs-f lutter-io.firebaseapp.com/flutter/flutter_driver/SerializableFinder-class.html) finder, [double](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/double-class.html) dx, [double](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/double-class.html) dy, [Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) duration, { [int](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/int-class.html) frequency: 60. [Duration] a timeout}) - > (https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < void >

-   Tell the driver to perform a scrolling action. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/scroll.html)

-   [scrollIntoView](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/scrollIntoView.html) ([SerializableFinder] (HTTPS: //docs-flutter-io.firebaseapp.com/flutter/flutter_driver/SerializableFinder-class.html) finder, { [double](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/double-class.html) alignment: 0.0, [Duration] a timeout}) - > (https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < void >

-   Scrolls the Scrollable ancestor of the widget located by `finder` until the widget is completely visible. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/scrollIntoView.html)

-   [scrollUntilVisible](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/scrollUntilVisible.html) ([SerializableFinder] (ht tps://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/SerializableFinder-class.html) scrollable, [SerializableFinder](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/SerializableFinder-class.html) item,  { [double](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/double-class.html) alignment: 0.0, [double] dxScroll (https://docs-flutter-io.firebaseapp.com/flutter/dart-core/double-class.html) : 0.0, [double] dyScroll (https://docs-flutter-io.firebaseapp.com/flutter/dart-core/double-class.html) : 0.0, [Duration] a timeout}) - > (https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < void >

-   Repeatedly [scroll](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/scroll.html) the widget located by `scrollable` by `dxScroll` and `dyScroll` until `item` is visible, and then use [scrollIntoView](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/scrollIntoView.html) to ensure the item's final position matches `alignment`. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/scrollUntilVisible.html)

-   [setSemantics](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/setSemantics.html) ([bool] (HTTP: / / https://docs-flutter-i o.firebaseapp.com/flutter/dart-core/bool-class.html) enabled, {[Duration] (https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) the timeout}) - > [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < [bool] (HTTP: / / https://docs-flutter-io.f irebaseapp.com/flutter/dart-core/bool-class.html)>

-   Turns semantics on or off in the Flutter app under test. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/setSemantics.html)

-   [setTextEntryEmulation](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/setTextEntryEmulation.html) ({[bool] (https://docs -flutter-io.firebaseapp.com/flutter/dart-core/bool-class.html) enabled, [Duration] a timeout}) - > (https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < void >

-   Configures text entry emulation. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/setTextEntryEmulation.html)

-   [startTracing](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/startTracing.html) ({[List] (https://docs-flutter- Io.firebaseapp.com/flutter/dart-core/List-class.html) < [TimelineStream] (HTTP: / / https://docs-flutter-io.firebaseapp.com/flutter/ flutter_driver/TimelineStream-class.html)> streams: _defaultStreams, [Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout: _kUnusuallyLongTimeout}) - > [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < void >

-   Starts recording performance traces. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/startTracing.html)

-   [stopTracingAndDownloadTimeline](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/stopTracingAndDownloadTimeline.html) ({[Duration] (https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout: _kUnusuallyLongTimeout}) - > [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < / Timeline (https://docs-flutter- io.firebaseapp.com/flutter/flutter_driver/Timeline-class.html)>

-   Stops recording performance traces and downloads the timeline. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/stopTracingAndDownloadTimeline.html)

-   [tap](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/tap.html) ([SerializableFinder] (HTTP: / / https://docs-flut ter-io.firebaseapp.com/flutter/flutter_driver/SerializableFinder-class.html) finder, {[Duration] (https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) the timeout}) - > [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < void >

-   Taps at the center of the widget located by `finder`.

-   [traceAction](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/traceAction.html) ([Future] (HTTP: / / https://docs-flutter- io.firebaseapp.com/flutter/dart-async/Future-class.html) action(), { [the List] (https://docs-flutter-io.firebaseapp.com/flutter/dart-core/List-class.html) < [TimelineStream] (HTTP: / / https://docs-flutter -io.firebaseapp.com/flutter/flutter_driver/TimelineStream-class.html)> streams: _defaultStreams, [bool](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/bool-class.html) retainPriorEvents: And false}) [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < / Timeline (https://docs-flutter- io.firebaseapp.com/flutter/flutter_driver/Timeline-class.html)>

-   Runs `action` and outputs a performance trace for it. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/traceAction.html)

-   [waitFor](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/waitFor.html) ([SerializableFinder] (HTTP: / / https://docs- flutter-io.firebaseapp.com/flutter/flutter_driver/SerializableFinder-class.html) finder, {[Duration] (https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) the timeout}) - > [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < void >

-   Waits until `finder` locates the target.

-   [waitForAbsent](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/waitForAbsent.html) ([SerializableFinder] (HTTPS: / /docs-flutter-io.firebaseapp.com/flutter/flutter_driver/SerializableFinder-class.html) finder, {[Duration] (https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) the timeout}) - > [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < void >

-   Waits until `finder` can no longer locate the target.

-   [waitUntilNoTransientCallbacks](https:/ / docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/waitUntilNoTransientCallbacks.html) ({/ Duration ( Timeout}) - > https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) [Future] (https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) < void >

-   Waits until there are no more transient callbacks in the queue. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/waitUntilNoTransientCallbacks.html)
Copy the code

findMethod supported lookupfind.text find.byValueKey find.bySemanticsLabel find.pageBack find.byType

SerializableFinder text(String text) => ByText(text);

/// Finds widgets by [key]. Only [String] and [int] values can be used.
SerializableFinder byValueKey(dynamic key) => ByValueKey(key);

/// Finds widgets with a tooltip with the given [message].
SerializableFinder byTooltip(String message) => ByTooltipMessage(message);

/// Finds widgets with the given semantics [label].
SerializableFinder bySemanticsLabel(Pattern label) => BySemanticsLabel(label);

/// Finds widgets whose class name matches the given string.
SerializableFinder byType(String type) => ByType(type);

/// Finds the back button on a Material or Cupertino page's scaffold.
SerializableFinder pageBack() => const PageBack();
Copy the code

run flutter drive --target=test_driver/app.dart

Run flutter drive –target=test_driver/app.dart Finally we see the console output All Tests Passed! The instructions have been successfully tested.

The test simulation

Test output

D:\project\dynamic_theme>flutter drive --target=test_driver/app.dart
Running "flutter pub get" in dynamic_theme...                    1,399ms
Running Gradle task 'assembleDebug'. Running Gradle task'assembleDebug'. Done 28.3s √ Built build\app\outputs\ flow-apk \app-debug.apk. Installing build\app\outputs\ flow-apk \app.apk... 803MS 00:00 +0: Switch navigation slider page (setUpAll) VMServiceFlutterDriver: Connecting to Flutter application at http://127.0.0.1:62607/4xhdHTUuf_U=/ VMServiceFlutterDriver: Isolate found with number: 3474149526286947 VMServiceFlutterDriver: Isolate is paused at start. VMServiceFlutterDriver: Attempting to Resume ISOLATE I/ Flutter (2384): Debug Version --KK VMServiceFlutterDriver: Connected to the Flutter Application. 00:02 +0: Toggle Navigation Swipe page toggle Page I/ Flutter (2384): Initial Link: I/ Flutter (2384): InitialLink -- 00:20 +1: Toggled navigation slide page slide page to the bottom VMServiceFlutterDriver: waitFor message is taking a long time to complete... VMServiceFlutterDriver: waitFor Message is taking a long time to complete... I/ FLUTTER (2384): enter NewView I/ Flutter (2384): _NewViewState#0c082(lifecycle state: initialized)00:58 +5: All tests passed! (tearDownAll) -Copy the code

The project address

Here is the end, interested partners can go to download the source experience. Check the version of the flutter if it cannot run.