← back to blog

how to test Flutter apps on real cloud Android devices

May 06, 2026

how to test Flutter apps on real cloud Android devices

if you build with Flutter and need to run integration tests on real Android hardware, cloud phones are the most flexible option. Flutter has three official test layers (unit, widget, integration), and the integration layer specifically wants a real or emulated device. cloud phones connected over ADB-over-network appear identical to USB-attached hardware to the Flutter toolchain, so flutter drive, flutter test integration_test/, Patrol, and Maestro all work without any platform-specific code.

this guide covers the full path: configure your Flutter project for integration tests, connect to a cloudf.one cloud phone, run the standard integration_test package, layer Patrol or Maestro on top for native-side gestures, and parallelize across a Singapore device pool.

why Flutter integration tests need real devices

three categories of Flutter bugs appear only on real arm64 hardware. native plugin glue (camera, biometric, in-app purchase, FCM push) often misbehaves on emulators. native rendering edge cases (impeller engine bugs, vsync stalls, touch latency) only reproduce on real GPUs. platform channels that talk to OEM-specific services (Samsung Knox, Xiaomi MIUI quirks) only resolve on the real OEM Android.

cloud phones cover all three because they are real Samsung Android phones running real arm64 builds.

for cloud phone basics, see cloud phone vs physical Android device and how to set up ADB on cloudf.one.

prerequisites

your Flutter project should be on Flutter 3.16 or newer. on your laptop or CI: Java 17, Android platform-tools, Flutter SDK with Android toolchain configured.

flutter doctor   # should show all green for Android

add the integration_test package if not already there:

# pubspec.yaml
dev_dependencies:
  integration_test:
    sdk: flutter
  flutter_test:
    sdk: flutter

step 1: connect to your cloud phone

adb connect adb-sg.cloudf.one:5555
adb devices

confirm the cloud phone shows up. then check Flutter sees it:

flutter devices

you should see the cloud phone listed alongside any other connected devices. Flutter detects ADB-over-network devices the same way it detects USB ones.

step 2: write your first integration test

create integration_test/login_test.dart:

import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  group('login flow', () {
    testWidgets('logs in with valid credentials', (tester) async {
      app.main();
      await tester.pumpAndSettle();

      expect(find.byKey(const Key('loginScreen')), findsOneWidget);

      await tester.enterText(find.byKey(const Key('usernameField')), 'test');
      await tester.enterText(find.byKey(const Key('passwordField')), 'test123');
      await tester.tap(find.byKey(const Key('loginButton')));
      await tester.pumpAndSettle();

      expect(find.text('Welcome, test'), findsOneWidget);
    });
  });
}

run it on the cloud phone:

flutter test integration_test/login_test.dart \
  -d adb-sg.cloudf.one:5555

Flutter builds, installs, and runs the integration test on the cloud phone. results appear in the terminal.

step 3: capture screenshots on failure

add screenshot capture to your test:

testWidgets('logs in', (tester) async {
  app.main();
  await tester.pumpAndSettle();
  await IntegrationTestWidgetsFlutterBinding.instance
      .takeScreenshot('login_screen');
  // ... rest of test
});

screenshots land in your Flutter project’s screenshots/ folder by default. for cloud-phone-driven CI, our CI/CD integration guide covers artifact upload.

step 4: layer Patrol for native gestures

integration_test is great for Flutter widget interactions but cannot drive native UI: notification trays, system dialogs, permissions prompts, biometric prompts. Patrol extends Flutter integration tests with native automation by speaking to UiAutomator2 underneath.

# install Patrol CLI
dart pub global activate patrol_cli

# add to pubspec.yaml
# dev_dependencies:
#   patrol: ^3.0.0

write a Patrol test:

import 'package:patrol/patrol.dart';

void main() {
  patrolTest('grants permission and uses camera', ($) async {
    await $.pumpWidgetAndSettle(MyApp());
    await $('Camera').tap();

    // native permission prompt
    await $.native.grantPermissionWhenInUse();

    // back to Flutter UI
    await $.tap(find.text('Take photo'));
  });
}

