@vaishnavkm/flutterbridge 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +7 -7
  2. package/index.js +76 -2
  3. package/package.json +4 -3
package/README.md CHANGED
@@ -37,7 +37,7 @@ bunx @vaishnavkm/flutterbridge
37
37
  Navigate to your Flutter project directory and run:
38
38
 
39
39
  ```bash
40
- flutterbridge
40
+ bridge
41
41
  ```
42
42
 
43
43
  A QR code will appear in your terminal. Scan it with the FlutterBridge companion app to connect.
@@ -49,18 +49,18 @@ and encode that address in the QR so phones can connect over WiFi.
49
49
 
50
50
  ```bash
51
51
  # Choose a specific device
52
- flutterbridge --device <device-id>
53
- flutterbridge -d <device-id>
52
+ bridge --device <device-id>
53
+ bridge -d <device-id>
54
54
 
55
55
  # Print only the QR code (no extra logs)
56
- flutterbridge --qr-only
56
+ bridge --qr-only
57
57
 
58
58
  # Print machine-readable JSON output
59
- flutterbridge --json
59
+ bridge --json
60
60
 
61
61
  # Pass additional Flutter flags
62
- flutterbridge -- --release
63
- flutterbridge -- --flavor production
62
+ bridge -- --release
63
+ bridge -- --flavor production
64
64
  ```
65
65
 
66
66
  ## Requirements
package/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  const qrcode = require('qrcode-terminal');
4
4
  const chalk = require('chalk');
5
+ const WebSocket = require('ws');
5
6
  const { spawn } = require('child_process');
6
7
  const readline = require('readline');
7
8
  const fs = require('fs');
@@ -9,7 +10,7 @@ const path = require('path');
9
10
  const os = require('os');
10
11
  const net = require('net');
11
12
 
12
- const VM_URL_TIMEOUT_MS = 60000;
13
+ const VM_URL_TIMEOUT_MS = 300000; // 5 minutes (initial gradle builds take time)
13
14
  const SERVICE_URI_KEYS = [
14
15
  'vmServiceUri',
15
16
  'observatoryUri',
@@ -200,6 +201,61 @@ function closeAllProxies() {
200
201
 
201
202
  process.on('exit', closeAllProxies);
202
203
 
204
+ function startScreenshotServer(deviceId) {
205
+ return new Promise((resolve, reject) => {
206
+ try {
207
+ const wss = new WebSocket.Server({ port: 0, host: '0.0.0.0' });
208
+ let intervalId = null;
209
+ let connections = 0;
210
+
211
+ wss.on('listening', () => {
212
+ const port = wss.address().port;
213
+ resolve(port);
214
+ });
215
+
216
+ wss.on('error', (err) => {
217
+ reject(err);
218
+ });
219
+
220
+ wss.on('connection', (ws) => {
221
+ connections++;
222
+
223
+ if (connections === 1) {
224
+ intervalId = setInterval(() => {
225
+ const adbArgs = deviceId ? ['-s', deviceId, 'exec-out', 'screencap', '-p'] : ['exec-out', 'screencap', '-p'];
226
+ const child = spawn('adb', adbArgs);
227
+ const chunks = [];
228
+
229
+ child.stdout.on('data', chunk => chunks.push(chunk));
230
+ child.on('close', code => {
231
+ if (code === 0 && wss.clients.size > 0) {
232
+ const buffer = Buffer.concat(chunks);
233
+ wss.clients.forEach(client => {
234
+ if (client.readyState === WebSocket.OPEN) {
235
+ client.send(buffer);
236
+ }
237
+ });
238
+ }
239
+ });
240
+ }, 500); // 2 fps polling
241
+ }
242
+
243
+ ws.on('close', () => {
244
+ connections--;
245
+ if (connections === 0 && intervalId) {
246
+ clearInterval(intervalId);
247
+ intervalId = null;
248
+ }
249
+ });
250
+ });
251
+
252
+ registerProxy(wss);
253
+ } catch(err) {
254
+ reject(err);
255
+ }
256
+ });
257
+ }
258
+
203
259
  function startTcpProxy(targetHost, targetPort) {
204
260
  return new Promise((resolve, reject) => {
205
261
  const server = net.createServer((client) => {
@@ -457,6 +513,14 @@ async function main() {
457
513
  return;
458
514
  }
459
515
 
516
+ let previewPort = null;
517
+ try {
518
+ previewPort = await startScreenshotServer(deviceId);
519
+ if (!quiet) console.log(chalk.gray(`Started Live Preview server on port ${previewPort}`));
520
+ } catch (e) {
521
+ if (!quiet) console.warn(chalk.yellow(`Warning: Failed to start Live Preview server: ${e.message}`));
522
+ }
523
+
460
524
  if (!quiet) {
461
525
  console.log(chalk.gray('Starting Flutter...\n'));
462
526
  }
@@ -509,7 +573,17 @@ async function main() {
509
573
  if (vmServiceUrl) {
510
574
  return;
511
575
  }
512
- vmServiceUrl = result.url;
576
+
577
+ let finalUrl = result.url;
578
+ if (previewPort) {
579
+ try {
580
+ const u = new URL(finalUrl);
581
+ u.searchParams.set('previewPort', previewPort);
582
+ finalUrl = u.toString();
583
+ } catch(e) {}
584
+ }
585
+
586
+ vmServiceUrl = finalUrl;
513
587
  clearTimeout(vmTimeout);
514
588
 
515
589
  if (jsonOutput) {
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@vaishnavkm/flutterbridge",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Bridge your Flutter code to your phone instantly. Wireless development with QR code pairing.",
5
5
  "main": "index.js",
6
6
  "bin": {
7
- "flutterbridge": "index.js"
7
+ "bridge": "index.js"
8
8
  },
9
9
  "publishConfig": {
10
10
  "access": "public"
@@ -40,6 +40,7 @@
40
40
  "packageManager": "pnpm@10.33.0",
41
41
  "dependencies": {
42
42
  "chalk": "^4.1.2",
43
- "qrcode-terminal": "^0.12.0"
43
+ "qrcode-terminal": "^0.12.0",
44
+ "ws": "^8.21.0"
44
45
  }
45
46
  }