Flutter Web rewrite of the Benefits Digger frontend. Talks to the existing FastAPI backend in the sibling Gov-Benefits-Search repo.
- Flutter (stable channel)
- Dart 3.5+
git clone https://github.com/dcsid/Gov-Benefits-Search-Flutter.git
cd Gov-Benefits-Search-Flutter
flutter pub get
dart run build_runner build --delete-conflicting-outputsPick the mode that matches what you're doing. Debug is 30-50× slower than release; most "the app feels laggy" reports trace back to running in debug. Don't evaluate smoothness in debug mode.
| Mode | Command | When to use |
|---|---|---|
| Release | flutter run -d chrome --release --web-port=3000 |
Smoothest interactive build — what users actually see in the production deploy. |
| Profile | flutter run -d chrome --profile --web-port=3000 |
Profile build with the DevTools hook — required for the Performance tab and CPU/memory recording. |
| Debug | flutter run -d chrome --web-port=3000 |
Hot reload + asserts. Useful for development. Slow. Don't use for perf evaluation. |
All three accept the same --dart-defines — minimal example:
flutter run -d chrome --release --web-port=3000 \
--dart-define=API_BASE=http://localhost:8000/api/v1Port 3000 matches the backend's CORS allowlist (CORS_ALLOW_ORIGINS=http://localhost:3000).
Other dart-defines:
| Key | Default |
|---|---|
API_BASE |
http://localhost:8000/api/v1 |
APP_NAME |
Benefits Digger |
ENABLE_DEVTOOLS |
false |
USE_MOCKS |
false |
flutter build web --releaseThis is the documented default. Last measurement (2026-05-10):
| Artifact | Raw | Gzipped |
|---|---|---|
main.dart.js |
3730 KB | 1070 KB |
canvaskit.wasm |
6988 KB | 2802 KB |
Total build/web/ |
- | 38 MB |
Plus 4 deferred-route chunks (main.dart.js_*.part.js, ~43 KB gzipped combined) that are fetched on first navigation to /results, /dashboard, /chat, or /_kitchen_sink. See Code-splitting below.
The default flutter build web --release ships dart2js + the canvaskit (or skwasm) WASM rendering engine. Flutter also has a --wasm mode that compiles Dart itself to WebAssembly:
flutter build web --release --wasmComparison on this app (2026-05-10):
| Mode | Critical bytes (gz) | Total build/web/ |
Notes |
|---|---|---|---|
| Default (dart2js) | ~1070 KB main + canvaskit | 38 MB | Universal browser support; deferred imports DO split chunks. |
--wasm |
~1217 KB main.dart.wasm + wimp.wasm | 42 MB | Requires browser WASM-GC support; ships JS fallback alongside. deferred imports do NOT split. |
The default JS build is the smaller deploy and the documented default. --wasm is opt-in; revisit when WASM-GC is universal in evergreen browsers.
lib/app/router.dart declares deferred as imports for the four heaviest routes (/results, /dashboard, /chat, /_kitchen_sink). Each lives in its own main.dart.js_*.part.js chunk and is fetched on first navigation; a centered spinner renders during the load. Eager-imported routes (landing, explorer, screening, info pages) ship in the initial bundle.
- Inter font is bundled locally (
assets/fonts/Inter/, declared underflutter.fonts:inpubspec.yaml); first paint no longer waits on a Google Fonts network round-trip, and there's no FOIT. web/index.htmlpreloadsflutter_bootstrap.js,main.dart.js, the canvaskit chunks, AND the two Inter weights (Regular + Bold) used on first paint, so the browser fetches them in parallel with HTML parsing.web/index.htmlalsodns-prefetches andpreconnects to the local backend at127.0.0.1:8000so the first/api/v1fetch from app bootstrap doesn't pay the DNS + TLS round-trip serially.- Long lists (chat messages, search results, comparison rows) use
ListView.builder/GridView.builderso off-screen items aren't materialized. - Rebuild scope is narrow: page-level consumers
.select()only the slices they display, andBdHeader/BdFootersit insideRepaintBoundarys so unrelated state changes don't repaint chrome. What-Ifscenario inputs own theirTextEditingControllers in stateful widgets — typing no longer recreates controllers (which previously dropped cursor position on every keystroke).
The FastAPI backend that this app talks to lives in the Gov-Benefits-Search repo — see its README for setup. By default this app expects it at http://localhost:8000.
Note: the backend repo is currently private, so external users can't run the backend yet. There is no working backend-free demo mode at the moment either — the
USE_MOCKSdart-define (lib/core/env.dart) is declared but not yet wired to anything, and only the help bot has a mock (MockHelpService, applied via aProviderScopeoverride in tests).
lib/app/— root widget, routing, themelib/core/— env, API client, shared infrastructurelib/features/— feature modules (added by follow-up work)lib/widgets/— shared widgetslib/l10n/— localization (ar, en, es, ko, ru, vi, zh)