Debug Flutter, React Native & Android apps — network, state, logs, storage, database — all in one beautiful desktop tool.
Features · Download · Quick Start · Why DevConnect? · SDKs
If you've used Reactotron, Flipper, or Flutter DevTools — you know they're powerful but limited to one framework. DevConnect is a single desktop app that works with all of them.
| DevConnect | Reactotron | Flipper | Flutter DevTools | |
|---|---|---|---|---|
| Flutter support | ✅ | ❌ | ✅ | |
| React Native support | ✅ | ✅ | ✅ | ❌ |
| Android Native support | ✅ | ❌ | ✅ | ❌ |
| Network inspector | ✅ | ✅ | ✅ | ✅ |
| State debugging | ✅ Redux/MobX/Zustand/BLoC/Riverpod/GetX | ✅ Redux/MobX | ✅ | ❌ |
| Log viewer | ✅ | ✅ | ✅ | ✅ |
| Storage viewer | ✅ | ✅ | ✅ | ❌ |
| Database browser | ✅ | ❌ | ✅ | ❌ |
| Performance profiling | ✅ FPS/CPU/Memory/Jank | ❌ | ❌ | ✅ |
| Memory leak detection | ✅ | ❌ | ❌ | ✅ |
| Benchmark timing | ✅ | ✅ | ❌ | ✅ |
| Custom commands | ✅ | ✅ | ❌ | ❌ |
| Multi-device | ✅ | ❌ | ✅ | ❌ |
| Zero-config setup | ✅ auto-detect | ❌ manual | ||
| macOS + Windows | ✅ | ✅ | ✅ | |
| Dark + Light theme | ✅ | ✅ | ✅ | ✅ |
| Active maintenance | ✅ | ❌ deprecated | ✅ |
TL;DR — One tool to replace Reactotron + Flipper + DevTools. Works with Flutter, React Native, and Android Native. Auto-detects everything.
- Network Inspector — HTTP request/response viewer with headers, body (tree + JSON), timing bar, copy as cURL, status badges
- State Inspector — Real-time state change timeline with before/after diff for Redux, MobX, Zustand, Jotai, Valtio, XState, BLoC, Riverpod, GetX, Provider, ViewModel, StateFlow, LiveData
- Console / Logs — Log viewer with level filters (debug/info/warn/error), search, tags, metadata, stack traces
- Storage Viewer — Browse and monitor SharedPreferences, AsyncStorage, Hive, MMKV, SecureStorage, DataStore
- Database Browser — SQLite, Drift, Room, Isar table viewer with SQL query editor
- Performance Profiling — Real-time FPS, CPU, memory usage charts with jank frame detection
- Memory Leak Detection — Detect undisposed controllers, streams, timers, growing collections with severity levels and stack traces
- Benchmark — Performance timing with step markers
- Custom Commands — Send commands from desktop to app and get results
- Multi-Device — Connect multiple apps simultaneously, per-device filtering
- All Events — Unified timeline of all events across features
- Screenshot — Full-content screenshot capture of any detail panel
- ADB Reverse — One-click USB connection for Android devices
- Auto-detect — SDK auto-discovers desktop IP, zero configuration needed
- Dual Theme — Dark and light mode
| Platform | File | Architecture |
|---|---|---|
| macOS | DevConnect-macOS-v1.0.0-universal.dmg |
arm64 + x86_64 |
| Windows | DevConnect-Windows-v1.0.0.zip |
x64 |
Download from Releases.
import 'package:devconnect_flutter/devconnect_flutter.dart';
void main() async {
await DevConnect.initAndRunApp(
appName: 'MyApp',
runApp: () => runApp(const MyApp()),
);
// Done. Network + logs auto-captured.
}import { DevConnect } from 'devconnect-react-native';
await DevConnect.init({ appName: 'MyApp' });
// Done. fetch + XHR + console auto-captured.// Application.onCreate()
DevConnect.init(context = this, appName = "MyApp")That's it. Open DevConnect desktop, run your app, and everything appears.
| Platform | File | Architecture |
|---|---|---|
| macOS | DevConnect-macOS-v1.0.0-universal.dmg |
arm64 + x86_64 |
| Windows | DevConnect-Windows-v1.0.0.zip |
x64 |
Download from Releases.
git clone https://github.com/ridelinktechs/devconnect.git
cd devconnect
flutter pub get
dart run build_runner build --delete-conflicting-outputs
flutter build macos --release # macOS
flutter build windows --release # Windows
# Output: build/macos/Build/Products/Release/DevConnect.app- Console/Logs - real-time log viewer, level filters, search, clear
- Network Inspector - request/response, headers, body, timing, copy cURL, copy response
- State Inspector - state change timeline, before/after diff, snapshot + restore
- Storage Viewer - SharedPreferences, AsyncStorage, Hive, MMKV, SecureStorage
- Database Viewer - SQLite, Drift, Room, Isar with query editor
- Performance Profiling - real-time FPS, CPU, memory charts with jank detection
- Memory Leak Detection - severity-based leak viewer with stack traces and metadata
- Benchmark - performance timing with steps
- Custom Commands - send commands from desktop to app
- Device Panel - connected devices with platform badge, OS version
- ADB Reverse - one-click for Android USB
- Port Config - change WebSocket port in Settings
- Auto-detect Host - SDK auto-finds desktop IP
- Dual Theme - dark / light
# From pub.dev (after published)
flutter pub add devconnect_flutterOr from GitHub:
# pubspec.yaml
dependencies:
devconnect_flutter:
git:
url: https://github.com/ridelinktechs/devconnect.git
path: client_sdks/devconnect_flutterimport 'package:devconnect_flutter/devconnect_flutter.dart';
void main() async {
// Auto-detect: captures all HTTP + logs automatically
await DevConnect.initAndRunApp(
appName: 'MyApp',
runApp: () => runApp(const MyApp()),
);
}await DevConnect.initAndRunApp(
appName: 'MyApp',
runApp: () => runApp(const MyApp()),
appVersion: '1.0.0',
host: null, // null = auto-detect, '192.168.1.100' = manual
port: 9090, // default: 9090
enabled: true, // false = disable (production)
autoInterceptHttp: true, // auto-capture all HTTP (default: true)
autoInterceptLogs: true, // auto-capture print/debugPrint (default: true)
);
initAndRunApp()already callsinit()+httpOverrides()+runZoned()internally. Only use manual setup if you need to control each step separately (e.g., custom HttpOverrides, custom Zone, or init without runApp).
void main() async {
// Step 1: Connect to DevConnect desktop (WebSocket)
await DevConnect.init(appName: 'MyApp');
// Step 2: Intercept ALL HTTP globally (http, Dio, Chopper, etc.)
// Skip if you already have a custom HttpOverrides
HttpOverrides.global = DevConnect.httpOverrides();
// Step 3: Capture print/debugPrint logs via Zone + run app
DevConnect.runZoned(() => runApp(const MyApp()));
}Auto-captured via HttpOverrides: http, Dio, Chopper, Retrofit, GraphQL, Firebase, OAuth2, gRPC-web, Image.network.
// Dio (optional, for extra detail)
dio.interceptors.add(DevConnect.dioInterceptor());
// GetX GetConnect
final connect = GetConnect();
connect.httpClient.addRequestModifier(DevConnect.getConnectModifier());
connect.httpClient.addResponseModifier(DevConnect.getConnectResponseModifier());Auto-captured via Zone: print, debugPrint, logger, talker, logging, fimber, simple_logger.
// Manual
DevConnect.log('User logged in');
DevConnect.debug('Token refreshed', tag: 'Auth');
DevConnect.warn('Rate limit approaching');
DevConnect.error('Payment failed', stackTrace: StackTrace.current.toString());
// Tagged logger
final logger = DevConnect.logger('AuthService');
logger.info('Login success');
// Loggy
final printer = DevConnect.loggyPrinter();// Riverpod
class DevConnectObserver extends ProviderObserver {
@override
void didUpdateProvider(ProviderBase p, Object? prev, Object? next, ProviderContainer c) {
DevConnect.reportStateChange(
stateManager: 'riverpod',
action: '${p.name ?? p.runtimeType} updated',
previousState: {'value': prev.toString()},
nextState: {'value': next.toString()},
);
}
}
// main.dart
runApp(
ProviderScope(
observers: [DevConnectObserver()],
child: const MyApp(),
),
);// BLoC
class DevConnectBlocObserver extends BlocObserver {
@override
void onChange(BlocBase bloc, Change change) {
super.onChange(bloc, change);
DevConnect.reportStateChange(
stateManager: 'bloc',
action: '${bloc.runtimeType} changed',
previousState: {'state': change.currentState.toString()},
nextState: {'state': change.nextState.toString()},
);
}
}
// main.dart
Bloc.observer = DevConnectBlocObserver();// Provider / ChangeNotifier
class MyModel extends ChangeNotifier {
String _name = '';
set name(String val) {
final prev = _name;
_name = val;
notifyListeners();
DevConnect.reportStateChange(
stateManager: 'provider',
action: 'name changed',
previousState: {'name': prev},
nextState: {'name': val},
);
}
}// GetX
class MyController extends GetxController {
final count = 0.obs;
void increment() {
final prev = count.value;
count.value++;
DevConnect.reportStateChange(
stateManager: 'getx',
action: 'increment',
previousState: {'count': prev},
nextState: {'count': count.value},
);
}
}// MobX
final counter = Observable(0);
autorun((_) {
DevConnect.reportStateChange(
stateManager: 'mobx',
action: 'counter changed',
nextState: {'counter': counter.value},
);
});// Signals
final observer = DevConnect.signalsObserver();
observer.observe(mySignal, 'counterSignal');// SharedPreferences
final reporter = DevConnect.sharedPreferencesReporter();
reporter.reportWrite('token', 'abc123');
reporter.reportRead('token', 'abc123');
reporter.reportDelete('token');
// Hive
final hiveReporter = DevConnect.hiveReporter();
hiveReporter.reportWrite('settings', {'darkMode': true});
// flutter_secure_storage
final secureReporter = DevConnect.secureStorageReporter();
secureReporter.reportWrite('token', 'secret');
secureReporter.reportRead('token', '***');
// MMKV
final mmkvReporter = DevConnect.mmkvReporter();
mmkvReporter.reportWrite('key', 'value');
mmkvReporter.reportRead('key', 'value');// Drift
final driftReporter = DevConnect.driftReporter();
driftReporter.reportQuery('SELECT * FROM users', results);
// Drift auto-intercept
final executor = DevConnect.driftQueryExecutor(innerExecutor);
// Isar
final isarReporter = DevConnect.isarReporter();
isarReporter.reportQuery('User', results);
isarReporter.reportPut('User', {'name': 'John'});// Report FPS
DevConnect.reportPerformanceMetric(
metricType: 'fps',
value: 58.5,
label: 'Main Thread FPS',
);
// Report memory usage (MB)
DevConnect.reportPerformanceMetric(
metricType: 'memory_usage',
value: 142.3,
label: 'Dart Heap',
);
// Report CPU usage (%)
DevConnect.reportPerformanceMetric(
metricType: 'cpu_usage',
value: 35.2,
);
// Report jank frame (build time in ms)
DevConnect.reportPerformanceMetric(
metricType: 'jank_frame',
value: 32.1,
label: 'Slow build in ListView',
);Available metric types: fps, frame_build_time, frame_raster_time, memory_usage, memory_peak, cpu_usage, jank_frame.
// Report undisposed controller
DevConnect.reportMemoryLeak(
leakType: 'undisposed_controller',
severity: 'warning',
objectName: 'AnimationController',
detail: 'AnimationController not disposed in ProfileScreen',
retainedSizeBytes: 2048,
stackTrace: StackTrace.current.toString(),
);
// Report growing collection
DevConnect.reportMemoryLeak(
leakType: 'growing_collection',
severity: 'critical',
objectName: 'eventCache',
detail: 'List grows unbounded — 15000 items, expected < 100',
retainedSizeBytes: 1200000,
metadata: {'currentSize': 15000, 'maxExpected': 100},
);Available leak types: undisposed_controller, undisposed_stream, undisposed_timer, undisposed_animation_controller, widget_leak, growing_collection, custom.
Severity levels: info, warning, critical.
DevConnect.benchmarkStart('loadHome');
await fetchUser();
DevConnect.benchmarkStep('loadHome');
await fetchPosts();
DevConnect.benchmarkStop('loadHome');DevConnect.registerCommand('clearCache', (args) {
return {'cleared': true};
});// Send a state snapshot to desktop
DevConnect.sendStateSnapshot(
stateManager: 'riverpod',
state: {'counter': 42, 'user': userMap},
);
// Handle state restore from desktop
DevConnect.onStateRestore = (state) {
ref.read(appStateProvider.notifier).restore(state);
};// Check connection status
if (DevConnect.isConnected) {
print('Connected to DevConnect desktop');
}
// Disconnect
await DevConnect.disconnect();MaterialApp(navigatorObservers: [DevConnect.navigationObserver()])
GoRouter(observers: [DevConnect.navigationObserver()])# From npm (after published)
yarn add devconnect-react-native
# or
npm install devconnect-react-nativeOr from GitHub:
yarn add github:ridelinktechs/devconnect#mainimport { DevConnect } from 'devconnect-react-native';
await DevConnect.init({ appName: 'MyApp' });
// Auto-captures: fetch, XHR, console.log/warn/errorawait DevConnect.init({
appName: 'MyApp',
appVersion: '1.0.0',
host: undefined, // undefined = auto-detect, '192.168.1.100' = manual
port: 9090, // default: 9090
enabled: __DEV__, // false in production
autoInterceptFetch: true,
autoInterceptXHR: true,
autoInterceptConsole: true,
});Auto-captured: fetch, XHR, axios, got, ky, superagent, apisauce, Apollo, urql, TanStack Query, SWR, RTK Query, ofetch, wretch, redaxios, Firebase, OAuth2.
// Axios (optional, for extra tagging)
import { setupAxiosInterceptor } from 'devconnect-react-native';
setupAxiosInterceptor(axios);Auto-captured: console.log, console.debug, console.info, console.warn, console.error, console.trace. Also auto-captures: consola, debug, tslog, signale (they use console internally).
// Manual
DevConnect.log('User logged in');
DevConnect.debug('Debug info', 'Auth');
DevConnect.warn('Warning');
DevConnect.error('Error', 'Tag', stackTrace);
// react-native-logs
import { devConnectTransport } from 'devconnect-react-native';
const log = logger.createLogger({ transport: [consoleTransport, devConnectTransport] });
// loglevel
import { patchLoglevel } from 'devconnect-react-native';
patchLoglevel(log);
// pino
import { pinoDevConnectTransport } from 'devconnect-react-native';
const logger = pino({}, pinoDevConnectTransport());
// winston
import { winstonDevConnectTransport } from 'devconnect-react-native';
// bunyan
import { bunyanDevConnectStream } from 'devconnect-react-native';
// Any custom logger
import { wrapLogger } from 'devconnect-react-native';
const wrapped = wrapLogger(myLogger, 'myLoggerName');// Redux - Classic createStore
import { createStore, applyMiddleware, compose } from 'redux';
import { devConnectReduxMiddleware } from 'devconnect-react-native';
import { DevConnect } from 'devconnect-react-native';
let store;
if (__DEV__) {
store = createStore(
rootReducer,
compose(applyMiddleware(thunkMiddleware, devConnectReduxMiddleware))
);
// Allow desktop to dispatch actions into the app
DevConnect.connectReduxStore(store);
} else {
store = createStore(rootReducer, applyMiddleware(thunkMiddleware));
}// Redux Toolkit
import { configureStore } from '@reduxjs/toolkit';
import { devConnectReduxMiddleware } from 'devconnect-react-native';
import { DevConnect } from 'devconnect-react-native';
const store = configureStore({
reducer: rootReducer,
middleware: (getDefault) =>
__DEV__
? getDefault().concat(devConnectReduxMiddleware)
: getDefault(),
});
if (__DEV__) DevConnect.connectReduxStore(store);// MobX
import { spy } from 'mobx';
import { setupMobxSpy } from 'devconnect-react-native';
if (__DEV__) {
setupMobxSpy(spy); // reports all observable changes
}// Zustand
import { create } from 'zustand';
import { devConnectMiddleware } from 'devconnect-react-native';
// Wrap your store creator with devConnectMiddleware
const useStore = create(
devConnectMiddleware(
(set) => ({
count: 0,
name: '',
increment: () => set((s) => ({ count: s.count + 1 })),
setName: (name: string) => set({ name }),
}),
'CounterStore' // label shown in DevConnect desktop
)
);// Jotai
import { atom, createStore } from 'jotai';
import { watchAtom } from 'devconnect-react-native';
const countAtom = atom(0);
const store = createStore();
if (__DEV__) {
// Watches atom and reports every value change
const unsub = watchAtom(store, countAtom, 'countAtom');
// unsub() to stop watching
}// Valtio
import { proxy } from 'valtio';
import { watchValtio } from 'devconnect-react-native';
const state = proxy({ count: 0, user: { name: '' } });
if (__DEV__) {
watchValtio(state, 'AppState'); // reports all proxy mutations
}// XState
import { interpret } from 'xstate';
import { devConnectXStateInspector } from 'devconnect-react-native';
const service = interpret(toggleMachine);
if (__DEV__) {
// Reports every state transition (event, from, to, context)
service.onTransition(devConnectXStateInspector('ToggleMachine'));
}
service.start();// AsyncStorage
import { DevConnectAsyncStorage } from 'devconnect-react-native';
DevConnectAsyncStorage.patchInPlace(AsyncStorage);
// MMKV
import { DevConnectMMKV } from 'devconnect-react-native';
DevConnectMMKV.wrap(storage);// Report FPS
DevConnect.reportPerformanceMetric({
metricType: 'fps',
value: 58.5,
label: 'JS Thread FPS',
});
// Report memory usage (MB)
DevConnect.reportPerformanceMetric({
metricType: 'memory_usage',
value: 142.3,
label: 'JS Heap',
});
// Report CPU usage (%)
DevConnect.reportPerformanceMetric({
metricType: 'cpu_usage',
value: 35.2,
});
// Report jank frame (ms)
DevConnect.reportPerformanceMetric({
metricType: 'jank_frame',
value: 32.1,
label: 'Slow render in FlatList',
});// Report undisposed subscription
DevConnect.reportMemoryLeak({
leakType: 'undisposed_stream',
severity: 'warning',
objectName: 'UserDataSubscription',
detail: 'EventEmitter listener not removed in ProfileScreen',
retainedSizeBytes: 2048,
stackTrace: new Error().stack,
});
// Report growing collection
DevConnect.reportMemoryLeak({
leakType: 'growing_collection',
severity: 'critical',
objectName: 'eventCache',
detail: 'Array grows unbounded — 15000 items, expected < 100',
retainedSizeBytes: 1200000,
metadata: { currentSize: 15000, maxExpected: 100 },
});const logger = DevConnect.logger('AuthService');
logger.log('User logged in');
logger.debug('Token refreshed');
logger.warn('Session expiring');
logger.error('Login failed', error.stack);// When auto-interception is disabled or for custom transports
const requestId = 'req-123';
DevConnect.reportNetworkStart({
requestId,
method: 'POST',
url: 'https://api.example.com/data',
headers: { 'Content-Type': 'application/json' },
body: { name: 'John' },
});
// After response
DevConnect.reportNetworkComplete({
requestId,
method: 'POST',
url: 'https://api.example.com/data',
statusCode: 200,
startTime: 1711180800000,
responseBody: { success: true },
});DevConnect.benchmark('loadUserData');
await fetchUser();
DevConnect.benchmarkStep('loadUserData', 'fetched user');
await fetchPosts();
DevConnect.benchmarkStop('loadUserData');DevConnect.registerCommand('clearCache', () => {
AsyncStorage.clear();
return { success: true };
});DevConnect.sendStateSnapshot('redux', store.getState());
DevConnect.onStateRestore((state) => {
store.dispatch({ type: 'RESTORE_STATE', payload: state });
});// Check connection status
if (DevConnect.isConnected()) {
console.log('Connected to DevConnect desktop');
}
// Disconnect
DevConnect.disconnect();// From Maven Central (after published)
dependencies {
implementation("com.ridelink:devconnect-android:1.0.0")
}Or from JitPack (GitHub):
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
maven { url = uri("https://jitpack.io") }
}
}
// app/build.gradle.kts
dependencies {
implementation("com.github.ridelinktechs.devconnect:devconnect-android:v1.0.0")
}Or AAR file from Releases:
dependencies {
implementation(files("libs/devconnect-android-1.0.0.aar"))
}class MyApp : Application() {
override fun onCreate() {
super.onCreate()
DevConnect.init(
context = this,
appName = "MyApp",
)
}
}DevConnect.init(
context = this,
appName = "MyApp",
appVersion = "1.0.0",
host = null, // null = auto-detect, "192.168.1.100" = manual
port = 9090, // default: 9090
enabled = BuildConfig.DEBUG, // false in release
autoInterceptLogs = true, // auto-capture println() (default: false)
)// OkHttp (captures Retrofit, Firebase, OAuth2, Glide, Coil)
val client = OkHttpClient.Builder()
.addInterceptor(DevConnect.okHttpInterceptor())
.build()
// Ktor
val client = HttpClient {
install(DevConnect.ktorPlugin())
}
// Volley
val stack = object : HurlStack() {
override fun createConnection(url: URL): HttpURLConnection {
return DevConnectHttpURLConnection.wrap(super.createConnection(url))
}
}// Drop-in replacement for android.util.Log
// Change: import android.util.Log -> import com.devconnect.interceptors.DCLog as Log
// All existing Log.d/i/w/e calls will send to both Logcat AND DevConnect
import com.devconnect.interceptors.DCLog as Log
Log.d("MyTag", "Hello") // -> Logcat + DevConnect
Log.e("MyTag", "Error", exception)
Log.w("MyTag", "Warning")// Timber - add DevConnect tree alongside DebugTree
import com.devconnect.interceptors.DevConnectTimberHelper
class DevConnectTree : Timber.Tree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
DevConnectTimberHelper.log(priority, tag, message, t)
}
}
// Application.onCreate()
Timber.plant(Timber.DebugTree()) // keep normal logcat
Timber.plant(DevConnectTree()) // add DevConnect reporting// Intercept all println() calls
DevConnectLogInterceptor.interceptSystemOut()// Kermit (KMP) - add DevConnect log writer
import co.touchlab.kermit.Logger
Logger.addLogWriter(DevConnect.kermitWriter())// Napier (KMP) - set DevConnect as antilog
import io.github.aakira.napier.Napier
Napier.base(DevConnect.napierAntilog())// Manual logging
DevConnect.sendLog("info", "User logged in", tag = "Auth")
DevConnect.sendLog("error", "Payment failed", tag = "Payment", stackTrace = e.stackTraceToString())// ViewModel + StateFlow
class MyViewModel : ViewModel() {
private val _state = MutableStateFlow(UserState())
val state = _state.asStateFlow()
fun updateName(name: String) {
val prev = _state.value
_state.value = prev.copy(name = name)
DevConnectViewModelObserver.reportStateUpdate(
viewModelName = "MyViewModel",
action = "updateName",
previousState = mapOf("name" to prev.name),
nextState = mapOf("name" to name),
)
}
}// Auto-observe StateFlow (reports every change automatically)
val observer = DevConnect.stateObserver()
observer.observe(viewLifecycleOwner.lifecycleScope, viewModel.state, "UserState")// Auto-observe LiveData
val observer = DevConnect.stateObserver()
observer.observe(viewLifecycleOwner, viewModel.userLiveData, "UserLiveData")// SharedPreferences
val reporter = DevConnect.sharedPrefsReporter()
reporter.reportWrite("token", "abc123")
reporter.reportRead("token", "abc123")
reporter.reportDelete("token")
// DataStore
val dsReporter = DevConnect.dataStoreReporter()
dsReporter.reportWrite("darkMode", true)
dsReporter.reportRead("darkMode", true)
// MMKV
val mmkvReporter = DevConnect.mmkvReporter()
mmkvReporter.reportWrite("key", "value")
mmkvReporter.reportRead("key", "value")// Room
val roomReporter = DevConnect.roomReporter()
roomReporter.reportQuery("SELECT * FROM users", results)
roomReporter.reportInsert("users", rowId)// Report FPS
DevConnect.reportPerformanceMetric(
metricType = "fps",
value = 58.5,
label = "Main Thread FPS"
)
// Report memory usage (MB)
DevConnect.reportPerformanceMetric(
metricType = "memory_usage",
value = 142.3,
label = "Heap Used"
)
// Report CPU usage (%)
DevConnect.reportPerformanceMetric(
metricType = "cpu_usage",
value = 35.2
)
// Report jank frame (ms)
DevConnect.reportPerformanceMetric(
metricType = "jank_frame",
value = 32.1,
label = "Slow render in RecyclerView"
)// Report undisposed listener (integrates well with LeakCanary)
DevConnect.reportMemoryLeak(
leakType = "undisposed_stream",
severity = "warning",
objectName = "LocationListener",
detail = "LocationManager listener not removed in MapsActivity",
retainedSizeBytes = 4096
)
// Report Activity leak (e.g. from LeakCanary)
DevConnect.reportMemoryLeak(
leakType = "widget_leak",
severity = "critical",
objectName = "DetailActivity",
detail = "Activity retained after onDestroy",
stackTrace = leakTrace.toString()
)
// Report growing collection
DevConnect.reportMemoryLeak(
leakType = "growing_collection",
severity = "critical",
objectName = "eventCache",
detail = "ArrayList grows unbounded — 15000 items",
retainedSizeBytes = 1200000,
metadata = mapOf("currentSize" to 15000, "maxExpected" to 100)
)DevConnect.benchmarkStart("loadHome")
fetchUser()
DevConnect.benchmarkStep("loadHome")
fetchPosts()
DevConnect.benchmarkStop("loadHome")DevConnect.registerCommand("clearCache") { args ->
mapOf("cleared" to true)
}// Send a state snapshot to desktop
DevConnect.sendStateSnapshot("viewmodel", mapOf("user" to userMap))
// Handle state restore from desktop
DevConnect.onStateRestore = { state ->
viewModel.restoreState(state)
}// Check connection status
if (DevConnect.isConnected()) {
Log.d("DC", "Connected to DevConnect desktop")
}
// Disconnect
DevConnect.disconnect()SDK tries these addresses in order:
localhost(iOS simulator, macOS)10.0.2.2(Android emulator)10.0.3.2(Genymotion)- Scan local network subnet
Check your desktop IP in Settings page (click to copy), then:
await DevConnect.init(appName: 'MyApp', host: '192.168.1.5');In desktop Settings > Android Device (USB), click "Run ADB Reverse".
Or manually: adb reverse tcp:9090 tcp:9090
- Desktop: Flutter Desktop (macOS/Windows) + Riverpod + go_router + Freezed
- Protocol: JSON over WebSocket (default port 9090)
- SDKs: Flutter (pub.dev / git), React Native (npm / git), Android (Maven / JitPack / AAR)
Contributions welcome! Feel free to open issues or pull requests.
git clone https://github.com/ridelinktechs/devconnect.git
cd devconnect
flutter pub get
dart run build_runner build --delete-conflicting-outputs
flutter run -d macosLooking for mobile debugging tools? Here's how DevConnect compares:
- Reactotron — Great for React Native + Redux, but no Flutter/Android support. DevConnect covers all three.
- Flipper — Facebook's extensible debugger, now deprecated. DevConnect is actively maintained.
- Flutter DevTools — Official Flutter debugging, but no React Native or Android Native. DevConnect adds cross-platform support.
Searching for: reactotron alternative, flipper replacement, flutter debugging tool, react native debugger, android debug inspector, mobile app debugger, cross-platform debugging, network inspector, state debugger, redux devtools mobile? DevConnect is built for you.
MIT - by buivietphi
DevConnect — Debug Flutter, React Native & Android apps from one desktop tool.
A modern alternative to Reactotron, Flipper, and platform-specific debugging tools.