Skip to main content

General Questions

Patrol is a powerful UI testing framework for Flutter that extends Flutter’s built-in testing capabilities with native automation features. It allows you to interact with both Flutter widgets and native platform UI elements (like permission dialogs, system settings, and notifications) in a single test.
Patrol builds on top of Flutter’s testing infrastructure but adds several key features:
  • Native automation: Interact with platform UI elements outside your Flutter app
  • Better finders: More intuitive and powerful widget finding with chaining support
  • Hot restart: Quickly iterate on tests without full rebuilds
  • Test bundling: Run tests as native instrumented tests for better reliability
  • Web support: Run the same tests on web browsers with platform-specific automation
  • DevTools extension: Inspect native view hierarchy while developing tests
No, Patrol can coexist with integration_test. However, Patrol uses a different default test directory (patrol_test/ instead of integration_test/) to avoid conflicts. You can configure Patrol to use a custom directory if needed.
Patrol supports:
  • Android (emulators and physical devices)
  • iOS (simulators and physical devices)
  • macOS (alpha support)
  • Web (Chrome, Firefox, Safari, Edge, Webkit)
See the Supported Platforms page for details.
Yes, Patrol is open-source and free to use under the Apache 2.0 license. We also offer premium support and consulting services. See our Pricing page for more information.

Installation & Setup

This is usually caused by a version mismatch between patrol and patrol_cli. Check the Compatibility Table to ensure you’re using compatible versions. Update both packages:
dart pub global activate patrol_cli
flutter pub upgrade patrol
To run your application within a patrol test, you need to call $.pumpWidgetAndSettle() and pass your application’s main widget to it. Make sure you register all necessary services before calling $.pumpWidgetAndSettle().
void main() {
  patrolTest('real app test', ($) async {
    // Do all the necessary setup here (DI, services, etc.)
    
    await $.pumpWidgetAndSettle(const MyApp());
    
    // Start testing your app here
  });
}
It’s a good practice to create a setup wrapper function for your tests. See Initializing app inside a test.
Android package_name: Go to android/app/build.gradle and look for applicationId in the defaultConfig section.iOS bundle_id: Go to ios/Runner.xcodeproj/project.pbxproj and look for PRODUCT_BUNDLE_IDENTIFIER.macOS bundle_id: Go to macos/Runner.xcodeproj/project.pbxproj and look for PRODUCT_BUNDLE_IDENTIFIER.
Add test_directory to your pubspec.yaml:
patrol:
  app_name: My App
  test_directory: my_custom_tests
  android:
    package_name: com.example.myapp
  ios:
    bundle_id: com.example.MyApp

Android Issues

This is caused by using an incompatible JDK version. Run javac -version to check your JDK version. Patrol officially supports JDK 17.If you have Android Studio or IntelliJ IDEA installed, you can find the path to JDK by opening your project’s android directory and going to SettingsBuild, Execution, DeploymentBuild ToolsGradleGradle JDK.Learn more about JDK management
If the build hangs and running with --verbose shows it stuck at $ flutter build apk --config-only, updating orchestrator to 1.6.1 in your build.gradle might help.
androidTestImplementation 'androidx.test:orchestrator:1.6.1'
This can happen for several reasons:
  • Version mismatch between patrol and patrol_cli
  • Incorrect configuration in pubspec.yaml
  • Issues with Android test orchestrator
Try running patrol doctor to diagnose the issue.
WebView testing is supported. Make sure you’re using a recent version of Patrol (2.0.1+) which includes fixes for WebView support on modern Android versions. See issue #244 for details.

iOS Issues

Make sure you disabled “Parallel execution” for all schemes in Xcode. See this video for details.
Search for FLUTTER_TARGET in your project files and remove it (both value and key) from *.xcconfig and *.pbxproj files.After removing it, regenerate the configuration:
flutter build ios --config-only patrol_test/example_test.dart
Search for FLUTTER_TARGET in your project files and remove it (both value and key) from *.xcconfig and *.pbxproj files.
Check if this line in Podfile is present and uncommented:
platform :ios, '12.0'
Then check if iOS deployment version in Xcode project’s Build Settings section for all targets (Runner and RunnerUITests) are set to the same value as in Podfile.
Physical iOS device setup requires additional configuration. See our detailed guide: Setup for Physical iOS Devices
This was fixed in Patrol 1.0.8+. Make sure you’re using the latest version. Patrol automatically strips out native automation code in release builds to prevent App Store warnings.

