import 'dart:ui'; import 'package:flutter/material.dart'; class ParticipantBuffer { final bool audioEnabled; final String id; ParticipantBuffer({ @required this.audioEnabled, @required this.id, }) : assert(audioEnabled != null), assert(id != null); } class ParticipantWidget extends StatelessWidget { final Widget child; final String id; final bool audioEnabled; final bool videoEnabled; final bool isRemote; final bool isDummy; final bool isDominant; const ParticipantWidget({ Key key, @required this.child, @required this.audioEnabled, @required this.videoEnabled, @required this.id, @required this.isRemote, this.isDominant = false, this.isDummy = false, }) : assert(child != null), assert(audioEnabled != null), assert(videoEnabled != null), assert(isRemote != null), assert(isDominant != null), assert(isDummy != null), super(key: key); ParticipantWidget copyWith({ Widget child, bool audioEnabled, bool videoEnabled, bool isDominant, }) { return ParticipantWidget( id: id, child: child ?? this.child, audioEnabled: audioEnabled ?? this.audioEnabled, videoEnabled: videoEnabled ?? this.videoEnabled, isDominant: isDominant ?? this.isDominant, isRemote: isRemote, ); } @override Widget build(BuildContext context) { final children = []; final icons = []; if (!videoEnabled) { icons.add(_buildVideoEnabledIcon()); children.add( ClipRect( // Need to clip this BackdropFilter, otherwise it will blur the entire screen child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), child: Container( decoration: BoxDecoration(color: Colors.black.withOpacity(.1)), child: child, ), ), ), ); } else { children.add(child); } children.add(Padding( padding: const EdgeInsets.all(8.0), child: AnimatedOpacity( duration: Duration(milliseconds: 500), opacity: isDominant ? 1 : 0, child: Icon( Icons.volume_up, color: Colors.white, ), ), )); if (!audioEnabled) { icons.add(_buildAudioEnabledIcon()); } if (icons.isNotEmpty) { if (isRemote) { final rows = []; rows.add(_buildRow(icons)); if (!audioEnabled && !videoEnabled) { rows.add(_buildRow(_fitText('The camera and microphone are off', Colors.white24))); } else if (!audioEnabled) { rows.add(_buildRow(_fitText('The microphone is off', Colors.black26))); } else if (!videoEnabled) { rows.add(_buildRow(_fitText('The camera is off', Colors.white24))); } children.add( Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, children: rows, ), ); } else { children.add(Column( mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.stretch, children: icons, )); } } return Stack( children: children, ); } List _fitText(String text, Color color) { return [ Flexible( child: FittedBox( fit: BoxFit.scaleDown, child: Padding( padding: const EdgeInsets.only(left: 8.0, right: 8.0), child: Text(text, maxLines: 1, style: _buildTextStyle(color)), ), ), ), ]; } TextStyle _buildTextStyle(Color color) { return TextStyle( color: color, shadows: [ Shadow( blurRadius: 1.0, color: Color.fromARGB(255, 0, 0, 0), ), Shadow( blurRadius: 1.0, color: Color.fromARGB(24, 255, 255, 255), ), ], fontSize: 15, ); } Widget _buildRow(List children) { return Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: children, ); } Widget _buildAudioEnabledIcon() { return Padding( padding: const EdgeInsets.all(8), child: CircleAvatar( maxRadius: 15, child: FittedBox( child: Icon( Icons.mic_off, color: Colors.black, key: Key('microphone-off-icon'), ), ), backgroundColor: Colors.white24, ), ); } Widget _buildVideoEnabledIcon() { return Padding( padding: const EdgeInsets.all(8), child: CircleAvatar( maxRadius: 15, child: FittedBox( child: Icon( Icons.videocam_off, color: Colors.black, key: Key('videocam-off-icon'), ), ), backgroundColor: Colors.white24, ), ); } }