Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

flutter-browser

Run a Flutter web app in a browser Flutter refuses to launch (Arc, Safari, Orion, anything that isn't Chrome or Edge) with hot reload, release mode and a clean quit.

flutter-browser              # debug, opens in Arc
flutter-browser --release    # release build, same flow
flutter-browser -b Safari    # any macOS browser

macOS only for now.

The problem

Point CHROME_EXECUTABLE at Arc and flutter run -d chrome dies with:

Failed to launch browser after 3 tries.
Failed to launch browser. Make sure you are using an up-to-date Chrome or Edge.

The advice is wrong. Arc is up to date. Flutter's chrome device doesn't just open a URL, it remote-controls the browser over the Chrome DevTools Protocol: it launches the binary with --remote-debugging-port and --user-data-dir, then blocks on the child's stderr waiting for a line starting with DevTools listening.

Arc never prints it. It isn't a rebranded Chrome binary, it's a native Swift app that embeds Chromium as ArcCore.framework and owns main(), so Chromium switches never reach the engine. With Flutter's exact flags:

Arc Chrome, same conditions
--version prints nothing prints version
DevTools listening on stderr never printed
CDP port responds nothing listening serves /json/version
--user-data-dir honoured ignored, dir left empty full profile written

No configuration fixes this, and no upstream patch could either, since the capability isn't Flutter's to add. Flutter closed the request as r: invalid (flutter/flutter#148131).

The way around it

The web-server device needs none of that. Instead of Flutter reaching into the browser, the page reaches out: DWDS talks to the app over its own injected WebSocket client. No debugging port, no handshake, no CDP. The browser only has to do the one thing every browser does, which is load a URL.

In Arc: hot reload 56-139ms, hot restart 40-171ms, Dart VM Service and the DevTools debugger/profiler attached, release builds running.

flutter-browser wraps that device and fixes the two things it gets wrong on its own, see docs/HOW-IT-WORKS.md.

Install

git clone <your-fork-url> flutter-browser
cd flutter-browser
./install.sh

Symlinks flutter-browser and flutter-browser-shim into ~/.local/bin, so git pull updates them in place.

Usage

flutter-browser [options] [-- flutter run args]

  -b, --browser NAME   browser to open (default: Arc, or $FLUTTER_BROWSER)
  -p, --port PORT      port to serve on (default: first free from 8899)
      --keep-tab       leave the browser tab open after the run ends
      --open-as MODE   tab (default) or window
      --tab            force a real tab - shorthand for --open-as tab
      --window         open a separate window instead
      --no-console     don't mirror the browser console into this terminal
      --dry-run        print the flutter command that would run, then exit
  -h, --help

Console output

print(), console.log, warnings and uncaught errors land in the terminal, the way they do with flutter run -d chrome:

$ flutter-browser
→ Arc will open at http://localhost:8899 once the build is ready
lib/main.dart is being served at http://localhost:8899
Starting application from main method in: org-dartlang-app:/web_entrypoint.dart.
counter is now 1
Bad state: something went wrong
    at Object.throw_ [as throw] (http://localhost:8899/dart_sdk.js:3803:11)

The chrome device gets those over CDP, which is exactly what web-server doesn't have, so this wrapper injects a console hook into the served page and prints what the page posts back. Errors are red, console.debug is dropped - both match what DWDS does for the chrome device.

Stack traces are the raw JavaScript ones. The chrome device maps them back to Dart source through DWDS; nothing here does that.

--no-console (or FLUTTER_BROWSER_CONSOLE=0) turns the whole thing off and serves the app directly.

Tabs vs windows on Arc

open -a Arc <url>, the obvious way to open a URL, makes Arc spawn a Little Arc window: a throwaway popup outside its normal window/tab model. They pile up one per run, and Arc's own AppleScript can't enumerate them, so nothing can clean them up afterwards.

Tab mode is the default and creates a real sidebar tab via AppleScript, which can be reused next run and closed on quit. If Arc refuses the script (it can, while launching or busy) the tool retries, and only then falls back to a window with a warning on stderr. Silence there was what let stray Little Arc windows pile up unexplained.

--window opts into the old behaviour deliberately.

Anything unrecognised is passed straight to flutter run, so every flutter run flag works unchanged, --dart-define included:

flutter-browser --release
flutter-browser --dart-define=API=staging
flutter-browser --dart-define=API=staging --dart-define=DEBUG=true
flutter-browser --dart-define="MSG=hello world"
flutter-browser --dart-define-from-file=config/dev.json
flutter-browser -b "Google Chrome" --profile
flutter-browser -- --web-renderer canvaskit

Values with spaces and = survive intact. Use --dry-run to see exactly what gets forwarded:

$ flutter-browser --dry-run --dart-define="MSG=hello world"
browser: Arc
open-as: tab
console: on (flutter serves on 8900)
url:     http://localhost:8899
command: flutter run -d web-server --web-hostname=localhost --web-port=8900 --dart-define=MSG=hello\ world

-p is always the port you browse. With the console mirror on, that port is the mirror's and Flutter serves behind it on the next free one.

Use -- when a value would otherwise be eaten as one of this tool's own flags.

r, R and q work as normal. q stops the server and closes the tab.

What you get, and what you don't

Works: running the app, hot reload, hot restart, release mode, print() and console output in the terminal, the browser's own DevTools console, clean quit.

Untested: IDE breakpoint debugging. The web-server device prints a warning about needing the Dart Debug Chrome extension, and while the debug service attached without any extension in every run here, breakpoints from VS Code were never verified. Treat that as unknown rather than working.

The IDE case

If something insists on -d chrome, like a VS Code or Android Studio run button, use the shim instead:

export CHROME_EXECUTABLE="$HOME/.local/bin/flutter-browser-shim"

A headless Chrome absorbs the debug protocol while your real browser shows the app, and flutter run -d chrome works untouched. It costs one hidden Chrome process, which is why it isn't the default.

Browser support

Tab reuse and auto-close need an AppleScript tab model:

browser opens closes tab on quit
Arc, Chrome, Edge, Brave, Vivaldi, Chromium yes yes
Safari yes yes
everything else (Firefox, Orion, ...) yes no, tab stays open

The tab is matched on localhost:<port>, never on localhost alone, so unrelated local work is never closed.

License

MIT

About

Flutter web app in a browser Flutter refuses to launch (Arc, Safari, Orion, anything that isn't Chrome or Edge)

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages