MIT licensed. Interactive demo: https://onlinetechsupport.biz/portfolio/crowleys-instruments/
Flutter demo gallery with photographic instrument housings. The analog gauge now uses code-drawn ticks, numbers, readout and needle for precise interchangeable scales. The other four instruments retain their original photo-layer rendering. This follows the owner's revised direction; AGENTS.md still contains the earlier photo-only brief because updating that protected file was not approved.
flutter pub get
flutter analyze
flutter test
flutter build apk --debug
adb install -r build/app/outputs/flutter-apk/app-debug.apk
Display name: Crowley's Instruments. Debug APK: build/app/outputs/flutter-apk/app-debug.apk.
The same gallery runs in a browser (dart2js plus CanvasKit):
flutter build web --release --base-href /your/path/
web/flutter_bootstrap.js pins CanvasKit to the local canvaskit/ folder, so
the built app makes no third-party requests. Serve the contents of
build/web/ over HTTP; opening index.html from the filesystem does not work
because the engine fetches its assets.
- AnalogGaugeWidget — cleaned photo housing + AnalogDialPainter (lib/analog_dial.dart). Precision, Minimal and Warning band styles; 0–100 %, 0–10 bar and -50–50 °C ranges. All use one 270° linear mapping for ticks, labels and needle. Prepare the housing locally with scripts/prepare_analog_housing.py; scripts/split_assets.py also calls this preparation.
- LedBarMeterWidget — regenerated photo housing with twelve equal registered cells (four cyan, four amber, four red). Reveals whole cells bottom-up: floor(clampedValue * 12), never partial bars.
- LcdReadoutWidget — five fixed-width photographic digit cells with a compact decimal sprite inside the cell gutter, not an extra slot. decimalPlaces accepts 0–4 (default 1); zero hides the point. Nonnegative values clamp to the five-digit capacity at the chosen precision. The gallery exposes a decimal selector.
- OscilloscopeWidget — screen photo + photographic glow-segment sprites stretched and rotated between successive data points.
- RadialScannerWidget — photographic submarine housing with an opaque flat
screen and code-rendered phosphor sweep (lib/sonar_screen.dart). Optional
contacts: List<SonarContact>draws contact IDs, blips and course arrows independently of the sweep. Bearing/course are radians clockwise from north; range/vectorLength are fractions of screen radius. Defaults to no contacts; gallery SonarPanel starts five randomly moving demo contacts. Arrows stop at the circular boundary, invalid contacts are omitted, display capped at 64 contacts. lib/sonar_contacts.dart contains the model and overlay.
All values are demo/simulated only; nothing is calibrated.
Scrollable dark gallery (#0a0a0c) with a slider + randomize button per instrument and a Play demo toggle animating all five from fake data.
All five components accept their own optional controller:. Each controller
owns only its instrument's state; there is no shared instrument controller.
See docs/instrument-json.md for standalone integration,
per-component schemas, mode precedence and the five examples/*_input.json files.
Each gallery instrument has an Update JSON action. JSON input does not stop or
change the other instruments.
See docs/sonar-json.md and examples/sonar_snapshot.json. SonarController.updateJson atomically replaces the full contact snapshot and disables random motion. SonarPanel(controller: controller, assets: assets, angle: angle) listens for updates. Feed strings from your own HTTP/WebSocket/sensor adapter; no network listener or location permission is installed by this component. The gallery's Update JSON editor exercises the same API. New random demo returns to simulation; Pause contacts pauses simulation independently of the sweep.
See assets/PIPELINE.md for the generate-then-split pipeline, provenance rules, measured deviations, and the QA checklist before shipping a new split. Sources are preserved under assets/sources/; never regenerate over them.
MIT. See LICENSE.