VideoCard class
A stateful widget displaying video participant card with stream rendering, controls, and waveform.
Renders a participant tile showing:
- Video stream via CardVideoDisplay (or MiniCard fallback if no stream)
- Name badge overlay (top-right by default)
- Audio/video toggle buttons (top-left by default)
- Animated waveform bars (9 bars, visible when video off + audio detected)
- Optional mirror mode for local camera
Rendering Logic:
- If
customBuilder
provided → delegates full rendering - Else builds Stack with:
- Background Container (containerBuilder)
- Video display via CardVideoDisplay or MiniCard fallback
- AudioDecibelCheck (monitors audio levels)
- Waveform overlay (overlayBuilder → waveformBuilder)
- Name badge (infoBuilder)
- Control buttons (audio/video toggles)
Layout Structure:
Stack (wrapperBuilder)
├─ Positioned.fill → Container (containerBuilder)
│ └─ IF videoStream != null:
│ └─ CardVideoDisplay (video stream)
│ ELSE:
│ └─ MiniCard (avatar fallback)
├─ AudioDecibelCheck (monitors audio, updates waveform state)
├─ IF showWaveform:
│ └─ Positioned.fill → Container (overlayBuilder)
│ └─ Row (waveformBuilder)
│ └─ 9 × AnimatedContainer (bars)
├─ Positioned (infoPosition) → Container (infoBuilder)
│ └─ Row (nameBadge + waveform indicator)
└─ Positioned (controlsPosition) → Row
├─ GestureDetector (audio toggle)
└─ GestureDetector (video toggle)
Video Stream Rendering:
- Uses
CardVideoDisplay
to render MediaStream via WebRTC RTCVideoRenderer - Applies mirror mode if
doMirror=true
(CSS scaleX(-1)) - Falls back to MiniCard avatar if
videoStream
null or video track unavailable - Respects
forceFullDisplay
for aspect ratio handling
Audio Level Detection:
AudioDecibelCheck
component pollsparameters.audioDecibels
every 2 seconds- Matches by
participant.name
againstaudioDecibels[i].name
- Updates
showWaveform
state based on:- Participant unmuted (
participant.muted == false
) - Audio detected (avgAudioDecibels > threshold)
- Video off or
!forceFullDisplay
- Participant unmuted (
- Triggers waveform animation via AnimationControllers
Control Button Workflow:
-
Audio Toggle (Mute/Unmute):
- Taps mute icon in controls overlay
- Calls
toggleAudio()
→controlUserMedia
- Parameters:
{ participantId, participantName, type: 'audio' }
- Checks permissions (host/coHost with 'media' permission)
- Emits socket event
'controlMedia'
to server
-
Video Toggle (On/Off):
- Taps video icon in controls overlay
- Calls
toggleVideo()
→controlUserMedia
- Parameters:
{ participantId, participantName, type: 'video' }
- Checks permissions (host/coHost with 'media' permission)
- Emits socket event
'controlMedia'
to server
Waveform Animation:
- State creates 9 AnimationControllers (1 per bar)
animateWaveform()
starts repeat(reverse: true) animationresetWaveform()
stops/resets all controllers- Bar heights scale from 1px (silent) to 40px (loud)
- Bars hidden if participant muted or no audio detected
- Disposed on widget unmount
Builder Hook Priorities:
customBuilder
→ full widget replacement (ignores all other props)wrapperBuilder
→ wraps Stack; receives stackChildren + defaultcontainerBuilder
→ wraps video/avatar container; receives child + defaultinfoBuilder
→ wraps name badge/waveform; receives nameBadge + waveform + defaultoverlayBuilder
→ wraps waveform overlay; receives waveform + defaultwaveformBuilder
→ wraps bars; receives animationControllers + default
Common Use Cases:
-
Grid View with Videos:
GridView.builder( itemCount: videoParticipants.length, itemBuilder: (context, index) { final participant = videoParticipants[index]; return VideoCard( options: VideoCardOptions( parameters: parameters, name: participant.name, remoteProducerId: participant.videoID, eventType: EventType.conference, videoStream: participant.stream, participant: participant, ), ); }, )
-
Local Camera Preview:
VideoCard( options: VideoCardOptions( parameters: parameters, name: 'You', remoteProducerId: 'local', eventType: EventType.conference, videoStream: localStream, participant: localParticipant, doMirror: true, controlsPosition: 'bottomRight', ), )
-
Host-Only Controls:
VideoCard( options: VideoCardOptions( parameters: parameters, name: 'John Doe', remoteProducerId: 'producer-123', eventType: EventType.conference, videoStream: mediaStream, participant: participant, showControls: isHost, videoControlsComponent: isHost ? hostControls : null, ), )
-
Custom Name Badge with Status:
VideoCard( options: VideoCardOptions( parameters: parameters, name: 'John Doe', remoteProducerId: 'producer-123', eventType: EventType.conference, videoStream: mediaStream, participant: participant, infoBuilder: (context, nameBadge, waveform, defaultInfo) { return Stack( children: [ defaultInfo, Positioned( bottom: 0, right: 0, child: Icon(Icons.fiber_manual_record, color: Colors.green, size: 12), ), ], ); }, ), )
Override Integration:
Integrates with MediasfuUICustomOverrides
for global styling:
overrides: MediasfuUICustomOverrides(
videoCardOptions: ComponentOverride<VideoCardOptions>(
builder: (existingOptions) => VideoCardOptions(
parameters: existingOptions.parameters,
name: existingOptions.name,
remoteProducerId: existingOptions.remoteProducerId,
eventType: existingOptions.eventType,
videoStream: existingOptions.videoStream,
participant: existingOptions.participant,
containerDecoration: BoxDecoration(
gradient: LinearGradient(colors: [Colors.blue, Colors.purple]),
borderRadius: BorderRadius.circular(16),
),
barColor: Colors.yellow,
nameTextStyle: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
),
),
Performance Notes:
- Animation controllers created once in initState (9 controllers)
- Audio level polling every 2 seconds (not every frame)
- Waveform hidden when video active (skips rendering 9 bars)
- Video rendering delegated to CardVideoDisplay (uses RTCVideoRenderer)
- Disposed controllers + video renderer cleaned up in dispose()
Permission Requirements:
- Controlling others requires host or coHost with 'media' permission
- Self-control always allowed
- Socket connection required for remote control actions
Mirror Mode:
- Applied via CSS transform: scaleX(-1) on video element
- Typically used for local camera to match user expectation
- Does not affect stream data, only display
- Inheritance
-
- Object
- DiagnosticableTree
- Widget
- StatefulWidget
- VideoCard
Constructors
- VideoCard.new({Key? key, required VideoCardOptions options})
-
const
Properties
- hashCode → int
-
The hash code for this object.
no setterinherited
- key → Key?
-
Controls how one widget replaces another widget in the tree.
finalinherited
- options → VideoCardOptions
-
final
- runtimeType → Type
-
A representation of the runtime type of the object.
no setterinherited
Methods
-
createElement(
) → StatefulElement -
Creates a StatefulElement to manage this widget's location in the tree.
inherited
-
createState(
) → _VideoCardState -
Creates the mutable state for this widget at a given location in the tree.
override
-
debugDescribeChildren(
) → List< DiagnosticsNode> -
Returns a list of DiagnosticsNode objects describing this node's
children.
inherited
-
debugFillProperties(
DiagnosticPropertiesBuilder properties) → void -
Add additional properties associated with the node.
inherited
-
noSuchMethod(
Invocation invocation) → dynamic -
Invoked when a nonexistent method or property is accessed.
inherited
-
toDiagnosticsNode(
{String? name, DiagnosticsTreeStyle? style}) → DiagnosticsNode -
Returns a debug representation of the object that is used by debugging
tools and by DiagnosticsNode.toStringDeep.
inherited
-
toString(
{DiagnosticLevel minLevel = DiagnosticLevel.info}) → String -
A string representation of this object.
inherited
-
toStringDeep(
{String prefixLineOne = '', String? prefixOtherLines, DiagnosticLevel minLevel = DiagnosticLevel.debug, int wrapWidth = 65}) → String -
Returns a string representation of this node and its descendants.
inherited
-
toStringShallow(
{String joiner = ', ', DiagnosticLevel minLevel = DiagnosticLevel.debug}) → String -
Returns a one-line detailed description of the object.
inherited
-
toStringShort(
) → String -
A short, textual description of this widget.
inherited
Operators
-
operator ==(
Object other) → bool -
The equality operator.
inherited