A complete prototype system demonstrating Over-The-Air (OTA) updates for a Flutter app using a WebView to render a VueJS-based UI with bi-directional JavaScript-to-Flutter native bridge communication.
┌─────────────────────────────────────────────────────────┐
│ Flutter App │
│ ┌──────────────────────────────────────────────────┐ │
│ │ WebView Container │ │
│ │ ┌────────────────────────────────────────────┐ │ │
│ │ │ VueJS Web Application │ │ │
│ │ │ • TypeScript │ │ │
│ │ │ • Scanner UI Button │ │ │
│ │ │ • Native Bridge (JS → Flutter) │ │ │
│ │ └────────────────────────────────────────────┘ │ │
│ │ ↕ │ │
│ │ JavascriptChannel Bridge │ │
│ │ ↕ │ │
│ │ ┌────────────────────────────────────────────┐ │ │
│ │ │ Flutter Native Layer │ │ │
│ │ │ • Native Bridge Handler │ │ │
│ │ │ • Mobile Scanner (Camera) │ │ │
│ │ │ • OTA Update Service │ │ │
│ │ └────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
↕
Network (HTTP)
↕
┌─────────────────────────────────────────────────────────┐
│ Backend OTA Server │
│ • Express.js + TypeScript │
│ • Serves version.json │
│ • Serves web assets │
└─────────────────────────────────────────────────────────┘
ota_prototype/
├── webui/ # VueJS Web App
│ ├── src/
│ │ ├── App.vue # Main Vue component with scanner UI
│ │ ├── main.ts # Entry point with bridge setup
│ │ ├── types/
│ │ │ └── native-bridge.d.ts # TypeScript definitions
│ │ └── style.css
│ ├── index.html
│ ├── package.json
│ ├── vite.config.ts
│ └── tsconfig.json
│
├── flutter_app/ # Flutter Native App
│ ├── lib/
│ │ ├── main.dart # App entry with OTA check
│ │ ├── screens/
│ │ │ └── webview_screen.dart # WebView container
│ │ ├── services/
│ │ │ ├── native_bridge.dart # Bridge handler
│ │ │ └── ota_service.dart # OTA update logic
│ │ └── widgets/
│ │ └── scanner_overlay.dart # Scanner UI
│ ├── android/
│ │ └── app/src/main/AndroidManifest.xml # Permissions
│ ├── ios/
│ │ └── Runner/Info.plist # iOS permissions
│ ├── pubspec.yaml # Dependencies
│ └── assets/webui/ # Bundled web assets
│
├── backend/ # OTA Update Server
│ ├── src/
│ │ └── server.ts # Express server
│ ├── assets/ # Served web assets
│ │ └── version.json # Version manifest
│ ├── package.json
│ └── tsconfig.json
│
├── build.sh # Build automation script
├── task.md # Original specification
└── README.md # This file
- Node.js (v18+)
- Flutter SDK (3.0+)
- Android Studio / Xcode (for mobile development)
cd webui
npm install
npm run buildcd flutter_app
flutter pub get
# Copy web assets to Flutter assets folder
mkdir -p assets/webui
cp -r ../webui/dist/* assets/webui/cd backend
npm install
# Copy web assets to backend
mkdir -p assets
cp -r ../webui/dist/* assets/
# Start server
npm run devThe server will run on http://localhost:3000
cd flutter_app
flutter runImportant: Update the OTA server URL in lib/services/ota_service.dart based on your platform:
- Android Emulator:
http://10.0.2.2:3000 - iOS Simulator:
http://localhost:3000 - Physical Device:
http://<YOUR_COMPUTER_IP>:3000
chmod +x build.sh
./build.sh- App Launch: Flutter app checks backend for new version
- Version Check: Compares local version with
version.jsonfrom server - Download: If newer version available, downloads all files
- Atomic Replace: Downloads to temp folder, then swaps atomically
- Reload: WebView loads updated assets
// In VueJS
window.NativeBridgeChannel.postMessage(
JSON.stringify({ type: "scanner", action: "open" })
);// In Flutter
controller.runJavaScript(
"window.onFlutterScanResult('scanned_code_here');"
);- User clicks "Scan QR/Barcode" button in VueJS UI
- VueJS sends message via
NativeBridgeChannel - Flutter receives message and shows camera scanner overlay
- User scans code
- Flutter sends result back to JavaScript
- VueJS displays the scanned code
- VueJS 3 + TypeScript web app
- Vite build system for optimized assets
- Flutter WebView integration
- Bi-directional native bridge
- Mobile camera scanner (QR/Barcode)
- OTA update mechanism with version checking
- Atomic asset replacement
- Express.js backend server
- TypeScript throughout
- Android & iOS support
- Camera permissions handling
- Network connectivity for OTA
| Type | Direction | Description |
|---|---|---|
scanner |
JS → Flutter | Opens camera scanner |
showDialog |
JS → Flutter | Shows native alert dialog |
getDeviceInfo |
JS → Flutter | Requests device information |
onFlutterScanResult |
Flutter → JS | Returns scanned code |
onFlutterMessage |
Flutter → JS | Generic Flutter response |
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" /><key>NSCameraUsageDescription</key>
<string>This app needs camera access to scan QR codes</string>Default: 3000
Change in backend/src/server.ts:
const PORT = 3000;Configure in flutter_app/lib/services/ota_service.dart:
static const String _updateUrlBase = 'http://10.0.2.2:3000';Version is stored in version.json:
{
"version": "1.0.0",
"timestamp": "2025-11-10T00:00:00.000Z",
"files": ["index.html", "assets/index.css", "assets/index.js"]
}- Build and run the app with initial assets
- Modify the VueJS app (e.g., change button text)
- Rebuild:
cd webui && npm run build - Update backend assets:
cp -r dist/* ../backend/assets/ - Update version in
backend/assets/version.json - Restart the Flutter app
- App should detect and download the update
vue^3.4.0vite^5.0.0typescript^5.3.0
webview_flutter^4.4.0mobile_scanner^3.5.0http^1.1.0path_provider^2.1.0shared_preferences^2.2.0
express^4.18.0cors^2.8.5typescript^5.3.0
- Check file paths in
webview_screen.dart - Verify assets are in
flutter_app/assets/webui/ - Check
pubspec.yamlincludes assets folder
- Verify backend server is running
- Check network connectivity
- Verify correct IP/URL for your platform
- Check version.json is properly formatted
- Verify camera permissions are granted
- Check
AndroidManifest.xmlandInfo.plist - Test on physical device (camera not available on emulators)
- Check JavaScript console in WebView
- Verify
JavascriptChannelis registered - Ensure JSON.stringify is used for messages
This is a prototype for educational purposes.
Built following the specification in task.md with complete TypeScript integration and modern best practices.
Ready to test! Follow the Getting Started guide above to build and run the complete system.