run on the cloud phone:

patrol test --target integration_test/camera_test.dart \
  -d adb-sg.cloudf.one:5555

step 5: alternative path with Maestro

if you do not want to write Dart test code at all, Maestro drives Flutter apps via UI taps the same way it drives native apps. our Maestro tutorial covers the full setup. a Flutter-specific Maestro flow is identical to a native-Android Maestro flow.

appId: com.example.flutterapp
---
- launchApp
- tapOn: "Get started"
- inputText: "test"
- tapOn: "Log in"
- assertVisible: "Welcome, test"

run:

maestro --device adb-sg.cloudf.one:5555 test flows/login.yaml

Flutter widgets that have semantic labels (the default for most Material widgets) are tappable by Maestro without any extra code.

step 6: parallelize across cloud phones

Flutter integration tests do not have a built-in parallel runner. for parallelism, use the standard Flutter test sharding plus multiple cloud phones.

# shard 3 ways across 3 cloud phones
adb connect adb-sg.cloudf.one:5555
adb connect adb-sg.cloudf.one:5556
adb connect adb-sg.cloudf.one:5557

flutter test integration_test/ \
  -d adb-sg.cloudf.one:5555 \
  --total-shards 3 --shard-index 0 &

flutter test integration_test/ \
  -d adb-sg.cloudf.one:5556 \
  --total-shards 3 --shard-index 1 &

flutter test integration_test/ \
  -d adb-sg.cloudf.one:5557 \
  --total-shards 3 --shard-index 2 &

wait

cloudf.one’s REST API gives you a list of locked devices; map to shards in CI.

handling Flutter on slow networks

ADB-over-network adds 150 to 250 ms latency from outside Singapore. Flutter integration tests do many small ADB calls (pump, settle, screenshot), so the latency multiplies. for tight CI windows, run from an Asia-Pacific runner.

for debugging slow tests, see our latency explainer.

common pitfalls

three issues catch teams new to Flutter on cloud phones.

first, hot reload over network. flutter run with hot reload works over ADB-over-network but is slower than USB. for active development, USB is fastest. for CI, ADB-over-network is fine because hot reload is not used.

second, plugin native code crashes. some plugins (especially older ones using deprecated Android Camera API) crash on Samsung Galaxy A-series. cloud phones in cloudf.one are Samsung A-series; if your plugin breaks, swap to a more current plugin or test on multiple OEMs.

third, vsync timing. Flutter’s pump / settle relies on vsync from the device. real cloud phones produce stable vsync; emulators sometimes produce noisy vsync. cloud phones are the more reliable target.

try Flutter integration tests on a real Singapore cloud phone

register for a free trial for a one-hour cloud phone token. install Flutter, connect to the cloud phone, run a single integration_test. once confirmed, scale to a paid plan and add cloud phones to your e2e fleet.

frequently asked questions

does flutter run work over ADB-over-network?

yes. flutter run -d adb-sg.cloudf.one:5555 works exactly like a USB device. hot reload is slower over network than USB but functional.

can I test Flutter web on cloud phones?

cloud phones run Android, so you can test Flutter web by opening Chrome on the cloud phone and pointing it at your web build. this is mobile-Chrome testing more than Flutter-specific testing. for desktop Flutter web, use desktop Chrome with Selenium.

what about Flutter for iOS?

cloudf.one is Android-first. for Flutter iOS integration tests, use Firebase Test Lab or AWS Device Farm. our comparison post covers when each makes sense.

does Patrol work with Hermes or impeller?

yes. Patrol speaks to the device’s native automation layer (UiAutomator2 on Android), which is independent of the Flutter rendering engine. impeller and Hermes (Flutter does not use Hermes) work identically.

how do I capture Flutter logs in CI?

flutter test --reporter expanded produces structured output. capture stdout to a log file. the Flutter integration test docs cover advanced reporters.