Part of flutter-skill — AI-Powered End-to-End Testing for Any App
Bridge SDK that lets flutter-skill automate React Native apps. Runs an embedded HTTP + WebSocket server inside the app process so AI agents can inspect the UI, tap buttons, enter text, scroll, and more.
npm install flutter-skill-react-native react-native-tcp-socket
# or
yarn add flutter-skill-react-native react-native-tcp-socketFor iOS, install the native pods:
cd ios && pod install// App.js
import { initFlutterSkill, setNavigationRef } from 'flutter-skill-react-native';
import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
export default function App() {
const navigationRef = useNavigationContainerRef();
useEffect(() => {
if (__DEV__) {
initFlutterSkill({ appName: 'MyApp' });
setNavigationRef(navigationRef);
}
}, []);
return (
<NavigationContainer ref={navigationRef}>
{/* ... */}
</NavigationContainer>
);
}The SDK needs to know about your interactive elements. Register them by testID:
import { registerComponent, unregisterComponent } from 'flutter-skill-react-native';
function LoginScreen() {
return (
<View>
<TextInput
testID="email_field"
ref={ref => registerComponent('email_field', ref, {
type: 'TextInput',
accessibilityLabel: 'Email',
})}
/>
<TouchableOpacity
testID="login_button"
ref={ref => registerComponent('login_button', ref, {
type: 'TouchableOpacity',
text: 'Log In',
interactive: true,
})}
onPress={handleLogin}
>
<Text>Log In</Text>
</TouchableOpacity>
</View>
);
}# Start the flutter-skill server -- it will discover the app on port 18118
flutter_skill server
# Or scan and connect directly
flutter_skill scan- Include the SDK in your React Native app and call
initFlutterSkill(). - The SDK starts a TCP server on port 18118 inside the app process.
- The health check endpoint at
GET /.flutter-skilladvertises the app to the flutter-skill proxy. - The proxy connects via WebSocket at
/wsand sends JSON-RPC 2.0 requests. - The SDK executes commands (tap, inspect, enter text, etc.) against the live React Native component tree.
flutter-skill server
|
v JSON-RPC 2.0 over WebSocket (port 18118)
FlutterSkill.js (embedded in app)
|
v React Native APIs
UIManager / findNodeHandle / Component refs
| Method | Description |
|---|---|
initialize |
Return framework info and SDK version |
inspect |
List all registered interactive elements with positions |
tap |
Tap an element by testID, text, or accessibility label |
enter_text |
Set text on a TextInput component |
swipe |
Dispatch swipe gesture in a direction |
scroll |
Scroll a ScrollView or FlatList |
find_element |
Check if an element exists and get its bounds |
get_text |
Get text content or input value |
wait_for_element |
Check element presence (proxy polls) |
screenshot |
Delegates to native tooling (returns _needs_native flag) |
| Method | Description |
|---|---|
get_logs |
Retrieve captured console output (max 500 entries) |
clear_logs |
Clear the log buffer |
get_route |
Get current React Navigation route name and params |
go_back |
Navigate back via React Navigation |
Elements are found by (in priority order):
keyortestID-- matches thetestIDprop registered viaregisterComponent()text-- matches thetextstring passed during registrationaccessibilityLabel-- matches the accessibility label
{ "method": "tap", "params": { "key": "login_button" } }
{ "method": "enter_text", "params": { "key": "email_field", "text": "user@example.com" } }
{ "method": "find_element", "params": { "text": "Log In" } }
{ "method": "scroll", "params": { "key": "main_list", "direction": "down", "distance": 500 } }
{ "method": "get_route" }Start the bridge server.
| Option | Type | Default | Description |
|---|---|---|---|
appName |
string | 'ReactNativeApp' |
App name shown in health check |
port |
number | 18118 |
TCP port to listen on |
Shut down the server and disconnect all clients.
Register a component so the SDK can find and interact with it.
| Param | Type | Description |
|---|---|---|
testID |
string | Unique identifier (should match the testID prop) |
ref |
React ref | The component ref |
extras |
object | Optional: { type, text, accessibilityLabel, interactive } |
Remove a component from the registry (call on unmount).
Set the React Navigation container ref for get_route and go_back support.
Set the root component ref for swipe and scroll fallbacks.
The SDK runs entirely in the JavaScript thread. No native modules are required beyond react-native-tcp-socket for networking.
+---------------------------+
| React Native App |
| |
| FlutterSkill.js |
| +------------------+ |
| | TCP Server :18118| |
| | GET /.flutter- | |
| | skill | | <--- Health check (HTTP)
| | WS /ws | | <--- JSON-RPC 2.0 (WebSocket)
| +------------------+ |
| | Component | |
| | Registry | | <--- testID -> ref mapping
| +------------------+ |
| | Console Capture | | <--- Intercepts console.log/warn/error
| +------------------+ |
+---------------------------+
- React Native 0.68+ (both Old Architecture and New Architecture / Fabric)
- iOS and Android
- Expo (with development builds that include
react-native-tcp-socket) - React Navigation 5+ for route detection
- Development only: Wrap
initFlutterSkill()inif (__DEV__)to avoid shipping the bridge in production. - Screenshots: The SDK returns
{ _needs_native: true }for screenshots. The flutter-skill proxy handles this viaxcrun simctl screenshot(iOS) oradb screencap(Android). - Element registration: Unlike the web SDK which can query the DOM, React Native requires explicit registration of interactive elements. Use
registerComponent()on elements you want the agent to interact with. - Port conflicts: If port 18118 is in use, pass a custom port via
initFlutterSkill({ port: 18119 }). The proxy scans ports 18118-18128.