Appium Flutter Driver

repository·main·Indexed 19 days ago

https://github.com/appium/appium-flutter-driver

A driver for Appium that enables automation of Flutter applications by communicating with the Dart VM via the Flutter Driver protocol. It supports switching between FLUTTER, NATIVE_APP, and WEBVIEW contexts, allowing developers to interact with Flutter widgets and native platform elements. The driver requires apps to be compiled in debug or profile mode with the flutter_driver extension enabled. It is accompanied by the Appium Flutter Finder library, providing finding utilities for Node.js, Python, Ruby, and Kotlin.

Tokens
16.6K
Snippets
59
Records
74
Agent score
68%

What's inside appium-flutter-driver

  1. Manage automation contexts in Appium Flutter Driver

    main

    To automate different parts of a Flutter application, you must switch between three primary contexts:

    1. FLUTTER: Used to send flutter_driver commands to the Dart VM. This is where you interact with Flutter widgets.
    2. NATIVE_APP: Used to interact with native Android (via UiAutomator2) or iOS (via XCUITest) elements. Use this for system dialogs or native components surrounding the Flutter app.
    3. WEBVIEW_XXXX: Used for testing WebViews that are not accessible via flutter_driver.

    You must explicitly call driver.switchContext() to move between these targets.

    // Example context switching in WebdriverIO
    if (process.env.APPIUM_OS === 'android') {
      await driver.switchContext('NATIVE_APP');
      await (await driver.$('~fab')).click(); // Interact with native element
      await driver.switchContext('FLUTTER'); // Switch back to Flutter widgets
    }
  2. Execute Flutter Driver commands via WebDriver

    main

    The Appium Flutter Driver exposes the underlying Flutter Driver API through the execute command. In most WebDriver clients, you use a command prefix (like flutter: for Android/iOS or mobile: in standard Appium) to invoke these specific Flutter capabilities.

    Commands are categorized by their Scope:

    • Session: Affects the entire driver session.
    • Widget: Acts upon a specific element found via a Finder.
    • System: Low-level system or VM information.

    Note: The examples below use webdriverio syntax. Replace driver.execute('flutter:...') with the appropriate method for your specific client (e.g., driver.execute_script in Python or driver.execute_script in Ruby).

  3. Compare Appium Flutter Driver vs UIAutomator2/XCUITest

    main

    Choosing between the Appium Flutter Driver and standard native drivers depends on your app's build and testing strategy:

    Use Appium Flutter Driver when:

    • You need to write tests in languages other than Dart.
    • You need to run tests on multiple devices simultaneously.
    • You are using device farms that support Appium.
    • You need to interact with Flutter elements directly via the Dart VM (requires debug or profile builds).

    Use UIAutomator2/XCUITest (Native Drivers) when:

    • You want to test a release app (blackbox testing).
    • You have properly configured semanticLabel or identifier (for Flutter 3.19+) in your Flutter code. These map to resource-id (Android) and accessibilityIdentifier (iOS).
    • You want to avoid the overhead of the Dart VM connection.

    Note for Flutter 3.19+: If using native drivers, you may need to set "appium:settings[disableIdLocatorAutocompletion]": true to work with resource-id without package name prefixes on Android.

  4. Understand Appium Flutter Driver contexts

    main

    The Appium Flutter Driver manages three distinct contexts to allow interaction with different layers of the application:

    • FLUTTER: Sends commands directly to the Dart VM over the observatory URL. This allows for direct interaction with Flutter elements using the flutter_driver API. Note that page source is not supported in this context; use the getRenderTree command instead.
    • NATIVE_APP: Uses the standard Appium UIAutomator2 (Android) or XCUITest (iOS) driver. This is useful for interacting with native OS elements or system dialogs.
    • WEBVIEW: Manages WebView contents using the underlying native driver (UIAutomator2 or XCUITest).
  5. Import appium-flutter-finder in ESM projects

    main

    As of version 0.3.0, appium-flutter-finder is an ESM-only package. You must use import statements instead of require(). Deep imports from appium-flutter-finder/build/... are no longer supported; always import from the package root.

    // Standard ESM import
    import * as finder from 'appium-flutter-finder';
    
    // Named imports
    import {byText, byValueKey} from 'appium-flutter-finder';
  6. Install Appium Flutter Driver

    main

    To use Appium Flutter Driver version 3.0.0 or later, you must have Appium 3 installed. You can install the driver via npm or from a local path.

    Install via npm:

    appium driver install --source=npm appium-flutter-driver

    Install from a local path:

    appium driver install --source local /path/to/appium-flutter-driver/driver
  7. Install AppiumFlutterFinder via JitPack

    main

    To use the Kotlin finder elements for the Appium Flutter Driver in your Android project, you can include them via JitPack. You must first configure your settings.gradle to include the JitPack repository, and then add the dependency to your build.gradle file.

    Note that the package name for this project is pro.truongsinh.appium_flutter.

    // settings.gradle
    dependencyResolutionManagement {
    	repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    	repositories {
    		mavenCentral()
    		maven { url 'https://jitpack.io' }
    	}
    }
    
    // build.gradle
    dependencies {
    	// Use the specific version or main-SNAPSHOT
    	testImplementation 'com.github.appium:appium-flutter-driver:kotlin-finder-0.0.7'
    }
  8. Start an application with Appium Flutter Driver

    main

    There are four ways to launch your application and establish a Dart VM connection:

    1. Standard Launch (using app capability)

    Include the app capability in your session configuration. The driver will start the app and immediately attempt to connect to the Dart VM.

    2. Mid-session Activation (using activate_app)

    Useful if you want to start a session first and then launch the app later:

    1. Start a session without the app capability.
    2. Install the app using driver.install_app or mobile:installApp.
    3. Launch the app using driver.activate_app or mobile:activateApp.

    3. External Launch (using flutter:connectObservatoryWsUrl)

    Use this if you manage the application lifecycle yourself (e.g., via ios-go, iproxy, or tidevice):

    1. Start a session without the app capability.
    2. Install the app.
    3. Call the flutter:connectObservatoryWsUrl command to continuously search for the observatory URL.
    4. Launch the app externally. Once the URL is identified, the driver connects.

    4. iOS Instrument Launch (using flutter:launchApp)

    Specifically for iOS apps that do not print the observatory URL via standard methods:

    1. Start a session without the app capability.
    2. Install the app.
    3. Call flutter:launchApp to start the iOS app via the instrument service.

    Note: Ensure the target app process is stopped before attempting these methods.

    // Example of using flutter:launchApp for iOS
    driver.execute_script('flutter:launchApp', 'com.example.bundleId', {arguments: ['arg1'], environment: {ENV1: 'env'}});
  9. Install and run Appium Flutter Driver locally

    main

    To run the Appium Flutter Driver automation locally for development or testing, follow these steps to set up the driver, Appium server, and example applications:

    1. Driver and Appium Setup

    1. Ensure node and npm are installed.
    2. Navigate to the driver directory: cd ./driver.
    3. Install TypeScript globally: npm install -g typescript.
    4. Install driver dependencies: npm install.
    5. Link the driver: npm link.
    6. Install Appium globally: npm i -g appium.
    7. Start the Appium server in a separate terminal by running: appium.

    2. Application Setup

    1. Create a directory named apps.
    2. Download the example debug applications from the v0.0.4 releases and place them in the apps directory:
      • Android: android-real-debug.apk
      • iOS: ios-sim-debug.zip
    3. Update the file paths for the .apk or .ipa in the example script located at example/nodejs/src/index.js (specifically around line 13) to match your local paths.

    3. Running the Example

    Execute the automation script using the npm start command, specifying the target operating system via the APPIUM_OS environment variable:

    • For Android: APPIUM_OS=android npm start
    • For iOS: APPIUM_OS=ios npm start
    # Setup steps summary
    cd ./driver
    npm install -g typescript
    npm install
    npm link
    npm i -g appium
    
    # Run Appium in a separate terminal
    appium
    
    # Run the automation example
    APPIUM_OS=android npm start
    # OR
    APPIUM_OS=ios npm start
  10. Configure Flutter application requirements

    main

    To use the Appium Flutter Driver, your Flutter application must meet the following requirements:

    1. Build Mode: The app must be compiled in debug or profile mode.
    2. Dependencies: The application must include the flutter_driver package in its pubspec.yaml.
    3. Configuration: You must use enableFlutterDriverExtension in your main.dart file.

    Example pubspec.yaml configuration:

    # pubspec.yaml
    dev_dependencies:
      flutter_driver:
        sdk: flutter
  11. Extend Flutter Driver with custom commands

    main

    You can add custom functionality to the Flutter Driver using the CommandExtension class. This requires adding specific Dart files to your project's lib folder and registering them in your app's entry point (main.dart or test_main.dart).

    Available Extensions:

    • dragAndDropWithCommandExtension: Performs drag-and-drop via coordinates and duration.
    • getTextWithCommandExtension: Retrieves text from widgets containing TextSpan.

    Implementation Steps:

    1. Copy the required .dart files (e.g., drag_commands.dart) to your lib folder.
    2. Update your main() function to include the extensions in enableFlutterDriverExtension().

    Example Registration:

    import 'drag_commands.dart';
    import 'get_text_command.dart';
    
    void main() {
      enableFlutterDriverExtension(
          commands: [DragCommandExtension(), GetTextCommandExtension()]);
      runApp(const MyApp());
    }