Skip to main content
PatrolIntegrationTester is the main testing interface provided to your patrolTest() callback. It extends PatrolTester with native automation capabilities through the platform property.

Class Definition

class PatrolIntegrationTester extends PatrolTester {
  PatrolIntegrationTester({
    required WidgetTester tester,
    required PatrolTesterConfig config,
    required PlatformAutomator platformAutomator,
  });
}

Properties

platform
PlatformAutomator
Provides access to native automation features for interacting with Android, iOS, and Web platforms.This is the primary interface for native automation.
platformAutomator
PlatformAutomator
Alternative name for platform. Both provide the same functionality.
tester
WidgetTester
The underlying Flutter WidgetTester instance. Use this when you need direct access to Flutter’s testing APIs.
config
PatrolTesterConfig
Configuration object controlling timeouts, settle policies, and logging behavior.

Methods

Finder Methods

$(dynamic matching)
PatrolFinder
Creates a custom Patrol finder. This is a callable method that makes the tester instance itself callable.Accepts: Type, Key, Symbol, String, Pattern, IconData, Widget, Finder, or PatrolFinder.
// Find by widget type
$(ElevatedButton)

// Find by key (using Symbol)
$(#loginButton)

// Find by text
$('Submit')

// Find by icon
$(Icons.add)
log(String message)
void
Logs a message to the Patrol log output.
$.log('Starting login flow');

Widget Interaction Methods

tap(Finder finder, {...})
Future<void>
Waits for a widget to be visible and taps it.Parameters:
  • settlePolicy - How to pump frames after tapping
  • visibleTimeout - How long to wait for visibility
  • settleTimeout - Timeout for settling
  • alignment - Where to tap within the widget
await $.tap($(#submitButton));
longPress(Finder finder, {...})
Future<void>
Waits for a widget to be visible and performs a long press.
await $.longPress($(#deleteButton));
enterText(Finder finder, String text, {...})
Future<void>
Waits for a text field to be visible and enters text.
await $.enterText($(TextField).first, '[email protected]');

Waiting Methods

waitUntilVisible(Finder finder, {...})
Future<PatrolFinder>
Waits until the finder locates at least one visible widget.Parameters:
  • timeout - Maximum time to wait (defaults to config.visibleTimeout)
  • alignment - Point to check for visibility
await $.waitUntilVisible($(Text('Welcome')));
waitUntilExists(Finder finder, {...})
Future<PatrolFinder>
Waits until the finder locates at least one widget (doesn’t need to be visible).
await $.waitUntilExists($(Text('Hidden')));

Scrolling Methods

scrollUntilVisible({required Finder finder, ...})
Future<PatrolFinder>
Scrolls a view until the target widget becomes visible.Parameters:
  • finder - The widget to scroll to
  • view - The scrollable widget (defaults to first Scrollable)
  • delta - Distance to scroll per iteration
  • scrollDirection - Direction to scroll
  • maxScrolls - Maximum scroll attempts
await $.scrollUntilVisible(
  finder: $(Text('Bottom Item')),
  view: $(ListView),
);
scrollUntilExists({...})
Future<PatrolFinder>
Similar to scrollUntilVisible, but scrolls until the widget exists (not necessarily visible).

Pump Methods

pumpWidget(Widget widget, [...])
Future<void>
Renders a widget in the test environment. See WidgetTester.pumpWidget.
pumpWidgetAndSettle(Widget widget, {...})
Future<void>
Renders a widget and waits for all animations to complete.
await $.pumpWidgetAndSettle(MyApp());
pump([Duration? duration])
Future<void>
Triggers a single frame. See WidgetTester.pump.
pumpAndSettle({...})
Future<void>
Repeatedly pumps frames until there are no more pending frames.
pumpAndTrySettle({...})
Future<void>
Like pumpAndSettle, but doesn’t throw if it times out (useful for infinite animations).

Usage Examples

Basic Widget Testing

patrolTest('login form validation', ($) async {
  await $.pumpWidgetAndSettle(MyApp());
  
  // Enter email
  await $(#emailField).enterText('[email protected]');
  
  // Enter password
  await $(#passwordField).enterText('password123');
  
  // Submit
  await $(#loginButton).tap();
  
  // Verify success
  await $(Text('Welcome')).waitUntilVisible();
});

Scrolling to Elements

patrolTest('scroll to item in list', ($) async {
  await $.pumpWidgetAndSettle(MyApp());
  
  // Scroll to find a specific item
  await $.scrollUntilVisible(
    finder: $(Text('Item 50')),
    view: $(ListView),
    delta: 100,
  );
  
  await $(Text('Item 50')).tap();
});

Combined Flutter + Native Testing

patrolTest('handle system permission dialog', ($) async {
  await $.pumpWidgetAndSettle(MyApp());
  
  // Tap button in Flutter app
  await $('Request Location').tap();
  
  // Handle native permission dialog
  await $.platform.mobile.grantPermissionWhenInUse();
  
  // Verify permission granted in Flutter
  await $(Text('Location Enabled')).waitUntilVisible();
});

Using Custom Configuration

patrolTest(
  'test with custom config',
  ($) async {
    // Custom timeout for this specific wait
    await $(Text('Slow Widget')).waitUntilVisible(
      timeout: Duration(seconds: 30),
    );
    
    // Don't settle after tap (useful with infinite animations)
    await $(#animatedButton).tap(
      settlePolicy: SettlePolicy.noSettle,
    );
  },
  config: PatrolTesterConfig(
    visibleTimeout: Duration(seconds: 20),
    settlePolicy: SettlePolicy.trySettle,
  ),
);

Logging

patrolTest('test with logging', ($) async {
  $.log('Starting test');
  await $.pumpWidgetAndSettle(MyApp());
  
  $.log('Attempting login');
  await $(#loginButton).tap();
  
  $.log('Verifying success');
  await $(Text('Dashboard')).waitUntilVisible();
});

See Also

Build docs developers (and LLMs) love