Appearance
Overview
- File:
lib/views/call_page.dart
The CallPage is a Flutter widget that manages video and audio calls in the HVC Mobile application. It provides functionality for initiating, managing, and participating in video calls, including features like screen sharing, chat, and peripheral device integration.
Class Structure
Class Definition
dart
class CallPage extends StatefulWidget {
final CallDetails callDetails;
final bool isEventOwner;
final bool isGuest;
final bool isRapidConnect;
const CallPage({
Key? key,
required this.callDetails,
required this.isEventOwner,
this.isGuest = false,
this.isRapidConnect = false,
}) : super(key: key);
@override
State<CallPage> createState() => _CallPageState();
}Controllers
dart
final _localVideoRenderer = RTCVideoRenderer();
final _remoteVideoRenderer = RTCVideoRenderer();
final pexipController = Get.put(PexipController());
final MeetingsController meetingsController = Get.find();
final MediaController mediaController = Get.find();
final ContactsController contactsController = Get.find();
final EventsController eventsController = Get.find();
final MessagesController messagesController = Get.find();
final RealTimeController realTimeController = Get.find();
final AuthController authController = Get.find();State Variables
dart
final String _sessionId = const Uuid().v1();
final DateTime _sessionDate = DateTime.now();
final Rx<Orientation> ort = Orientation.portrait.obs;
RxBool isPortrait = true.obs;
RxString imageUrl = ''.obs;
late final UserData user;
int? selectedContact;
int meetingId = -1;Dependencies
Required Packages
flutter/material.dart- Core Flutter UI componentsget/get.dart- State management and routingflutter_webrtc- WebRTC implementationfont_awesome_flutter- Icon libraryonesignal_flutter- Push notificationsuuid- Unique ID generationwakelock_plus- Screen wake lockdropdown_search- Dropdown search functionality
Internal Dependencies
pexip_controller.dart- Pexip integrationmedia_controller.dart- Media handlingcontacts_controller.dart- Contact managementevents_controller.dart- Event managementmessages_controller.dart- Message handlingreal_time_controller.dart- Real-time communicationauth_controller.dart- Authenticationhlk_helpers/- Custom helper utilitiesmodels/- Data modelsshared/- Shared constants and configurationswidgets/- Custom widgets
State Management
Initialization
dart
@override
void initState() {
defaultPeri = selectedPeripheralType.value;
meetingsController.isRapidConnectPageOpened = false;
OneSignal.User.addTagWithKey('mId', widget.callDetails.meetingId);
isInCall.value = true;
WidgetsBinding.instance.addObserver(this);
initRenderer();
super.initState();
user = UserData.fromMap(userData);
logInfo('callDetails from the cal or meeting page');
logInfo(widget.callDetails.toMap());
pexipController.initializePexip(widget.callDetails);
pexipController.isCallOwner = widget.isEventOwner;
messagesController.selectedGroupId.value = 0;
messagesController.messages.clear();
logInfo('messagesController.messages = ${messagesController.messages}');
WakelockPlus.enable();
// Additional initialization code...
}Reactive State
dart
Obx(() => pexipController.isInRoom.value ? callView : preview)Call Management
Call Initialization
dart
void _handleStartCall() async {
await pexipController.start();
realTimeController.sendState('call');
await SystemChrome.setPreferredOrientations([]);
}Call Termination
dart
onPressed: () {
pexipController.stop();
realTimeController.resetSocket();
Get.back();
}Participant Management
dart
participantWatcher = ever(pexipController.participants, (callback) {
var me = pexipController.participants
.firstWhereOrNull((e) => getIdFromTag(e.call_tag) == user.id);
isMyVideoMutedByAdmin.value = getBoolean(me?.is_video_muted);
isPinnedByAdmin.value = getNumber(me?.spotlight) != 0;
isMyAudioMutedByAdmin.value =
getString(me?.is_muted).containsIgnoreCase('yes');
pexipController.pexipRole.value = me?.role ?? 'GUEST';
// Additional participant management code...
});UI Components
Video Renderers
dart
final localVideo = Obx(() => RTCVideoView(
_localVideoRenderer,
mirror: pexipController.isMainCamera.isFalse,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
));
final remoteVideo = RTCVideoView(
_remoteVideoRenderer,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain,
);Control Buttons
dart
RoundIconButton(
icon: Icon(pexipController.isAudioEnable.value
? Icons.mic
: Icons.mic_off),
onPressed: () async {
await pexipController.toggleAudio();
},
btnColor: kAppColorWhite,
size: btnSize,
)Chat Window
dart
ChatWindow(
scrollController: messagesController.scrollController,
showNotification: false,
onNotificationChange: (show) {},
messageList: messagesController.messages,
width: getDisplayWidth(),
height: getDisplayHeight(),
sendMessage: (text) async {
var msg = await realTimeController.callSendMessage(
content: text,
contentType: 'text',
user: user,
meetingId: meetingId,
meetingParticipantId: selectedContact == null
? null
: getNumber(selectedContact) as int,
);
messagesController.scrollToBottom();
if (msg != null) {
messagesController.selectedGroupId.value = msg.groupId;
messagesController.addMessageToQueue(msg);
}
},
// Additional chat window properties...
)Media Handling
Audio Device Management
dart
void setDefault() async {
var bluetooth = pexipController.outputAudios.firstWhereOrNull((e) =>
e.label.containsIgnoreCase('blu') ||
e.deviceId.containsIgnoreCase('blu'));
var wiredHeadset = pexipController.outputAudios.firstWhereOrNull((e) =>
e.label.containsIgnoreCase('wired') ||
e.deviceId.containsIgnoreCase('headset'));
if (bluetooth != null) {
await Helper.selectAudioOutput(bluetooth.deviceId);
pexipController.currentOutAudio.value = bluetooth;
} else if (wiredHeadset != null) {
await Helper.selectAudioOutput(wiredHeadset.deviceId);
pexipController.currentOutAudio.value = wiredHeadset;
} else {
pexipController.currentOutAudio.value = pexipController.outputAudios
.firstWhere((e) =>
e.label.containsIgnoreCase('speaker') ||
e.deviceId.containsIgnoreCase('speaker'));
}
setState(() {});
}Camera Management
dart
Obx(
() => pexipController.hasManyCameras.value
? RoundIconButton(
icon: const Icon(Icons.flip_camera_ios),
onPressed: () {
pexipController.toggleCamera();
},
btnColor: kAppColorWhite,
)
: const SizedBox.shrink(),
)Permission Handling
Permission Checks
dart
if (!hasPermission(HvcPermissions.TakePhoto)) {
HlkDialog.showErrorSnackBar('Not permitted to perform action');
return;
}Permission-Protected Actions
- Taking photos
- Recording calls
- Managing participants
Peripheral Integration
Peripheral Management
dart
Future<void> _handlePeripheralChange(
PeripheralType p, bool isSelected) async {
var inputDeviceNames =
pexipController.inputAudios.map((e) => e.label).toList();
logInfo('inputDeviceNames = $inputDeviceNames');
if (isSelected) {
var res =
await meetingsController.getPeripheralDevice(p.id, inputDeviceNames);
if (res.success) {
lastSelectedDevice = res.data;
await pexipController.enableLowLevelSound(isSelected);
audioDeviceId = pexipController.inputAudios
.firstWhereOrNull((el) => el.label == res.data)
?.deviceId ??
defaultAudioInId();
logInfo('audioDeviceId = $audioDeviceId');
if (audioDeviceId.isNotEmpty) {
await Helper.selectAudioInput(audioDeviceId);
}
var ok = await realTimeController.startPeripheral(
meetingId,
lastSelectedDevice,
);
if (ok)
HlkDialog.showSnackBar(
title: 'Info',
message: 'Peripheral enabled successfully',
color: Colors.white30,
);
} else {
HlkDialog.showErrorSnackBar(res.message ?? 'Failed to get device');
}
} else {
// Disable peripheral code...
}
logInfo('audioDeviceId = $audioDeviceId');
setState(() {});
}Peripheral UI
dart
final peripheralsView = Container(
color: kAppColorWhite,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
verticalSpace(0.2),
Expanded(
child: ListView.builder(
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
itemCount: peripherals.length,
itemBuilder: (BuildContext context, int index) {
final p = peripherals[index];
var isCurrent = p.id == selectedPeripheralType.value.id;
final iconCode = int.tryParse('0x${p.iconCode}') ?? -1;
return Card(
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.black, width: 1),
borderRadius: BorderRadius.circular(5),
),
child: RadioListTile<PeripheralType>(
toggleable: true,
title: Text(p.name),
secondary: FaIcon(FontAwesomeIcons.stethoscope,
color: kAppColorBlue),
value: p,
selected: isCurrent,
groupValue: selectedPeripheralType.value,
onChanged: (PeripheralType? value) async {
var isSelected = value != null;
selectedPeripheralType.value =
isSelected ? value : defaultPeri;
await _handlePeripheralChange(p, isSelected);
},
),
);
},
),
),
],
));