camview
Live camera feed from the 4Brains Camera Controller over WebRTC, in one Flutter widget. Point it at your server and room with an API key — signaling is handled for you, and the video flows peer-to-peer (the server only brokers the handshake, so it never becomes a latency bottleneck).
Works on any Flutter target flutter_webrtc supports — iPad, phone, kiosk, TV.
Install
dependencies:
camview: ^0.2.0
This pulls in flutter_webrtc, which needs a little native setup in your app.
iOS
ios/Podfile:
platform :ios, '13.0'
ios/Runner/Info.plist — allow the LAN connection and satisfy the WebRTC engine
(the app only receives, but the plugin links these APIs):
<key>NSAppTransportSecurity</key>
<dict><key>NSAllowsLocalNetworking</key><true/></dict>
<key>NSCameraUsageDescription</key>
<string>Required by the video engine; this app only receives the live view.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Required by the video engine; this app only receives the live view.</string>
Android
android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
Set minSdkVersion 23 (or higher) in android/app/build.gradle.
Use
import 'package:camview/camview.dart';
FourBrainsCameraView(
server: 'http://192.168.1.42:3000', // the server's LAN URL
room: 'camera', // the room the controller broadcasts to
apiKey: 'your-api-key', // your 4Brains API key
fit: CameraFit.cover, // contain (default) or cover
onStatus: (status) {
// connecting | live | reconnecting | unauthorized
},
onSize: (w, h) => debugPrint('feed is ${w}x$h'),
)
Drop it into any layout — a bounded box, Positioned.fill, or an AspectRatio.
It reconnects on its own if the network drops or the broadcaster restarts.
Status
| Status | Meaning |
|---|---|
connecting |
Reaching the server, or waiting for the broadcaster's offer. |
live |
Frames are arriving. |
reconnecting |
Transport dropped, or the broadcaster went away. Retrying. |
unauthorized |
The server rejected apiKey. Terminal — retrying cannot fix a bad key, so the widget stops. |
Worth handling unauthorized distinctly in your UI: it is the one failure a
user can act on, and it is otherwise indistinguishable from a weak network.
API
| Property | ||
|---|---|---|
server |
String |
Signaling server base URL. |
room |
String |
Room shared with the broadcaster. |
apiKey |
String |
Sent in the socket handshake. Required — the server refuses the connection without a valid key. |
fit |
CameraFit |
contain (letterbox) or cover (crop). |
iceServers |
List<Map> |
STUN/TURN. Empty for LAN; add for cross-network. |
onStatus |
callback | Connection state changes. |
onSize |
callback | Video pixel size. |
placeholder |
WidgetBuilder |
Shown before the first frame. |
How it fits together
Camera → 4Brains Camera Controller (broadcaster) your app (this widget)
│ SDP/ICE via signaling server │
└──────────────► server ◄──────────────┘
video is peer-to-peer, not via the server
The desktop controller captures the camera and broadcasts; your app is a viewer.
Run the server locally (see the server's DOCKER.md). The API key is whatever
you set in the server's API_KEYS — the same string goes in the controller and
here.
LAN vs. internet
Default iceServers is empty — correct for devices on the same Wi-Fi (they
connect directly via host candidates). To stream across networks you need a TURN
server; pass it via iceServers.
Libraries
- camview
- Live camera feed from the 4Brains Camera Controller over WebRTC.