Learn how to use Patrol’s native automation API to interact with device settings, permissions, notifications, and native UI
Once native automation is set up, you can interact with the OS through the $.platform API. This guide covers all major use cases with real examples from the source code.
Tap native views like buttons in WebViews or system dialogs:
// Tap by textawait $.platform.mobile.tap(Selector(text: 'Sign up for newsletter'));// Tap by content description (accessibility label)await $.platform.mobile.tap( Selector(contentDescription: 'Submit button'),);// Tap by resource IDawait $.platform.mobile.tap( Selector(resourceId: 'com.example:id/submit'),);
// Enter text by selectorawait $.platform.mobile.enterText( Selector(text: 'Enter your email'), text: '[email protected]',);// Enter text in the nth visible text field (index starts at 0)await $.platform.mobile.enterTextByIndex('username', index: 0);await $.platform.mobile.enterTextByIndex('password', index: 1);
Text fields on Android include EditText and AutoCompleteTextView. On iOS, they include TextField and SecureTextField.
// Swipe from one point to anotherawait $.platform.mobile.swipe( from: Offset(0.5, 0.8), to: Offset(0.5, 0.2), steps: 12, // Controls speed (Android));// Swipe back gesture (left to right)await $.platform.mobile.swipeBack(dy: 0.5);// Pull to refreshawait $.platform.mobile.pullToRefresh( from: Offset(0.5, 0.5), to: Offset(0.5, 0.9), steps: 50,);
On Android, steps controls swipe speed (1 step = 5ms). Increase steps for slower, smoother swipes.
// Request permission in your appawait $('Enable Location').tap();// Handle the native dialogawait $.platform.mobile.grantPermissionWhenInUse();// Or deny permissionawait $.platform.mobile.denyPermission();// Or grant one-time permission (Android 11+, iOS)await $.platform.mobile.grantPermissionOnlyThisTime();
// Tap notification by index (0-based)await $.platform.mobile.tapOnNotificationByIndex(0); // First notificationawait $.platform.mobile.tapOnNotificationByIndex(1); // Second notification// Tap notification by contentawait $.platform.mobile.tapOnNotificationBySelector( Selector(textContains: 'Someone liked your post'),);// With custom timeoutawait $.platform.mobile.tapOnNotificationBySelector( Selector(text: 'New message'), timeout: Duration(seconds: 5),);
Notification shade must be opened with openNotifications() before tapping notifications.
// Get first notificationfinal notification = await $.platform.mobile.getFirstNotification();print('Title: ${notification.title}');print('Content: ${notification.content}');// Get all visible notificationsfinal notifications = await $.platform.mobile.getNotifications();for (var notif in notifications) { print('Notification: ${notif.title}');}
// Close notification shadeawait $.platform.mobile.closeNotifications();// iOS: Close heads-up notification (banner at top)await $.platform.ios.closeHeadsUpNotification();
// Enable dark modeawait $.platform.mobile.enableDarkMode();// Disable dark mode (light mode)await $.platform.mobile.disableDarkMode();// For specific app (optional)await $.platform.mobile.enableDarkMode(appId: 'com.example.otherapp');
// Press home buttonawait $.platform.mobile.pressHome();// Open your app (or specific app by ID)await $.platform.mobile.openApp();await $.platform.mobile.openApp(appId: 'com.example.otherapp');// Press recent apps / app switcherawait $.platform.mobile.pressRecentApps();// Android: Double press recent apps (switches to previous app)await $.platform.android.pressDoubleRecentApps();// Open quick settings (Android) or Control Center (iOS)await $.platform.mobile.openQuickSettings();
// Press back buttonawait $.platform.android.pressBack();// Open a specific Android appawait $.platform.android.openPlatformApp( androidAppId: 'com.android.settings',);
On iOS, to interact with UI elements in other apps, you must specify the app’s bundle ID:
// Take photo with default selectorsawait $.platform.mobile.takeCameraPhoto();// With custom selectors for shutter and done buttonsawait $.platform.mobile.takeCameraPhoto( shutterButtonSelector: Selector(resourceId: 'com.android.camera:id/shutter'), doneButtonSelector: Selector(text: 'OK'), timeout: Duration(seconds: 5),);
// Pick single image (first image by default)await $.platform.mobile.pickImageFromGallery();// Pick specific image by indexawait $.platform.mobile.pickImageFromGallery(index: 2);// Pick with custom selectorawait $.platform.mobile.pickImageFromGallery( imageSelector: Selector(contentDescription: 'Photo taken on Jan 1'),);// Pick multiple imagesawait $.platform.mobile.pickMultipleImagesFromGallery( imageIndexes: [0, 2, 4], // First, third, and fifth images);
// Check if running on emulator/simulatorfinal isVirtual = await $.platform.mobile.isVirtualDevice();if (isVirtual) { print('Running on emulator/simulator');} else { print('Running on real device');}
// Get OS version as integerfinal osVersion = await $.platform.mobile.getOsVersion();if (osVersion >= 30) { // Android 11+ or iOS 14+ specific behavior await $.platform.mobile.grantPermissionOnlyThisTime();} else { await $.platform.mobile.grantPermissionWhenInUse();}
// Tap by textawait $.platform.web.tap(WebSelector(text: 'Submit'));// Tap by CSS selectorawait $.platform.web.tap(WebSelector(cssOrXpath: 'css=#submit-button'));// Tap by XPathawait $.platform.web.tap(WebSelector(cssOrXpath: 'xpath=//button[@id="submit"]'));// Tap by test IDawait $.platform.web.tap(WebSelector(testId: 'login-button'));// Enter textawait $.platform.web.enterText( WebSelector(placeholder: 'Email address'), text: '[email protected]',);// Scroll to elementawait $.platform.web.scrollTo(WebSelector(text: 'Load more'));
// Tap element inside an iframeawait $.platform.web.tap( WebSelector(text: 'Submit Payment'), iframeSelector: WebSelector(cssOrXpath: 'css=#payment-iframe'),);
// Wait for native element to become visibleawait $.platform.mobile.waitUntilVisible( Selector(text: 'Loading complete'), timeout: Duration(seconds: 10),);