Skip to main content
Patrol provides comprehensive support for testing Flutter applications on Android devices and emulators. Built on top of Android’s UIAutomator framework, Patrol enables you to interact with both Flutter widgets and native Android UI elements.

Platform Support

Patrol supports:
  • Android 5.0 (API 21) and newer
  • Both physical devices and emulators
  • Android-specific native interactions
Patrol requires Android SDK version 21 or higher. Make sure your minSdkVersion in android/app/build.gradle is set to at least 21.

Setup Requirements

Before testing on Android, ensure you have the following installed:
1

Android SDK

Install Android SDK through Android Studio or command-line tools.Set the ANDROID_HOME environment variable:
export ANDROID_HOME=$HOME/Library/Android/sdk  # macOS
export ANDROID_HOME=$HOME/Android/Sdk          # Linux
2

ADB (Android Debug Bridge)

Verify that adb is available:
adb version
ADB is typically installed with the Android SDK at $ANDROID_HOME/platform-tools/adb.
3

Java Development Kit (JDK)

Patrol officially works with JDK 17. Verify your version:
javac -version
Using an incompatible JDK version may cause “Unsupported class file major version” errors.

Native Integration

To enable Patrol’s native automation on Android, you need to configure your app’s test infrastructure.
1

Configure pubspec.yaml

Add your Android package name to pubspec.yaml:
pubspec.yaml
patrol:
  app_name: My App
  android:
    package_name: com.example.myapp
Find your package name in android/app/build.gradle under the applicationId field in the defaultConfig section.
2

Create Test File

Create MainActivityTest.java in android/app/src/androidTest/java/com/example/myapp/:
android/app/src/androidTest/java/com/example/myapp/MainActivityTest.java
package com.example.myapp; // replace with your app's package

import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import pl.leancode.patrol.PatrolJUnitRunner;

@RunWith(Parameterized.class)
public class MainActivityTest {
    @Parameters(name = "{0}")
    public static Object[] testCases() {
        PatrolJUnitRunner instrumentation = (PatrolJUnitRunner) InstrumentationRegistry.getInstrumentation();
        instrumentation.setUp(MainActivity.class);
        instrumentation.waitForPatrolAppService();
        return instrumentation.listDartTests();
    }

    public MainActivityTest(String dartTestName) {
        this.dartTestName = dartTestName;
    }

    private final String dartTestName;

    @Test
    public void runDartTest() {
        PatrolJUnitRunner instrumentation = (PatrolJUnitRunner) InstrumentationRegistry.getInstrumentation();
        instrumentation.runDartTest(dartTestName);
    }
}
3

Configure build.gradle.kts

Update android/app/build.gradle.kts (or build.gradle for Groovy):
android {
    defaultConfig {
        testInstrumentationRunner = "pl.leancode.patrol.PatrolJUnitRunner"
        testInstrumentationRunnerArguments["clearPackageData"] = "true"
    }
    
    testOptions {
        execution = "ANDROIDX_TEST_ORCHESTRATOR"
    }
}

dependencies {
    androidTestUtil("androidx.test:orchestrator:1.5.1")
}

Running Tests

On Emulator

Start an Android emulator and run your tests:
patrol test --target patrol_test/app_test.dart

On Physical Device

Connect your Android device via USB with USB debugging enabled:
# Enable USB debugging in Developer Options on your device
adb devices  # Verify device is connected
patrol test --target patrol_test/app_test.dart
To enable Developer Options on your Android device, go to Settings > About phone and tap Build number 7 times.

Specifying a Device

If you have multiple devices connected:
patrol devices  # List available devices
patrol test --device emulator-5554 --target patrol_test/app_test.dart

Android-Specific Features

Patrol provides Android-specific automation through $.platform.android:
// Press the back button
await $.platform.android.pressBack();

// Double press recent apps
await $.platform.android.pressDoubleRecentApps();

UI Interactions

// Tap on native Android views
await $.platform.android.tap(
  AndroidSelector(text: 'Accept'),
);

// Double tap
await $.platform.android.doubleTap(
  AndroidSelector(resourceId: 'com.example:id/button'),
);

// Tap at specific coordinates
await $.platform.android.tapAt(x: 100, y: 200);

