Skip to main content
Flutter’s integration_test provides basic integration testing support, but it cannot interact with the operating system your Flutter app runs on. This limitation makes it impossible to test many critical business features:
  • Granting runtime permissions (camera, location, notifications)
  • Signing into apps using WebView or OAuth (like Google Sign-In)
  • Interacting with system notifications
  • Testing app state after backgrounding and returning
  • Controlling device settings (Wi-Fi, mobile data, location, dark mode)
  • Handling native dialogs and system UI
Patrol’s native automation feature solves these problems by providing a unified API to interact with the underlying platform.

What is Native Automation?

Native automation allows your Flutter integration tests to:
  1. Control device settings - Toggle Wi-Fi, cellular, Bluetooth, dark mode, and more
  2. Handle permissions - Automatically grant or deny runtime permission requests
  3. Interact with notifications - Open notification shade, tap notifications, verify content
  4. Navigate native UI - Tap buttons, enter text in WebViews and native screens
  5. Simulate device actions - Press home button, open recent apps, simulate hardware buttons

Platform Support

Native automation is available on Android, iOS, and Web platforms.
macOS support is in alpha and does not have platform automation implemented yet.

Quick Example

Here’s a simple example showing the power of native automation:
patrol_test/app_test.dart
import 'package:patrol/patrol.dart';

void main() {
  patrolTest('handle location permission and test map', (PatrolIntegrationTester $) async {
    await $.pumpWidgetAndSettle(MyApp());

    // Prepare network conditions
    await $.platform.mobile.enableCellular();
    await $.platform.mobile.disableWifi();

    // Enable dark mode
    await $.platform.mobile.enableDarkMode();

    // Open map screen which requests location permission
    await $('Map').tap();

    // Handle the native permission dialog
    await $.platform.mobile.selectFineLocation();
    await $.platform.mobile.grantPermissionWhenInUse();

    // Verify map is working
    expect($('Current Location'), findsOneWidget);
  });
}

The $.platform API

Patrol provides native automation through the $.platform property on PatrolIntegrationTester. This API is organized into platform-specific namespaces:

Cross-Platform Mobile

// Works on both Android and iOS
await $.platform.mobile.pressHome();
await $.platform.mobile.openNotifications();
await $.platform.mobile.grantPermissionWhenInUse();

Android-Specific

// Android only
await $.platform.android.pressBack();
await $.platform.android.pressDoubleRecentApps();
await $.platform.android.enableLocation();

iOS-Specific

// iOS only
await $.platform.ios.swipeBack();
await $.platform.ios.closeHeadsUpNotification();

Web-Specific

// Web only
await $.platform.web.grantPermissions(permissions: ['clipboard-read']);
await $.platform.web.addCookie(name: 'session', value: 'abc123');
await $.platform.web.acceptNextDialog();

Key Capabilities

Permissions

Handle runtime permission dialogs automatically with support for location accuracy selection

Notifications

Open notification shade, tap notifications by index or selector, verify notification content

Device Settings

Control Wi-Fi, cellular, Bluetooth, dark mode, airplane mode, and location

Native UI

Tap, swipe, and enter text in WebViews, native dialogs, and system screens

How It Works

Patrol integrates Flutter tests with native Android and iOS testing frameworks:
  • Android: Uses UiAutomator to interact with native UI
  • iOS: Uses XCTest to control iOS UI and device features
  • Web: Uses Playwright to automate browser interactions
This integration allows your Dart tests to run as true native tests, giving you access to:
  • Native test runners (Gradle, Xcode, Firebase Test Lab)
  • Device farms and CI/CD integrations
  • HTML test reports and IDE test viewers
  • The entire native testing ecosystem
Your tests are written in Dart but run as native Android/iOS tests under the hood. This gives you the best of both worlds!

Real-World Use Cases

Testing OAuth Login

patrolTest('sign in with Google', ($) async {
  await $.pumpWidgetAndSettle(MyApp());
  
  // Tap sign in button which opens WebView
  await $('Sign in with Google').tap();
  
  // Interact with native WebView
  await $.platform.mobile.enterText(
    Selector(text: 'Email or phone'),
    text: '[email protected]',
  );
  await $.platform.mobile.tap(Selector(text: 'Next'));
  
  // Verify successful login
  await $('Welcome back!').waitUntilVisible();
});

Testing Push Notifications

patrolTest('handle push notification', ($) async {
  await $.pumpWidgetAndSettle(MyApp());
  
  // Grant notification permission
  await $('Enable Notifications').tap();
  await $.platform.mobile.grantPermissionWhenInUse();
  
  // Simulate receiving a notification
  // ... trigger notification from backend ...
  
  // Open notifications and tap the first one
  await $.platform.mobile.openNotifications();
  await $.platform.mobile.tapOnNotificationBySelector(
    Selector(textContains: 'New message from'),
  );
  
  // Verify app opened to correct screen
  expect($('Message Details'), findsOneWidget);
});

Testing Network Conditions

patrolTest('handle offline mode', ($) async {
  await $.pumpWidgetAndSettle(MyApp());
  
  // Disable network
  await $.platform.mobile.disableWifi();
  await $.platform.mobile.disableCellular();
  
  // Try to load data
  await $('Refresh').tap();
  
  // Verify offline message appears
  expect($('No internet connection'), findsOneWidget);
  
  // Re-enable network
  await $.platform.mobile.enableWifi();
  await $('Refresh').tap();
  
  // Verify data loads
  await $('Data loaded').waitUntilVisible();
});

Next Steps

Setup

Configure your project for native automation

Usage Guide

Learn the native automation API

Advanced Topics

Deep dive into advanced features

Feature Parity

See what’s available on each platform

Build docs developers (and LLMs) love