Skip to main content

u2_flutter 🚀

PyPI version Python versions License

u2_flutter is a lightweight Python plugin for uiautomator2 designed to inspect, locate, and interact with Flutter widgets in Android applications.

It connects directly to the Dart VM Service over a WebSocket connection using ADB port forwarding, sending JSON-RPC commands directly to Flutter's Driver extension (ext.flutter.driver).


✨ Key Features

  • Direct WebSocket Connection: Bypasses heavy HTTP/Node.js middleware (e.g., Appium) by communicating directly with the Dart VM Service over WebSocket.
  • Fluent Element API: Easily locate widgets by Key, Type, Text, or Value and perform actions like tap(), enter_text(), and text.
  • Automatic Bridge Lifecycle: Managed @with_flutter decorator handles dynamic logcat scanning, port forwarding, and connection lifecycle automatically.
  • Fast Diagnostics Tree Inspection: Supports fetching and caching the Flutter widget diagnostics tree (get_diagnostics_tree()) for fast offline or precondition checks (<1ms).
  • Hybrid App Compatibility: Works seamlessly alongside standard uiautomator2 native Android interactions (self.d for native, self.flutter for Flutter).

🛠️ How it Works

graph TD
    A[Python Test Script / uiautomator2] -->|1. @with_flutter| B(FlutterBridge)
    B -->|2. scans logcat| C{Find VM Port & Auth Token}
    C -->|3. adb forward| D[Local Port tcp:8181]
    D -->|4. WebSocket Handshake| E[Dart VM Service WebSocket]
    A -->|5. self.flutter.find_by_key| F(FlutterDriver)
    F -->|6. JSON-RPC ext.flutter.driver| E
    E -->|7. Executes action| G[Running Flutter Application]
  1. Service Registration: The Flutter app is built in debug mode with enableFlutterDriverExtension() inside lib/main.dart.
  2. Dynamic Handshake: u2_flutter scans logcat for the active Dart VM Service port and security authentication token.
  3. Port Forwarding: Automatically sets up adb forward tcp:8181 tcp:<vm_port>.
  4. WebSocket Communication: Establishes a WebSocket connection (ws://127.0.0.1:8181/<token>/ws) to execute driver commands directly on the running isolate.

📦 Installation

pip install u2-flutter

🚀 Quick Start (Basic Usage with uiautomator2)

1. Enable Flutter Driver Extension in your App

Inside your Flutter app's lib/main.dart:

import 'package:flutter/material.dart';
import 'package:flutter_driver/driver_extension.dart';

void main() {
  enableFlutterDriverExtension(); // Registers ext.flutter.driver
  runApp(const MyApp());
}

Add flutter_driver to pubspec.yaml:

dev_dependencies:
  flutter_driver:
    sdk: flutter

Build the debug APK:

flutter build apk --debug

2. Write Python Automation Script

import unittest
import uiautomator2 as u2
from u2_flutter import with_flutter

class FlutterAppTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.d = u2.connect()  # Connect uiautomator2 device instance

    @with_flutter
    def test_login_flow(self):
        # 1. Native uiautomator2 interaction
        self.d(text="OPEN FLUTTER").click()

        # 2. u2_flutter widget interactions
        self.flutter.find_by_key("username_input").enter_text("testuser")
        self.flutter.find_by_key("submit_btn").tap()

        # 3. Read updated widget state
        greeting = self.flutter.find_by_key("greeting_text").text
        self.assertEqual(greeting, "Hello, testuser!")

if __name__ == "__main__":
    unittest.main()

🔌 Using with Kea2 Fuzzing Framework

u2_flutter also serves as the official Flutter interaction plugin for Kea2 (property-based fuzzing testing for Android).

Example Property Test

import unittest
import logging
from kea2 import precondition, prob
from u2_flutter import with_flutter

logger = logging.getLogger(__name__)

class TestHybridApp(unittest.TestCase):

    @with_flutter
    @prob(1.0)
    @precondition(lambda self: self.d(text="OPEN FLUTTER").exists)
    def test_native_to_flutter_flow(self):
        # Native click
        self.d(text="OPEN FLUTTER").click()

        # Flutter widget checks & actions
        assert self.flutter.find_by_key("HomeListView").exists
        
        before_text = self.flutter.find_by_key("statusText").text
        before_count = int(before_text.split(":")[1].strip())

        self.flutter.find_by_key("actionButton").tap()

        after_text = self.flutter.find_by_key("statusText").text
        after_count = int(after_text.split(":")[1].strip())

        assert after_count == before_count + 1

⚡ Performance & Comparison

Compared to Appium Flutter Driver, u2_flutter follows the lean uiautomator2 design philosophy:

Metric Appium Flutter Driver u2_flutter
Transport HTTP → WebDriver → Node.js → ADB → Dart VM Direct WebSocket over ADB Forward
Network Hops 4+ hops 1 hop (Local WebSocket)
Middleware Appium Server (Node.js) required None (pure Python)
Widget Query Latency >150ms per request <1ms (cached diagnostics tree)
Setup Complexity High (npm, appium server, drivers) Minimal (pip install u2-flutter)

🤝 Acknowledgments


📜 License

MIT License © Muhammad Assad Ullah

Metadata

Release files for u2-flutter 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for u2-flutter 0.1.0
File Size Uploaded
u2_flutter-0.1.0.tar.gz 16.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for u2-flutter 0.1.0
File Interpreter ABI Platform
u2_flutter-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 30.5 kB

Release files / u2_flutter-0.1.0.tar.gz

Download URL u2_flutter-0.1.0.tar.gz
Size 16.9 kB
Tags Source
SHA-256 checksum
How to use checksums
8754116f727e907e0a210d7e6aa3b08318b327169f017e9295f3c536dce1a1e7
BLAKE2b-256 checksum
How to use checksums
8f933b5cbf98fa83285ce80ec2469c12ee2a9341d27b3c85dad1b7daf375fe2f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.15.0rc2

Release files / u2_flutter-0.1.0-py3-none-any.whl

Download URL u2_flutter-0.1.0-py3-none-any.whl
Size 13.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
88fb28c2258fa601940126eac7d80df7ca610ce432076dfea272552d76932b7c
BLAKE2b-256 checksum
How to use checksums
3e7e8ab2e3445f0a7b9316b954fbf06b3fc116ee09bd65e02b1cb47c33a04746
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.15.0rc2

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page