Test Writing

Patrol provides powerful custom finders. You can find widgets by:
  • Text: $("Login")
  • Key: $(#loginButton)
  • Type: $(TextField)
  • Icon: $(Icons.add)
  • Chaining: $(Scaffold).$(ListView).$("Item")
See Finders documentation for more details.
We strongly recommend using keys for most cases. Keys are:
  • More reliable than text (which can change with translations)
  • More semantic than widget types (which are implementation details)
  • Less likely to break when refactoring
See Effective Patrol for best practices.
Use Patrol’s platform automation features:
// Grant permission
await $.platform.mobile.grantPermissionWhenInUse();

// Tap on native button
await $.platform.mobile.tap(Selector(text: 'Allow'));

// Enter text in native field
await $.platform.mobile.enterText(
  Selector(text: 'Email'),
  '[email protected]',
);
See Native Automation for more examples.
Use the scrollTo() method:
await $(#listView).$("Item 50").scrollTo();
await $(#listView).$("Item 50").tap();
This works even for items in lazy-loaded lists.
Common causes of flaky tests:
  • Not waiting for animations: Use $.pumpAndSettle() after actions
  • Race conditions: Use waitUntilVisible() or waitUntilExists() before assertions
  • Hardcoded timeouts: Use proper waiting mechanisms instead
  • Multiple matching widgets: Use .at(), .first, or .last to be specific
See Tips and Tricks for more guidance.
Yes, use --dart-define to pass environment variables:
patrol test --dart-define="USERNAME=test" --dart-define="PASSWORD=secret"
Then access them in your test:
const username = String.fromEnvironment('USERNAME');
const password = String.fromEnvironment('PASSWORD');
You can also use a .patrol.env file in your project root.
Several options:
  • patrol develop: Hot restart during test development
  • DevTools Extension: Inspect native view hierarchy
  • VS Code extension: Run and debug tests from your editor
  • Logging: Use $.log('message') to add logs
  • Breakpoints: Standard Dart debugging with IDE breakpoints
See Debugging Patrol Tests for details.

Web Testing

Yes! Patrol 4.0+ includes comprehensive web support. You can run the same tests on web browsers with platform-specific automation features like:
  • Cookie management
  • Browser navigation
  • Keyboard shortcuts
  • File uploads/downloads
  • Dialog handling
See Web Testing for more information.
Patrol supports:
  • Chrome
  • Firefox
  • Safari
  • Edge
  • Webkit
Browser testing is powered by Playwright under the hood.

CI/CD Integration

Yes! Patrol works great in CI/CD environments. We have guides for:
  • GitHub Actions
  • GitLab CI
  • Bitrise
  • Codemagic
  • Firebase Test Lab
  • BrowserStack
  • LambdaTest
See CI/CD Integration for setup instructions.
Patrol supports major cloud testing platforms:Each platform has specific configuration requirements documented in the integration guides.

Performance & Advanced

Patrol tests run as native instrumented tests, which makes them very reliable and performant. The patrol develop command with hot restart makes test development much faster than traditional approaches.
Yes, use the test bundling feature to run tests as separate native test cases. This enables parallel execution in most CI environments. Note that you need to disable parallel execution in Xcode for iOS.
Use patrolSetUp() and patrolTearDown():
void main() {
  patrolSetUp(() async {
    // Runs before each test
    await initializeApp();
  });
  
  patrolTearDown(() async {
    // Runs after each test
    await cleanup();
  });
  
  patrolTest('test 1', ($) async { ... });
  patrolTest('test 2', ($) async { ... });
}
Yes! Patrol works well with:
  • patrol_finders: Can be used in regular widget tests
  • Mockito/Mocktail: For mocking dependencies
  • flutter_driver: For alternative test approaches
  • Allure: For test reporting (see Allure Integration)

Getting Help

Please open an issue on GitHub with:
  • Clear description of the problem
  • Steps to reproduce
  • Patrol and patrol_cli versions
  • Flutter and Dart versions
  • Platform (Android/iOS/Web/macOS)
  • Relevant logs and error messages
If you couldn’t find an answer here, please:

Build docs developers (and LLMs) love