// Enter text into native views
await $.platform.android.enterText(
  AndroidSelector(text: 'Enter email'),
  text: '[email protected]',
);

// Enter text by index
await $.platform.android.enterTextByIndex('username', index: 0);
await $.platform.android.enterTextByIndex('password', index: 1);

Gestures

// Swipe
await $.platform.android.swipe(
  from: AndroidSelector(text: 'Item 1'),
  to: AndroidSelector(text: 'Item 5'),
);

// Swipe back gesture
await $.platform.android.swipeBack();

// Pull to refresh
await $.platform.android.pullToRefresh();

Notifications

// Tap on notification by selector
await $.platform.mobile.openNotifications();
await $.platform.android.tapOnNotificationBySelector(
  AndroidSelector(textContains: 'New message'),
);

// Tap by index
await $.platform.android.tapOnNotificationByIndex(0);

Location

// Enable/disable location services
await $.platform.android.enableLocation();
await $.platform.android.disableLocation();
// Take a photo with camera
await $.platform.android.takeCameraPhoto();

// Pick image from gallery
await $.platform.android.pickImageFromGallery();

// Pick multiple images
await $.platform.android.pickMultipleImagesFromGallery();

Android Selectors

Use AndroidSelector to target native Android views:
// By text
AndroidSelector(text: 'Submit')

// By text contains
AndroidSelector(textContains: 'Submit')

// By text starts with
AndroidSelector(textStartsWith: 'Sub')

// By resource ID
AndroidSelector(resourceId: 'com.example.myapp:id/submit_button')

// By class name
AndroidSelector(className: 'android.widget.Button')

// By content description
AndroidSelector(contentDescription: 'Submit button')

// Combine multiple selectors
AndroidSelector(
  className: 'android.widget.EditText',
  instance: 1, // Second EditText
)

Capabilities

Android platform features available through $.platform.mobile (cross-platform) and $.platform.android (Android-only):
FeatureStatus
Press home
Open app
Open notifications
Close notifications
Open quick settings
Open URL
Enable/disable dark mode
Enable/disable airplane mode
Enable/disable cellular
Enable/disable Wi-Fi
Enable/disable Bluetooth
Press volume up/down
Handle permission dialogs
Set mock location✅ (emulator only)
Press back
Tap native views
Enter text
Swipe
Pull to refresh
Tap on notifications
Enable/disable location
Camera & gallery
WebView interaction⚠️ Limited
For a complete feature parity comparison across all platforms, see the Feature Parity page.

Limitations

Mock Location

Setting mock location ($.platform.mobile.setMockLocation()) is not supported on physical devices. It only works on emulators.

WebView Interactions

Interacting with WebView content on Android has limitations. For web content testing, consider:
  • Using Patrol’s web testing capabilities if your content can be tested in a browser
  • Exposing JavaScript interfaces for test automation
See issue #244 for more details.

ProGuard Configuration

ProGuard can cause issues with Patrol, such as ClassNotFoundExceptions. Either keep all Patrol packages or disable ProGuard in debug builds:
android/app/build.gradle.kts
buildTypes {
    getByName("debug") {
        isMinifyEnabled = false
        isShrinkResources = false
    }
}

Troubleshooting

Build Hangs

If patrol test or patrol build android gets stuck:
Try updating the orchestrator to version 1.6.1 in your build.gradle:
androidTestUtil("androidx.test:orchestrator:1.6.1")

Class File Version Error

If you see “Unsupported class file major version” errors:
  1. Check your JDK version: javac -version
  2. Patrol officially supports JDK 17
  3. Configure your IDE to use the correct JDK in Settings > Build, Execution, Deployment > Build Tools > Gradle > Gradle JDK

Device Not Found

If Patrol can’t find your device:
# Check device connection
adb devices

# If no devices listed, check:
# 1. USB debugging is enabled
# 2. USB cable is working (try a different one)
# 3. Device is authorized (check device screen for authorization prompt)

# Restart ADB server if needed
adb kill-server
adb start-server

Next Steps

Feature Parity

See all available Android features

Write Your First Test

Start writing Patrol tests

Native Usage

Learn native automation basics

iOS Platform

Testing on iOS devices

Build docs developers (and LLMs) love