Chat Fix & Web RTC Methods

merge-requests/188/head
Aamir Muhammad 3 years ago
parent 2b35c3d8d6
commit 74e01e60fb

Binary file not shown.

Binary file not shown.

@ -70,9 +70,7 @@ class ChatApiClient {
if (!kReleaseMode) { if (!kReleaseMode) {
logger.i("res: " + response.body); logger.i("res: " + response.body);
} }
return ChatUserModel.fromJson( return ChatUserModel.fromJson(json.decode(response.body));
json.decode(response.body),
);
} catch (e) { } catch (e) {
throw e; throw e;
} }

@ -7,7 +7,6 @@
// ignore_for_file: depend_on_referenced_packages // ignore_for_file: depend_on_referenced_packages
import 'package:audio_session/audio_session_web.dart'; import 'package:audio_session/audio_session_web.dart';
import 'package:camera_web/camera_web.dart';
import 'package:file_picker/_internal/file_picker_web.dart'; import 'package:file_picker/_internal/file_picker_web.dart';
import 'package:firebase_core_web/firebase_core_web.dart'; import 'package:firebase_core_web/firebase_core_web.dart';
import 'package:firebase_messaging_web/firebase_messaging_web.dart'; import 'package:firebase_messaging_web/firebase_messaging_web.dart';
@ -16,7 +15,6 @@ import 'package:geolocator_web/geolocator_web.dart';
import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; import 'package:google_maps_flutter_web/google_maps_flutter_web.dart';
import 'package:image_picker_for_web/image_picker_for_web.dart'; import 'package:image_picker_for_web/image_picker_for_web.dart';
import 'package:just_audio_web/just_audio_web.dart'; import 'package:just_audio_web/just_audio_web.dart';
import 'package:record_web/record_web.dart';
import 'package:shared_preferences_web/shared_preferences_web.dart'; import 'package:shared_preferences_web/shared_preferences_web.dart';
import 'package:url_launcher_web/url_launcher_web.dart'; import 'package:url_launcher_web/url_launcher_web.dart';
import 'package:video_player_web/video_player_web.dart'; import 'package:video_player_web/video_player_web.dart';
@ -26,7 +24,6 @@ import 'package:flutter_web_plugins/flutter_web_plugins.dart';
// ignore: public_member_api_docs // ignore: public_member_api_docs
void registerPlugins(Registrar registrar) { void registerPlugins(Registrar registrar) {
AudioSessionWeb.registerWith(registrar); AudioSessionWeb.registerWith(registrar);
CameraPlugin.registerWith(registrar);
FilePickerWeb.registerWith(registrar); FilePickerWeb.registerWith(registrar);
FirebaseCoreWeb.registerWith(registrar); FirebaseCoreWeb.registerWith(registrar);
FirebaseMessagingWeb.registerWith(registrar); FirebaseMessagingWeb.registerWith(registrar);
@ -35,7 +32,6 @@ void registerPlugins(Registrar registrar) {
GoogleMapsPlugin.registerWith(registrar); GoogleMapsPlugin.registerWith(registrar);
ImagePickerPlugin.registerWith(registrar); ImagePickerPlugin.registerWith(registrar);
JustAudioPlugin.registerWith(registrar); JustAudioPlugin.registerWith(registrar);
RecordPluginWeb.registerWith(registrar);
SharedPreferencesPlugin.registerWith(registrar); SharedPreferencesPlugin.registerWith(registrar);
UrlLauncherPlugin.registerWith(registrar); UrlLauncherPlugin.registerWith(registrar);
VideoPlayerPlugin.registerWith(registrar); VideoPlayerPlugin.registerWith(registrar);

@ -70,9 +70,9 @@ Future<void> main() async {
ChangeNotifierProvider<MarathonProvider>( ChangeNotifierProvider<MarathonProvider>(
create: (_) => MarathonProvider(), create: (_) => MarathonProvider(),
), ),
// ChangeNotifierProvider<ChatCallProvider>( ChangeNotifierProvider<ChatCallProvider>(
// create: (_) => ChatCallProvider(), create: (_) => ChatCallProvider(),
// ), ),
], ],
child: const MyApp(), child: const MyApp(),
), ),

@ -7,19 +7,31 @@ import 'dart:convert';
class CallDataModel { class CallDataModel {
CallDataModel({ CallDataModel({
this.callerId, this.callerId,
this.callerDetails, this.callerName,
this.callerEmail,
this.callerTitle,
this.callerPhone,
this.receiverId, this.receiverId,
this.receiverDetails, this.receiverName,
this.receiverEmail,
this.receiverTitle,
this.receiverPhone,
this.title, this.title,
this.calltype, this.callType,
}); });
String? callerId; int? callerId;
CallerDetails? callerDetails; String? callerName;
String? receiverId; String? callerEmail;
ReceiverDetails? receiverDetails; String? callerTitle;
dynamic title; dynamic callerPhone;
String? calltype; int? receiverId;
String? receiverName;
String? receiverEmail;
dynamic receiverTitle;
dynamic receiverPhone;
String? title;
String? callType;
factory CallDataModel.fromRawJson(String str) => CallDataModel.fromJson(json.decode(str)); factory CallDataModel.fromRawJson(String str) => CallDataModel.fromJson(json.decode(str));
@ -27,171 +39,92 @@ class CallDataModel {
factory CallDataModel.fromJson(Map<String, dynamic> json) => CallDataModel( factory CallDataModel.fromJson(Map<String, dynamic> json) => CallDataModel(
callerId: json["callerID"], callerId: json["callerID"],
callerDetails: json["callerDetails"] == null ? null : CallerDetails.fromJson(json["callerDetails"]), callerName: json["callerName"],
callerEmail: json["callerEmail"],
callerTitle: json["callerTitle"],
callerPhone: json["callerPhone"],
receiverId: json["receiverID"], receiverId: json["receiverID"],
receiverDetails: json["receiverDetails"] == null ? null : ReceiverDetails.fromJson(json["receiverDetails"]), receiverName: json["receiverName"],
receiverEmail: json["receiverEmail"],
receiverTitle: json["receiverTitle"],
receiverPhone: json["receiverPhone"],
title: json["title"], title: json["title"],
calltype: json["calltype"], callType: json["callType"],
); );
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
"callerID": callerId, "callerID": callerId,
"callerDetails": callerDetails?.toJson(), "callerName": callerName,
"callerEmail": callerEmail,
"callerTitle": callerTitle,
"callerPhone": callerPhone,
"receiverID": receiverId, "receiverID": receiverId,
"receiverDetails": receiverDetails?.toJson(), "receiverName": receiverName,
"receiverEmail": receiverEmail,
"receiverTitle": receiverTitle,
"receiverPhone": receiverPhone,
"title": title, "title": title,
"calltype": calltype, "callType": callType,
}; };
} }
class CallerDetails {
CallerDetails({
this.response,
this.errorResponses,
});
Response? response;
dynamic errorResponses;
factory CallerDetails.fromRawJson(String str) => CallerDetails.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory CallerDetails.fromJson(Map<String, dynamic> json) => CallerDetails( // To parse this JSON data, do
response: json["response"] == null ? null : Response.fromJson(json["response"]), //
errorResponses: json["errorResponses"], // final callSessionPayLoad = callSessionPayLoadFromJson(jsonString);
);
Map<String, dynamic> toJson() => {
"response": response?.toJson(),
"errorResponses": errorResponses,
};
}
class Response { class CallSessionPayLoad {
Response({ CallSessionPayLoad({
this.id, this.target,
this.userName, this.caller,
this.email, this.sdp,
this.phone,
this.title,
this.token,
this.isDomainUser,
this.isActiveCode,
this.encryptedUserId,
this.encryptedUserName,
}); });
int? id; int? target;
String? userName; int? caller;
String? email; Sdp? sdp;
dynamic phone;
String? title;
String? token;
bool? isDomainUser;
bool? isActiveCode;
String? encryptedUserId;
String? encryptedUserName;
factory Response.fromRawJson(String str) => Response.fromJson(json.decode(str)); factory CallSessionPayLoad.fromRawJson(String str) => CallSessionPayLoad.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson()); String toRawJson() => json.encode(toJson());
factory Response.fromJson(Map<String, dynamic> json) => Response( factory CallSessionPayLoad.fromJson(Map<String, dynamic> json) => CallSessionPayLoad(
id: json["id"], target: json["target"],
userName: json["userName"], caller: json["caller"],
email: json["email"], sdp: json["sdp"] == null ? null : Sdp.fromJson(json["sdp"]),
phone: json["phone"],
title: json["title"],
token: json["token"],
isDomainUser: json["isDomainUser"],
isActiveCode: json["isActiveCode"],
encryptedUserId: json["encryptedUserId"],
encryptedUserName: json["encryptedUserName"],
); );
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
"id": id, "target": target,
"userName": userName, "caller": caller,
"email": email, "sdp": sdp?.toJson(),
"phone": phone,
"title": title,
"token": token,
"isDomainUser": isDomainUser,
"isActiveCode": isActiveCode,
"encryptedUserId": encryptedUserId,
"encryptedUserName": encryptedUserName,
}; };
} }
class ReceiverDetails { class Sdp {
ReceiverDetails({ Sdp({
this.id, this.type,
this.userName, this.sdp,
this.email,
this.phone,
this.title,
this.userStatus,
this.image,
this.unreadMessageCount,
this.userAction,
this.isPin,
this.isFav,
this.isAdmin,
this.rKey,
this.totalCount,
}); });
int? id; String? type;
String? userName; String? sdp;
String? email;
dynamic phone; factory Sdp.fromRawJson(String str) => Sdp.fromJson(json.decode(str));
dynamic title;
int? userStatus;
String? image;
int? unreadMessageCount;
dynamic userAction;
bool? isPin;
bool? isFav;
bool? isAdmin;
String? rKey;
int? totalCount;
factory ReceiverDetails.fromRawJson(String str) => ReceiverDetails.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson()); String toRawJson() => json.encode(toJson());
factory ReceiverDetails.fromJson(Map<String, dynamic> json) => ReceiverDetails( factory Sdp.fromJson(Map<String, dynamic> json) => Sdp(
id: json["id"], type: json["type"],
userName: json["userName"], sdp: json["sdp"],
email: json["email"],
phone: json["phone"],
title: json["title"],
userStatus: json["userStatus"],
image: json["image"],
unreadMessageCount: json["unreadMessageCount"],
userAction: json["userAction"],
isPin: json["isPin"],
isFav: json["isFav"],
isAdmin: json["isAdmin"],
rKey: json["rKey"],
totalCount: json["totalCount"],
); );
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
"id": id, "type": type,
"userName": userName, "sdp": sdp,
"email": email,
"phone": phone,
"title": title,
"userStatus": userStatus,
"image": image,
"unreadMessageCount": unreadMessageCount,
"userAction": userAction,
"isPin": isPin,
"isFav": isFav,
"isAdmin": isAdmin,
"rKey": rKey,
"totalCount": totalCount,
}; };
} }

@ -0,0 +1,61 @@
// To parse this JSON data, do
//
// final remoteIceCandidatePayLoad = remoteIceCandidatePayLoadFromJson(jsonString);
import 'dart:convert';
class RemoteIceCandidatePayLoad {
RemoteIceCandidatePayLoad({
this.target,
this.candidate,
});
int? target;
Candidate? candidate;
factory RemoteIceCandidatePayLoad.fromRawJson(String str) => RemoteIceCandidatePayLoad.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory RemoteIceCandidatePayLoad.fromJson(Map<String, dynamic> json) => RemoteIceCandidatePayLoad(
target: json["target"],
candidate: json["candidate"] == null ? null : Candidate.fromJson(json["candidate"]),
);
Map<String, dynamic> toJson() => {
"target": target,
"candidate": candidate?.toJson(),
};
}
class Candidate {
Candidate({
this.candidate,
this.sdpMid,
this.sdpMLineIndex,
this.usernameFragment,
});
String? candidate;
String? sdpMid;
int? sdpMLineIndex;
String? usernameFragment;
factory Candidate.fromRawJson(String str) => Candidate.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Candidate.fromJson(Map<String, dynamic> json) => Candidate(
candidate: json["candidate"],
sdpMid: json["sdpMid"],
sdpMLineIndex: json["sdpMLineIndex"],
usernameFragment: json["usernameFragment"],
);
Map<String, dynamic> toJson() => {
"candidate": candidate,
"sdpMid": sdpMid,
"sdpMLineIndex": sdpMLineIndex,
"usernameFragment": usernameFragment,
};
}

@ -1,36 +1,62 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:ui'; import 'dart:io';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:just_audio/just_audio.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/chat/call.dart';
import 'package:mohem_flutter_app/models/chat/webrtc_payloads.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/ui/chat/call/chat_incoming_call_screen.dart';
import 'package:mohem_flutter_app/ui/chat/call/start_call_screen.dart';
import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart';
class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
///////////////////// Web RTC Video Calling ////////////////////// ///////////////////// Web RTC Video Calling //////////////////////
// Video Call // Video Call
late RTCPeerConnection _peerConnection; late RTCPeerConnection _pc;
RTCVideoRenderer _localVideoRenderer = RTCVideoRenderer(); late ChatProviderModel chatProvModel;
final RTCVideoRenderer _remoteRenderer = RTCVideoRenderer(); RTCVideoRenderer localVideoRenderer = RTCVideoRenderer();
RTCVideoRenderer remoteRenderer = RTCVideoRenderer();
final AudioPlayer player = AudioPlayer();
late MediaStream localStream;
late CallDataModel outGoingCallData;
bool isMicOff = false;
bool isLoudSpeaker = false;
bool isCamOff = false;
bool isCallEnded = false;
bool isVideoCall = false;
bool isCallStarted = false;
bool isFrontCamera = true;
/// WebRTC Connection Variables
bool _offer = false;
MediaStream? _localStream; late BuildContext providerContext;
MediaStream? _remoteStream;
void initCallListeners() { void initCallListeners({required BuildContext context}) {
providerContext = context;
if (kDebugMode) {
print("=================== Call Listeners Registered =======================");
}
chatHubConnection.on("OnCallAcceptedAsync", onCallAcceptedAsync); chatHubConnection.on("OnCallAcceptedAsync", onCallAcceptedAsync);
chatHubConnection.on("OnIceCandidateAsync", onIceCandidateAsync); chatHubConnection.on("OnIceCandidateAsync", onIceCandidateAsync);
chatHubConnection.on("OnOfferAsync", onOfferAsync); chatHubConnection.on("OnOfferAsync", onOfferAsync);
chatHubConnection.on("OnAnswerOffer", onAnswerOffer); chatHubConnection.on("OnAnswerOffer", onAnswerOffer);
chatHubConnection.on("OnHangUpAsync", onHangUpAsync); chatHubConnection.on("OnHangUpAsync", onHangUpAsync);
chatHubConnection.on("OnCallDeclinedAsync", onCallDeclinedAsync); chatHubConnection.on("OnCallDeclinedAsync", onCallDeclinedAsync);
chatHubConnection.on("OnIncomingCallAsync", OnIncomingCallAsync);
} }
//Video Constraints //Video Constraints
var videoConstraints = { Map<String, Object> videoConstraints = {
"video": { "video": {
"mandatory": { "mandatory": {
"width": {"min": 320}, "width": {"min": 1280},
"height": {"min": 180} "height": {"min": 720}
}, },
"optional": [ "optional": [
{ {
@ -41,12 +67,13 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
] ]
}, },
"frameRate": 25, "frameRate": 25,
"width": 420, //420,//640,//1280, "width": 1280, //420,//640,//1280,
"height": 240 //240//480//720 "height": 720, //240//480//720
"audio": true,
}; };
// Audio Constraints // Audio Constraints
var audioConstraints = { Map<String, Object> audioConstraints = {
"sampleRate": 8000, "sampleRate": 8000,
"sampleSize": 16, "sampleSize": 16,
"channelCount": 2, "channelCount": 2,
@ -54,128 +81,200 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
"audio": true, "audio": true,
}; };
Future<RTCPeerConnection> _createPeerConnection() async { Future<void> init() async {
// {"url": "stun:stun.l.google.com:19302"}, creatOfferWithCon();
Map<String, dynamic> configuration = { }
"iceServers": [
{"urls": 'stun:15.185.116.59:3478'},
{"urls": "turn:15.185.116.59:3479", "username": "admin", "credential": "admin"}
]
};
Map<String, dynamic> offerSdpConstraints = { Future<void> initLocalCamera({required ChatProviderModel chatProvmodel, required callData, required BuildContext context, bool isIncomingCall = false}) async {
"mandatory": { isCallEnded = false;
"OfferToReceiveAudio": true, chatProvModel = chatProvmodel;
"OfferToReceiveVideo": true, outGoingCallData = callData;
},
"optional": [],
};
RTCPeerConnection pc = await createPeerConnection(configuration, offerSdpConstraints); await localVideoRenderer.initialize();
// if (pc != null) print(pc); localStream = await navigator.mediaDevices.getUserMedia(isVideoCall ? videoConstraints : audioConstraints);
//pc.addStream(widget.localStream); localVideoRenderer.srcObject = localStream;
await remoteRenderer.initialize();
// playRingtone();
await startCall(callType: isVideoCall ? "Video" : "Audio", context: context);
_pc = await creatOfferWithCon();
notifyListeners();
}
pc.onIceCandidate = (e) { Future<void> startCall({required String callType, required BuildContext context}) async {
if (e.candidate != null) { chatProvModel.isTextMsg = true;
print(json.encode({ chatProvModel.isAttachmentMsg = false;
'candidate': e.candidate.toString(), chatProvModel.isVoiceMsg = false;
'sdpMid': e.sdpMid.toString(), chatProvModel.isReplyMsg = false;
'sdpMlineIndex': e.sdpMLineIndex, chatProvModel.isCall = true;
})); chatProvModel.message.text = "Start $callType call ${outGoingCallData.receiverName.toString().replaceAll(".", " ")}";
} chatProvModel.sendChatMessage(
}; context,
pc.onIceConnectionState = (e) { targetUserId: outGoingCallData.receiverId!,
print(e); userStatus: 1,
}; userEmail: outGoingCallData.receiverEmail!,
pc.onAddStream = (stream) { targetUserName: outGoingCallData.receiverName!,
print('addStream: ' + stream.id); );
_remoteRenderer.srcObject = stream; await invoke(
}; invokeMethod: "CallUserAsync",
return pc; currentUserID: outGoingCallData.callerId!,
targetUserID: outGoingCallData.receiverId!,
);
await invoke(invokeMethod: "UpdateUserStatusAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, userStatus: 4);
} }
void init() { Future<bool> endCall() async {
initRenderers(); await invoke(invokeMethod: "UpdateUserStatusAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, userStatus: 1);
_createPeerConnection().then((pc) { await invoke(invokeMethod: "HangUpAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, userStatus: 1);
_peerConnection = pc; _pc.dispose();
// _setRemoteDescription(widget.info); isCallStarted = false;
}); isVideoCall = false;
isCamOff = false;
isMicOff = false;
isLoudSpeaker = false;
localVideoRenderer.srcObject = null;
remoteRenderer.srcObject = null;
//player.stop();
_offer = false;
return true;
} }
void initRenderers() { Future<void> startIncomingCall() async {
_localVideoRenderer.initialize(); await localVideoRenderer.initialize();
_remoteRenderer.initialize(); localStream = await navigator.mediaDevices.getUserMedia(isVideoCall ? videoConstraints : audioConstraints);
initLocalCamera(); localVideoRenderer.srcObject = localStream;
await remoteRenderer.initialize();
} }
void initLocalCamera() async { // OutGoing Listeners
_localStream = await navigator.mediaDevices.getUserMedia({'video': true, 'audio': true}); void onCallAcceptedAsync(List<Object?>? params) async {
_localVideoRenderer.srcObject = _localStream; print("--------------------- On Call Accept ---------------------------------------");
// _localVideoRenderer.srcObject = await navigator.mediaDevices dynamic items = params!.toList();
// .getUserMedia({'video': true, 'audio': true}); RTCSessionDescription description = await _createOffer();
print('this source Object'); await _pc.setLocalDescription(description);
print('this suarce ${_localVideoRenderer.srcObject != null}'); var payload = {"target": items[0]["id"], "caller": outGoingCallData.callerId, "sdp": description.toMap()};
notifyListeners(); invoke(invokeMethod: "OfferAsync", currentUserID: outGoingCallData.callerId!, targetUserID: items[0]["id"], data: jsonEncode(payload));
} }
void startCall({required String callType}) {} Future<void> onIceCandidateAsync(List<Object?>? params) async {
print("--------------------- onIceCandidateAsync ---------------------------------------");
void endCall() {} var items = params!.toList();
if (kDebugMode) {
void checkCall(Map<String, dynamic> message) { logger.i("res: " + items.toString());
switch (message["callStatus"]) { }
case 'connected': RemoteIceCandidatePayLoad data = RemoteIceCandidatePayLoad.fromJson(jsonDecode(items.first.toString()));
{} if (_pc != null) {
break; await _pc.addCandidate(RTCIceCandidate(data.candidate!.candidate, data.candidate!.sdpMid, data.candidate!.sdpMLineIndex));
case 'offer': if (!isCallStarted) {
{} isCallStarted = true;
break; if (isCallStarted) {
case 'accept': Navigator.push(
{} providerContext,
break; MaterialPageRoute(
case 'candidate': builder: (BuildContext context) => StartCallPage(localRenderer: localVideoRenderer, remoteRenderer: remoteRenderer),
{} allowSnapshotting: false,
break; )).then((value) {
case 'bye': Navigator.of(providerContext).pop();
{} });
break; }
case 'leave': }
{}
break;
} }
notifyListeners();
} }
//// Listeners Methods //// void onOfferAsync(List<Object?>? params) {
print("--------------------- onOfferAsync ---------------------------------------");
}
void onCallAcceptedAsync(List<Object?>? params) {} // Incoming Listeners
void onIceCandidateAsync(List<Object?>? params) {} void onAnswerOffer(List<Object?>? payload) async {
print("--------------------- On Answer Offer Async ---------------------------------------");
var items = payload!.toList();
if (kDebugMode) {
logger.i("res: " + items.toString());
}
CallSessionPayLoad data = CallSessionPayLoad.fromJson(jsonDecode(items.first.toString()));
RTCSessionDescription description = RTCSessionDescription(data.sdp!.sdp, 'answer');
_pc.setRemoteDescription(description);
}
void onOfferAsync(List<Object?>? params) {} void onHangUpAsync(List<Object?>? params) {
print("--------------------- onHangUp ---------------------------------------");
endCall().then((bool value) {
isCallEnded = true;
notifyListeners();
});
}
void onAnswerOffer(List<Object?>? params) {} Future<void> OnIncomingCallAsync(List<Object?>? params) async {
print("--------------------- On Incoming Call ---------------------------------------");
dynamic items = params!.toList();
logger.d(items);
Map<String, dynamic> json = {
"callerID": items[0]["id"],
"callerName": items[0]["userName"],
"callerEmail": items[0]["email"],
"callerTitle": items[0]["title"],
"callerPhone": null,
"receiverID": AppState().chatDetails!.response!.id,
"receiverName": AppState().chatDetails!.response!.userName,
"receiverEmail": AppState().chatDetails!.response!.email,
"receiverTitle": AppState().chatDetails!.response!.title,
"receiverPhone": AppState().chatDetails!.response!.phone,
"title": AppState().chatDetails!.response!.userName!.replaceAll(".", " "),
"callType": items[1] ? "Video" : "Audio",
};
void onHangUpAsync(List<Object?>? params) {} CallDataModel callData = CallDataModel.fromJson(json);
await Navigator.push(
providerContext,
MaterialPageRoute(
builder: (BuildContext context) =>
IncomingCall(
isVideoCall: items[1] ? true : false,
outGoingCallData: callData,
),
),
);
}
void onCallDeclinedAsync(List<Object?>? params) {} void onCallDeclinedAsync(List<Object?>? params) {
print("--------------------- on Call Declined ---------------------------------------");
endCall().then((bool value) {
if (value) {
isCallEnded = true;
notifyListeners();
}
});
}
//// Invoke Methods //// Invoke Methods
Future<void> invoke({required String invokeMethod, required String currentUserID, required String targetUserID, bool isVideoCall = false, var data}) async { Future<void> invoke({required String invokeMethod, required int currentUserID, required int targetUserID, var data, int userStatus = 1}) async {
List<Object> args = []; List<Object> args = [];
if (invokeMethod == "answerCallAsync") { // logger.w(currentUserID.toString() + " -- " + targetUserID.toString() + " -- " + isVideoCall.toString());
args = [currentUserID, targetUserID]; if (invokeMethod == "CallUserAsync") {
} else if (invokeMethod == "CallUserAsync") {
args = [currentUserID, targetUserID, isVideoCall]; args = [currentUserID, targetUserID, isVideoCall];
} else if (invokeMethod == "answerCallAsync") {
args = [currentUserID, targetUserID];
} else if (invokeMethod == "IceCandidateAsync") { } else if (invokeMethod == "IceCandidateAsync") {
args = [targetUserID, data]; args = [targetUserID, data];
} else if (invokeMethod == "OfferAsync") { } else if (invokeMethod == "OfferAsync") {
args = [targetUserID, data]; args = [targetUserID, data];
} else if (invokeMethod == "AnswerOfferAsync") { } else if (invokeMethod == "AnswerOfferAsync") {
args = [targetUserID, data]; args = [targetUserID, data];
//json In Data // json In Data
} else if (invokeMethod == "UpdateUserStatusAsync") {
args = [currentUserID, userStatus];
} else if (invokeMethod == "HangUpAsync") {
args = [currentUserID, targetUserID];
}
logger.d(args);
try {
await chatHubConnection.invoke("$invokeMethod", args: args);
} catch (e) {
logger.w(e);
} }
await chatHubConnection.invoke(invokeMethod, args: args);
} }
void stopListeners() async { void stopListeners() async {
@ -184,4 +283,139 @@ class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin {
chatHubConnection.off('OnIceCandidateAsync'); chatHubConnection.off('OnIceCandidateAsync');
chatHubConnection.off('OnAnswerOffer'); chatHubConnection.off('OnAnswerOffer');
} }
void disposeRenders() async {
await localVideoRenderer.dispose();
localStream.dispose();
notifyListeners();
}
Future<RTCPeerConnection> creatOfferWithCon() async {
Map<String, dynamic> configuration = {
"sdpSemantics": "plan-b",
'iceServers': [
{
'urls': 'stun:15.185.116.59:3478',
},
{
'urls': 'turn:15.185.116.59:3479',
'username': 'admin',
'credential': 'admin',
},
]
};
Map<String, dynamic> offerSdpConstraints = {
'mandatory': {
'OfferToReceiveAudio': true,
'OfferToReceiveVideo': true,
},
'optional': []
};
RTCPeerConnection pc = await createPeerConnection(configuration, offerSdpConstraints);
await pc!.addStream(localStream!);
pc?.onConnectionState = (RTCPeerConnectionState state) {};
pc?.onAddStream = (MediaStream stream) {
remoteRenderer.srcObject = stream;
notifyListeners();
};
pc!.onIceCandidate = (RTCIceCandidate e) async {
if (e.candidate != null) {
var payload = {"target": outGoingCallData.callerId, "candidate": e.toMap()};
logger.i("Candidate:" + e.toMap().toString());
await invoke(invokeMethod: "IceCandidateAsync", currentUserID: outGoingCallData.callerId!, targetUserID: outGoingCallData.receiverId!, data: jsonEncode(payload));
}
};
// pc!.onTrack = (RTCTrackEvent event) async {
//
// String streamId = const Uuid().toString();
// MediaStream remoteStream = await createLocalMediaStream(streamId);
// event.streams[0].getTracks().forEach((MediaStreamTrack element) {
// logger.i("Stream Track: " + element.id.toString());
// // remoteRenderer.srcObject = element;
// remoteStream.addTrack(element);
// });
// };
pc!.onSignalingState = (RTCSignalingState state) {
logger.i("signaling state: " + state.name);
};
pc!.onIceGatheringState = (RTCIceGatheringState state) {
logger.i("rtc ice gathering state: " + state.name);
};
pc!.onIceConnectionState = (RTCIceConnectionState state) {
logger.i("rtc ice connection state: " + state.name);
};
pc!.onRenegotiationNeeded = () {};
return pc;
}
void playRingtone() async {
player.stop();
await player.setVolume(1.0);
String audioAsset = "";
if (Platform.isAndroid) {
audioAsset = "assets/audio/ring_60Sec.mp3";
} else {
audioAsset = "assets/audio/ring_30Sec.caf";
}
try {
await player.setAsset(audioAsset);
await player.load();
player.play();
} catch (e) {
print("Error: $e");
}
}
Future<RTCSessionDescription> _createOffer() async {
RTCSessionDescription description = await _pc!.createOffer();
_offer = true;
return description;
}
// Future<RTCSessionDescription> _createAnswer() async {
// RTCSessionDescription description = await _pc!.createAnswer();
// var session = description.sdp.toString();
// return description;
// _pc!.setLocalDescription(description);
// }
void micOff() {
isMicOff = !isMicOff;
localStream.getAudioTracks().forEach((track) {
track.enabled = !track.enabled;
});
notifyListeners();
}
void camOff() {
isCamOff = !isCamOff;
localStream.getVideoTracks().forEach((track) {
track.enabled = !track.enabled;
});
if (isCamOff) {
isVideoCall = false;
} else {
isVideoCall = true;
}
notifyListeners();
}
void loudOn() {
isLoudSpeaker = !isLoudSpeaker;
remoteRenderer.srcObject?.getAudioTracks().forEach((track) {
if (isLoudSpeaker) {
track.enableSpeakerphone(true);
} else {
track.enableSpeakerphone(false);
}
});
notifyListeners();
}
void switchCamera() {
isFrontCamera = !isFrontCamera;
print("================= Camera Switch Triggered ===================");
Helper.switchCamera(localStream.getVideoTracks()[0]);
notifyListeners();
}
} }

@ -24,12 +24,14 @@ import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.da
import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart' as userLoginToken; import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart' as userLoginToken;
import 'package:mohem_flutter_app/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav; import 'package:mohem_flutter_app/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav;
import 'package:mohem_flutter_app/models/my_team/get_employee_subordinates_list.dart'; import 'package:mohem_flutter_app/models/my_team/get_employee_subordinates_list.dart';
import 'package:mohem_flutter_app/provider/chat_call_provider.dart';
import 'package:mohem_flutter_app/ui/chat/chat_detailed_screen.dart'; import 'package:mohem_flutter_app/ui/chat/chat_detailed_screen.dart';
import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart';
import 'package:mohem_flutter_app/widgets/image_picker.dart'; import 'package:mohem_flutter_app/widgets/image_picker.dart';
import 'package:open_file/open_file.dart'; import 'package:open_file/open_file.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
import 'package:signalr_netcore/hub_connection.dart'; import 'package:signalr_netcore/hub_connection.dart';
import 'package:signalr_netcore/signalr_client.dart'; import 'package:signalr_netcore/signalr_client.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
@ -80,6 +82,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
int pageNo = 1; int pageNo = 1;
bool disbaleChatForThisUser = false; bool disbaleChatForThisUser = false;
bool isCall = false;
Future<void> getUserAutoLoginToken() async { Future<void> getUserAutoLoginToken() async {
userLoginToken.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken(); userLoginToken.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken();
@ -95,7 +98,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
} }
} }
Future<void> buildHubConnection() async { Future<void> buildHubConnection({required BuildContext context, required ChatCallProvider ccProvider}) async {
chatHubConnection = await getHubConnection(); chatHubConnection = await getHubConnection();
await chatHubConnection.start(); await chatHubConnection.start();
if (kDebugMode) { if (kDebugMode) {
@ -103,6 +106,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
} }
chatHubConnection.on("OnDeliveredChatUserAsync", onMsgReceived); chatHubConnection.on("OnDeliveredChatUserAsync", onMsgReceived);
chatHubConnection.on("OnGetChatConversationCount", onNewChatConversion); chatHubConnection.on("OnGetChatConversationCount", onNewChatConversion);
ccProvider.initCallListeners(context: context);
} }
Future<HubConnection> getHubConnection() async { Future<HubConnection> getHubConnection() async {
@ -124,6 +128,15 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
chatHubConnection.on("OnGetUserChatHistoryNotDeliveredAsync", chatNotDelivered); chatHubConnection.on("OnGetUserChatHistoryNotDeliveredAsync", chatNotDelivered);
chatHubConnection.on("OnUpdateUserChatHistoryStatusAsync", updateUserChatStatus); chatHubConnection.on("OnUpdateUserChatHistoryStatusAsync", updateUserChatStatus);
// Calling
// chatHubConnection.on("OnCallAcceptedAsync", callP.onCallAcceptedAsync);
// chatHubConnection.on("OnIceCandidateAsync", callP.onIceCandidateAsync);
// chatHubConnection.on("OnOfferAsync", callP.onOfferAsync);
// chatHubConnection.on("OnAnswerOffer", callP.onAnswerOffer);
// chatHubConnection.on("OnHangUpAsync", callP.onHangUpAsync);
// chatHubConnection.on("OnCallDeclinedAsync", callP.onCallDeclinedAsync);
if (kDebugMode) { if (kDebugMode) {
logger.i("All listeners registered"); logger.i("All listeners registered");
} }
@ -354,6 +367,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
Future<void> onMsgReceived(List<Object?>? parameters) async { Future<void> onMsgReceived(List<Object?>? parameters) async {
List<SingleUserChatModel> data = [], temp = []; List<SingleUserChatModel> data = [], temp = [];
for (dynamic msg in parameters!) { for (dynamic msg in parameters!) {
data = getSingleUserChatModel(jsonEncode(msg)); data = getSingleUserChatModel(jsonEncode(msg));
temp = getSingleUserChatModel(jsonEncode(msg)); temp = getSingleUserChatModel(jsonEncode(msg));
@ -363,7 +377,6 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
data.first.currentUserId = temp.first.targetUserId; data.first.currentUserId = temp.first.targetUserId;
data.first.currentUserName = temp.first.targetUserName; data.first.currentUserName = temp.first.targetUserName;
data.first.currentUserEmail = temp.first.targetUserEmail; data.first.currentUserEmail = temp.first.targetUserEmail;
if (data.first.fileTypeId == 12 || data.first.fileTypeId == 4 || data.first.fileTypeId == 3) { if (data.first.fileTypeId == 12 || data.first.fileTypeId == 4 || data.first.fileTypeId == 3) {
data.first.image = await ChatApiClient().downloadURL(fileName: data.first.contant!, fileTypeDescription: data.first.fileTypeResponse!.fileTypeDescription ?? "image/jpg"); data.first.image = await ChatApiClient().downloadURL(fileName: data.first.contant!, fileTypeDescription: data.first.fileTypeResponse!.fileTypeDescription ?? "image/jpg");
} }
@ -429,10 +442,6 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
} }
void OnSubmitChatAsync(List<Object?>? parameters) { void OnSubmitChatAsync(List<Object?>? parameters) {
print(isChatScreenActive);
print(receiverID);
print(isChatScreenActive);
logger.i(parameters);
List<SingleUserChatModel> data = [], temp = []; List<SingleUserChatModel> data = [], temp = [];
for (dynamic msg in parameters!) { for (dynamic msg in parameters!) {
data = getSingleUserChatModel(jsonEncode(msg)); data = getSingleUserChatModel(jsonEncode(msg));
@ -632,7 +641,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
return; return;
} }
sendChatToServer( sendChatToServer(
chatEventId: 1, chatEventId: isCall ? 3 : 1,
fileTypeId: null, fileTypeId: null,
targetUserId: targetUserId, targetUserId: targetUserId,
targetUserName: targetUserName, targetUserName: targetUserName,
@ -1030,6 +1039,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
favUsersList.clear(); favUsersList.clear();
searchedChats?.clear(); searchedChats?.clear();
pChatHistory?.clear(); pChatHistory?.clear();
// callP.stopListeners();
chatHubConnection.stop(); chatHubConnection.stop();
AppState().chatDetails = null; AppState().chatDetails = null;
} }

@ -1,381 +1,233 @@
import 'dart:convert';
import 'dart:ui'; import 'dart:ui';
import 'package:camera/camera.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/chat/call.dart'; import 'package:mohem_flutter_app/models/chat/call.dart';
import 'package:mohem_flutter_app/provider/chat_call_provider.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:provider/provider.dart';
class IncomingCall extends StatefulWidget { class IncomingCall extends StatefulWidget {
CallDataModel incomingCallData; CallDataModel outGoingCallData;
bool? isVideoCall; bool isVideoCall;
IncomingCall({Key? key, required this.incomingCallData, this.isVideoCall}) : super(key: key); IncomingCall({Key? key, required this.outGoingCallData, required this.isVideoCall}) : super(key: key);
@override @override
_IncomingCallState createState() => _IncomingCallState(); _IncomingCallState createState() => _IncomingCallState();
} }
class _IncomingCallState extends State<IncomingCall> with SingleTickerProviderStateMixin { class _IncomingCallState extends State<IncomingCall> with SingleTickerProviderStateMixin {
AnimationController? _animationController; late ChatCallProvider callProvider;
CameraController? _controller; late ChatProviderModel chatProvider;
Future<void>? _initializeControllerFuture;
bool isCameraReady = false;
@override @override
void initState() { void initState() {
_animationController = AnimationController( chatProvider = Provider.of<ChatProviderModel>(context, listen: false);
vsync: this, callProvider = Provider.of<ChatCallProvider>(context, listen: false);
duration: const Duration( init();
milliseconds: 500,
),
);
//_runAnimation();
// connectSignaling();
WidgetsBinding.instance.addPostFrameCallback(
(_) => _runAnimation(),
);
super.initState(); super.initState();
} }
void init() {
widget.isVideoCall ? callProvider.isVideoCall = true : callProvider.isVideoCall = false;
callProvider.initLocalCamera(chatProvmodel: chatProvider, callData: widget.outGoingCallData, context: context, isIncomingCall: true);
callProvider.init();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
body: FutureBuilder<void>( body: Consumer<ChatCallProvider>(builder: (BuildContext context, ChatCallProvider chatcp, Widget? child) {
future: _initializeControllerFuture, if (chatcp.isCallEnded) Navigator.pop(context);
builder: (BuildContext context, AsyncSnapshot<void> snapshot) { return Stack(
if (snapshot.connectionState == ConnectionState.done) { alignment: FractionalOffset.center,
return Stack( children: <Widget>[
alignment: FractionalOffset.center, if (widget.isVideoCall)
children: <Widget>[ Positioned.fill(
if (widget.isVideoCall!) child: RTCVideoView(
Positioned.fill( callProvider.localVideoRenderer,
child: AspectRatio( objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
aspectRatio: _controller!.value.aspectRatio, ),
child: CameraPreview( ),
_controller!, Positioned.fill(
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
child: Container(
decoration: BoxDecoration(
color: MyColors.grey57Color.withOpacity(
0.3,
), ),
), ),
), child: Column(
Positioned.fill( crossAxisAlignment: CrossAxisAlignment.start,
child: ClipRect( mainAxisSize: MainAxisSize.max,
child: BackdropFilter( children: <Widget>[
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), 40.height,
child: Container( Row(
decoration: BoxDecoration( crossAxisAlignment: CrossAxisAlignment.center,
color: MyColors.grey57Color.withOpacity( mainAxisAlignment: MainAxisAlignment.center,
0.7,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[ children: <Widget>[
Container( Container(
margin: const EdgeInsets.all(21.0), margin: const EdgeInsets.all(21.0),
child: Row( child: Container(
children: <Widget>[ margin: const EdgeInsets.only(
Image.asset( left: 10.0,
"assets/images/logos/main_mohemm_logo.png", right: 10.0,
height: 70, ),
width: 70, child: Column(
), crossAxisAlignment: CrossAxisAlignment.center,
Container( mainAxisSize: MainAxisSize.min,
margin: const EdgeInsets.only( mainAxisAlignment: MainAxisAlignment.spaceAround,
left: 10.0, children: <Widget>[
right: 10.0, SvgPicture.asset(
), "assets/images/user.svg",
child: Column( height: 70,
crossAxisAlignment: CrossAxisAlignment.start, width: 70,
mainAxisSize: MainAxisSize.min, fit: BoxFit.cover,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: const <Widget>[
// todo @aamir, need to use extension mehtods
Text(
"Aamir Saleem Ahmad",
style: TextStyle(
fontSize: 21,
fontWeight: FontWeight.bold,
color: MyColors.white,
letterSpacing: -1.26,
height: 23 / 12,
),
),
Text(
"Calling...",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(
0xffC6C6C6,
),
letterSpacing: -0.48,
height: 23 / 24,
),
),
SizedBox(
height: 2,
),
],
), ),
), 10.height,
], Text(
), widget.outGoingCallData.receiverName.toString().replaceAll(".", " "),
), style: const TextStyle(
// Container( fontSize: 21,
// margin: const EdgeInsets.all(21.0), fontWeight: FontWeight.bold,
// width: MediaQuery.of(context).size.width,
// decoration: cardRadius(15.0, color: MyColors.black, elevation: null),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisSize: MainAxisSize.min,
// children: [
// Container(
// padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 6.0),
// child: Text(
// "TranslationBase.of(context).appoInfo",
// style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: MyColors.white, letterSpacing: -0.64, height: 23 / 12),
// ),
// ),
// Container(
// padding: const EdgeInsets.only(left: 16.0, right: 16.0),
// child: Text(
// "widget.incomingCallData.appointmentdate + widget.incomingCallData.appointmenttime",
// style: TextStyle(fontSize: 12.0, letterSpacing: -0.48, color: Color(0xff8E8E8E), fontWeight: FontWeight.w600),
// ),
// ),
// Container(
// padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 21.0),
// child: Text(
// "widget.incomingCallData.clinicname",
// style: TextStyle(fontSize: 12.0, letterSpacing: -0.48, color: Color(0xff8E8E8E), fontWeight: FontWeight.w600),
// ),
// ),
// ],
// ),
// ),
const Spacer(),
Container(
margin: const EdgeInsets.only(
bottom: 70.0,
left: 49,
right: 49,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
RotationTransition(
turns: Tween(
begin: 0.0,
end: -.1,
)
.chain(
CurveTween(
curve: Curves.elasticIn,
),
)
.animate(
_animationController!,
),
child: RawMaterialButton(
onPressed: () {
_submit();
},
elevation: 2.0,
fillColor: MyColors.green2DColor,
padding: const EdgeInsets.all(
15.0,
),
shape: const CircleBorder(),
child: const Icon(
Icons.call,
color: MyColors.white, color: MyColors.white,
size: 35.0, letterSpacing: -1.26,
height: 23 / 12,
), ),
), ),
), const Text(
RawMaterialButton( "Ringing...",
onPressed: () { style: TextStyle(
backToHome(); fontSize: 16,
}, fontWeight: FontWeight.w600,
elevation: 2.0, color: Color(
fillColor: MyColors.redA3Color, 0xffC6C6C6,
padding: const EdgeInsets.all( ),
15.0, letterSpacing: -0.48,
height: 23 / 24,
),
), ),
shape: const CircleBorder(), const SizedBox(
child: const Icon( height: 2,
Icons.call_end,
color: MyColors.white,
size: 35.0,
), ),
), ],
], ),
), ),
), ),
], ],
), ),
), const Spacer(),
Container(
margin: const EdgeInsets.only(
bottom: 70.0,
left: 49,
right: 49,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
if (widget.isVideoCall)
RawMaterialButton(
onPressed: () {
callProvider.camOff();
},
elevation: 2.0,
fillColor: callProvider.isCamOff ? MyColors.green2DColor : Colors.grey,
padding: const EdgeInsets.all(
15.0,
),
shape: const CircleBorder(),
child: Icon(
callProvider.isCamOff ? Icons.videocam_off : Icons.videocam,
color: MyColors.white,
size: 35.0,
),
)
else
RawMaterialButton(
onPressed: () {
callProvider.loudOn();
},
elevation: 2.0,
fillColor: callProvider.isLoudSpeaker ? MyColors.green2DColor : Colors.grey,
padding: const EdgeInsets.all(
15.0,
),
shape: const CircleBorder(),
child: const Icon(
Icons.volume_up,
color: MyColors.white,
size: 35.0,
),
),
RawMaterialButton(
onPressed: () {
callProvider.micOff();
},
elevation: 2.0,
fillColor: callProvider.isMicOff ? MyColors.green2DColor : Colors.grey,
padding: const EdgeInsets.all(
15.0,
),
shape: const CircleBorder(),
child: Icon(
callProvider.isMicOff ? Icons.mic_off : Icons.mic,
color: MyColors.white,
size: 35.0,
),
),
RawMaterialButton(
onPressed: () {
callProvider.endCall().then((value) {
if (value) {
Navigator.of(context).pop();
}
});
},
elevation: 2.0,
fillColor: MyColors.redA3Color,
padding: const EdgeInsets.all(
15.0,
),
shape: const CircleBorder(),
child: const Icon(
Icons.call_end,
color: MyColors.white,
size: 35.0,
),
),
],
),
),
],
), ),
), ),
), ),
], ),
); ),
} else { ],
return const Center( );
child: CircularProgressIndicator(), }),
);
}
},
),
); );
} }
void _runAnimation() async {
List<CameraDescription> cameras = await availableCameras();
CameraDescription firstCamera = cameras[1];
_controller = CameraController(
firstCamera,
ResolutionPreset.medium,
);
_initializeControllerFuture = _controller!.initialize();
setState(() {});
// setAudioFile();
for (int i = 0; i < 100; i++) {
await _animationController!.forward();
await _animationController!.reverse();
}
}
Future<void> _submit() async {
try {
// backToHome();
// final roomModel = RoomModel(name: widget.incomingCallData.name, token: widget.incomingCallData.sessionId, identity: widget.incomingCallData.identity);
await _controller?.dispose();
// changeCallStatusAPI(4);
// if (_session != null && _signaling != null) {
// await Navigator.of(context).pushReplacement(
// MaterialPageRoute(
// // fullscreenDialog: true,
// builder: (BuildContext context) {
// // if (widget.incomingCallData.isWebRTC == "true") {
// return StartVideoCall(signaling: _signaling, session: _session);
//
// // else {
// // return OpenTokConnectCallPage(apiKey: OPENTOK_API_KEY, sessionId: widget.incomingCallData.sessionId, token: widget.incomingCallData.token);
// // }
//
// // return VideoCallWebPage(receiverId: widget.incomingCallData.receiverID, callerId: widget.incomingCallData.callerID); // Web WebRTC VideoCall
//
// // return CallHomePage(receiverId: widget.incomingCallData.receiverID, callerId: widget.incomingCallData.callerID); // App WebRTC VideoCall
// },
// ),
// );
// } else {
// // Invalid Params/Data
// Utils.showToast("Failed to establish connection with server");
// }
} catch (err) {
print(err);
// await PlatformExceptionAlertDialog(
// exception: err,
// ).show(context);
Utils.showToast(err.toString());
}
}
// void changeCallStatusAPI(int sessionStatus) {
// LiveCareService service = new LiveCareService();
// service.endCallAPI(widget.incomingCallData.sessionId, sessionStatus, context).then((res) {}).catchError((err) {
// print(err);
// });
// }
void backToHome() async {
// final connected = await signaling.declineCall(widget.incomingCallData.callerID, widget.incomingCallData.receiverID);
// LandingPage.isOpenCallPage = false;
// _signaling
_animationController!.dispose();
// player.stop();
// changeCallStatusAPI(3);
// _signaling.bye(_session, callRejected: true);
// _signaling.callDisconnected(_session, callRejected: true);
Navigator.of(context).pop();
}
//
// void disposeAudioResources() async {
// await player.dispose();
// }
//
// void setAudioFile() async {
// player.stop();
// await player.setVolume(1.0); // full volume
// try {
// await player.setAsset('assets/sounds/ring_60Sec.mp3').then((value) {
// player.setLoopMode(LoopMode.one); // loop ring sound
// player.play();
// }).catchError((err) {
// print("Error: $err");
// });
// } catch (e) {
// print("Error: $e");
// }
// }
//
// void connectSignaling({@required bool iAmCaller = false}) async {
// print("----------------- + Signaling Connection Started ---------------------------");
// var caller = widget.incomingCallData.callerID;
// var receiver = widget.incomingCallData.receiverID;
// var host = widget.incomingCallData.server;
//
// var selfRole = iAmCaller ? "Caller" : "Receiver";
// var selfId = iAmCaller ? caller : receiver;
// var selfUser = SocketUser(id: selfId, name: "$selfRole-$selfId", userAgent: DeviceInfo.userAgent, moreInfo: {});
//
// var remoteRole = !iAmCaller ? "Caller" : "Receiver";
// var remoteId = !iAmCaller ? caller : receiver;
// var remoteUser = SocketUser(id: remoteId, name: "$remoteRole-$remoteId", userAgent: DeviceInfo.userAgent, moreInfo: {});
//
// var sessionId = "$caller-$receiver";
// _session = SessionOneToOne(id: sessionId, local_user: selfUser, remote_user: remoteUser);
//
// _signaling = Signaling(host, session: _session);
// await _signaling.connect();
//
// if (_signaling.state == SignalingState.Open) {
// return;
// }
// }
BoxDecoration cardRadius(double radius, {required Color color, double? elevation}) { BoxDecoration cardRadius(double radius, {required Color color, double? elevation}) {
return BoxDecoration( return BoxDecoration(
shape: BoxShape.rectangle, shape: BoxShape.rectangle,
color: color ?? Colors.white, color: color ?? Colors.white,
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(Radius.circular(radius)),
Radius.circular(radius), boxShadow: <BoxShadow>[BoxShadow(color: const Color(0xff000000).withOpacity(.05), blurRadius: elevation ?? 27, offset: const Offset(-2, 3))],
),
boxShadow: <BoxShadow>[
BoxShadow(
color: const Color(
0xff000000,
).withOpacity(
.05,
),
//spreadRadius: 5,
blurRadius: elevation ?? 27,
offset: const Offset(
-2,
3,
),
),
],
); );
} }
} }

@ -1,16 +1,18 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:ui'; import 'dart:ui';
import 'package:camera/camera.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/chat/call.dart'; import 'package:mohem_flutter_app/models/chat/call.dart';
import 'package:mohem_flutter_app/provider/chat_call_provider.dart'; import 'package:mohem_flutter_app/provider/chat_call_provider.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/ui/chat/call/start_call_screen.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class OutGoingCall extends StatefulWidget { class OutGoingCall extends StatefulWidget {
@ -23,407 +25,242 @@ class OutGoingCall extends StatefulWidget {
_OutGoingCallState createState() => _OutGoingCallState(); _OutGoingCallState createState() => _OutGoingCallState();
} }
class _OutGoingCallState extends State<OutGoingCall> with SingleTickerProviderStateMixin { class _OutGoingCallState extends State<OutGoingCall>{
AnimationController? _animationController; late ChatCallProvider callProvider;
late CameraController controller; late ChatProviderModel chatProvider;
late List<CameraDescription> _cameras;
Future<void>? _initializeControllerFuture;
bool isCameraReady = false;
bool isMicOff = false;
bool isLoudSpeaker = false;
bool isCamOff = false;
late ChatCallProvider callProviderd;
@override @override
void initState() { void initState() {
callProviderd = Provider.of<ChatCallProvider>(context, listen: false); chatProvider = Provider.of<ChatProviderModel>(context, listen: false);
_animationController = AnimationController( callProvider = Provider.of<ChatCallProvider>(context, listen: false);
vsync: this, init();
duration: const Duration(
milliseconds: 500,
),
);
// _runAnimation();
// connectSignaling();
WidgetsBinding.instance.addPostFrameCallback(
(_) => _runAnimation(),
);
super.initState(); super.initState();
} }
void init() {
widget.isVideoCall ? callProvider.isVideoCall = true : callProvider.isVideoCall = false;
callProvider.initLocalCamera(chatProvmodel: chatProvider, callData: widget.outGoingCallData, context: context);
//callProvider.init();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
body: FutureBuilder<void>( body: Consumer<ChatCallProvider>(builder: (BuildContext context, ChatCallProvider chatcp, Widget? child) {
future: _initializeControllerFuture, if (chatcp.isCallEnded) Navigator.pop(context);
builder: (BuildContext context, AsyncSnapshot<void> snapshot) { return Stack(
if (snapshot.connectionState == ConnectionState.done) { alignment: FractionalOffset.center,
return Stack( children: <Widget>[
alignment: FractionalOffset.center, if (widget.isVideoCall)
children: <Widget>[ Positioned.fill(
if (widget.isVideoCall) child: RTCVideoView(
Positioned.fill( callProvider.localVideoRenderer,
child: CameraPreview( objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
controller, ),
),
Positioned.fill(
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
child: Container(
decoration: BoxDecoration(
color: MyColors.grey57Color.withOpacity(
0.3,
),
), ),
), child: Column(
Positioned.fill( crossAxisAlignment: CrossAxisAlignment.start,
child: ClipRect( mainAxisSize: MainAxisSize.max,
child: BackdropFilter( children: <Widget>[
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), 40.height,
child: Container( Row(
decoration: BoxDecoration( crossAxisAlignment: CrossAxisAlignment.center,
color: MyColors.grey57Color.withOpacity( mainAxisAlignment: MainAxisAlignment.center,
0.3,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[ children: <Widget>[
40.height,
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
margin: const EdgeInsets.all(21.0),
child: Container(
margin: const EdgeInsets.only(
left: 10.0,
right: 10.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
SvgPicture.asset(
"assets/images/user.svg",
height: 70,
width: 70,
fit: BoxFit.cover,
),
10.height,
Text(
widget.outGoingCallData.title,
style: const TextStyle(
fontSize: 21,
fontWeight: FontWeight.bold,
color: MyColors.white,
letterSpacing: -1.26,
height: 23 / 12,
),
),
const Text(
"Ringing...",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(
0xffC6C6C6,
),
letterSpacing: -0.48,
height: 23 / 24,
),
),
const SizedBox(
height: 2,
),
],
),
),
),
],
),
// Container(
// margin: const EdgeInsets.all(21.0),
// width: MediaQuery.of(context).size.width,
// decoration: cardRadius(15.0, color: MyColors.black, elevation: null),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisSize: MainAxisSize.min,
// children: [
// Container(
// padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 6.0),
// child: Text(
// "TranslationBase.of(context).appoInfo",
// style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: MyColors.white, letterSpacing: -0.64, height: 23 / 12),
// ),
// ),
// Container(
// padding: const EdgeInsets.only(left: 16.0, right: 16.0),
// child: Text(
// "widget.OutGoingCallData.appointmentdate + widget.OutGoingCallData.appointmenttime",
// style: TextStyle(fontSize: 12.0, letterSpacing: -0.48, color: Color(0xff8E8E8E), fontWeight: FontWeight.w600),
// ),
// ),
// Container(
// padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 21.0),
// child: Text(
// "widget.OutGoingCallData.clinicname",
// style: TextStyle(fontSize: 12.0, letterSpacing: -0.48, color: Color(0xff8E8E8E), fontWeight: FontWeight.w600),
// ),
// ),
// ],
// ),
// ),
const Spacer(),
Container( Container(
margin: const EdgeInsets.only( margin: const EdgeInsets.all(21.0),
bottom: 70.0, child: Container(
left: 49, margin: const EdgeInsets.only(
right: 49, left: 10.0,
), right: 10.0,
child: Row( ),
mainAxisSize: MainAxisSize.max, child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[ mainAxisSize: MainAxisSize.min,
if (widget.isVideoCall) mainAxisAlignment: MainAxisAlignment.spaceAround,
RawMaterialButton( children: <Widget>[
onPressed: () { SvgPicture.asset(
_camOff(); "assets/images/user.svg",
}, height: 70,
elevation: 2.0, width: 70,
fillColor: isCamOff ? MyColors.green2DColor : Colors.grey, fit: BoxFit.cover,
padding: const EdgeInsets.all( ),
15.0, 10.height,
), Text(
shape: const CircleBorder(), widget.outGoingCallData.receiverName.toString().replaceAll(".", " "),
child: Icon( style: const TextStyle(
isCamOff ? Icons.videocam_off : Icons.videocam, fontSize: 21,
color: MyColors.white, fontWeight: FontWeight.bold,
size: 35.0,
),
)
else
RawMaterialButton(
onPressed: () {
_loudOn();
},
elevation: 2.0,
fillColor: isLoudSpeaker ? MyColors.green2DColor : Colors.grey,
padding: const EdgeInsets.all(
15.0,
),
shape: const CircleBorder(),
child: const Icon(
Icons.volume_up,
color: MyColors.white, color: MyColors.white,
size: 35.0, letterSpacing: -1.26,
height: 23 / 12,
), ),
), ),
RawMaterialButton( const Text(
onPressed: () { "Ringing...",
_micOff(); style: TextStyle(
}, fontSize: 16,
elevation: 2.0, fontWeight: FontWeight.w600,
fillColor: isMicOff ? MyColors.green2DColor : Colors.grey, color: Color(
padding: const EdgeInsets.all( 0xffC6C6C6,
15.0, ),
), letterSpacing: -0.48,
shape: const CircleBorder(), height: 23 / 24,
child: Icon( ),
isMicOff ? Icons.mic_off : Icons.mic,
color: MyColors.white,
size: 35.0,
),
),
RawMaterialButton(
onPressed: () {
backToHome();
},
elevation: 2.0,
fillColor: MyColors.redA3Color,
padding: const EdgeInsets.all(
15.0,
), ),
shape: const CircleBorder(), const SizedBox(
child: const Icon( height: 2,
Icons.call_end,
color: MyColors.white,
size: 35.0,
), ),
), ],
], ),
), ),
), ),
], ],
), ),
), // Container(
// margin: const EdgeInsets.all(21.0),
// width: MediaQuery.of(context).size.width,
// decoration: cardRadius(15.0, color: MyColors.black, elevation: null),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisSize: MainAxisSize.min,
// children: [
// Container(
// padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 6.0),
// child: Text(
// "TranslationBase.of(context).appoInfo",
// style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: MyColors.white, letterSpacing: -0.64, height: 23 / 12),
// ),
// ),
// Container(
// padding: const EdgeInsets.only(left: 16.0, right: 16.0),
// child: Text(
// "widget.OutGoingCallData.appointmentdate + widget.OutGoingCallData.appointmenttime",
// style: TextStyle(fontSize: 12.0, letterSpacing: -0.48, color: Color(0xff8E8E8E), fontWeight: FontWeight.w600),
// ),
// ),
// Container(
// padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 21.0),
// child: Text(
// "widget.OutGoingCallData.clinicname",
// style: TextStyle(fontSize: 12.0, letterSpacing: -0.48, color: Color(0xff8E8E8E), fontWeight: FontWeight.w600),
// ),
// ),
// ],
// ),
// ),
const Spacer(),
Container(
margin: const EdgeInsets.only(
bottom: 70.0,
left: 49,
right: 49,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// if (widget.isVideoCall)
// RawMaterialButton(
// onPressed: () {
// callProvider.camOff();
// },
// elevation: 2.0,
// fillColor: callProvider.isCamOff ? MyColors.green2DColor : Colors.grey,
// padding: const EdgeInsets.all(
// 15.0,
// ),
// shape: const CircleBorder(),
// child: Icon(
// callProvider.isCamOff ? Icons.videocam_off : Icons.videocam,
// color: MyColors.white,
// size: 35.0,
// ),
// )
// else
// RawMaterialButton(
// onPressed: () {
// callProvider.loudOn();
// },
// elevation: 2.0,
// fillColor: callProvider.isLoudSpeaker ? MyColors.green2DColor : Colors.grey,
// padding: const EdgeInsets.all(
// 15.0,
// ),
// shape: const CircleBorder(),
// child: const Icon(
// Icons.volume_up,
// color: MyColors.white,
// size: 35.0,
// ),
// ),
// RawMaterialButton(
// onPressed: () {
// callProvider.micOff();
// },
// elevation: 2.0,
// fillColor: callProvider.isMicOff ? MyColors.green2DColor : Colors.grey,
// padding: const EdgeInsets.all(
// 15.0,
// ),
// shape: const CircleBorder(),
// child: Icon(
// callProvider.isMicOff ? Icons.mic_off : Icons.mic,
// color: MyColors.white,
// size: 35.0,
// ),
// ),
RawMaterialButton(
onPressed: () {
callProvider.endCall().then((bool value) {
if (value) {
Navigator.of(context).pop();
}
});
},
elevation: 2.0,
fillColor: MyColors.redA3Color,
padding: const EdgeInsets.all(
15.0,
),
shape: const CircleBorder(),
child: const Icon(
Icons.call_end,
color: MyColors.white,
size: 35.0,
),
),
],
),
),
],
), ),
), ),
), ),
], ),
); ),
} else { ],
return const Center( );
child: CircularProgressIndicator(), }),
);
}
},
),
); );
} }
void _runAnimation() async {
_cameras = await availableCameras();
CameraDescription firstCamera = _cameras[1];
controller = CameraController(firstCamera, ResolutionPreset.medium);
_initializeControllerFuture = controller.initialize();
setState(() {});
// setAudioFile();
for (int i = 0; i < 100; i++) {
await _animationController!.forward();
await _animationController!.reverse();
}
}
void _micOff() {
setState(() {
isMicOff = !isMicOff;
});
}
void _camOff() {
setState(() {
isCamOff = !isCamOff;
});
}
void _loudOn() {
setState(() {
isLoudSpeaker = !isLoudSpeaker;
});
}
Future<void> _submit() async {
try {
// backToHome();
// final roomModel = RoomModel(name: widget.OutGoingCallData.name, token: widget.OutGoingCallData.sessionId, identity: widget.OutGoingCallData.identity);
await controller?.dispose();
// changeCallStatusAPI(4);
// if (_session != null && _signaling != null) {
// await Navigator.of(context).pushReplacement(
// MaterialPageRoute(
// // fullscreenDialog: true,
// builder: (BuildContext context) {
// // if (widget.OutGoingCallData.isWebRTC == "true") {
// return StartVideoCall(signaling: _signaling, session: _session);
//
// // else {
// // return OpenTokConnectCallPage(apiKey: OPENTOK_API_KEY, sessionId: widget.OutGoingCallData.sessionId, token: widget.OutGoingCallData.token);
// // }
//
// // return VideoCallWebPage(receiverId: widget.OutGoingCallData.receiverID, callerId: widget.OutGoingCallData.callerID); // Web WebRTC VideoCall
//
// // return CallHomePage(receiverId: widget.OutGoingCallData.receiverID, callerId: widget.OutGoingCallData.callerID); // App WebRTC VideoCall
// },
// ),
// );
// } else {
// // Invalid Params/Data
// Utils.showToast("Failed to establish connection with server");
// }
} catch (err) {
print(err);
// await PlatformExceptionAlertDialog(
// exception: err,
// ).show(context);
Utils.showToast(err.toString());
}
}
// void changeCallStatusAPI(int sessionStatus) {
// LiveCareService service = new LiveCareService();
// service.endCallAPI(widget.OutGoingCallData.sessionId, sessionStatus, context).then((res) {}).catchError((err) {
// print(err);
// });
// }
void backToHome() async {
// final connected = await signaling.declineCall(widget.OutGoingCallData.callerID, widget.OutGoingCallData.receiverID);
// LandingPage.isOpenCallPage = false;
// _signaling
_animationController!.dispose();
// player.stop();
// changeCallStatusAPI(3);
// _signaling.bye(_session, callRejected: true);
// _signaling.callDisconnected(_session, callRejected: true);
Navigator.of(context).pop();
}
//
// void disposeAudioResources() async {
// await player.dispose();
// }
//
// void setAudioFile() async {
// player.stop();
// await player.setVolume(1.0); // full volume
// try {
// await player.setAsset('assets/sounds/ring_60Sec.mp3').then((value) {
// player.setLoopMode(LoopMode.one); // loop ring sound
// player.play();
// }).catchError((err) {
// print("Error: $err");
// });
// } catch (e) {
// print("Error: $e");
// }
// }
//
// void connectSignaling({@required bool iAmCaller = false}) async {
// print("----------------- + Signaling Connection Started ---------------------------");
// var caller = widget.OutGoingCallData.callerID;
// var receiver = widget.OutGoingCallData.receiverID;
// var host = widget.OutGoingCallData.server;
//
// var selfRole = iAmCaller ? "Caller" : "Receiver";
// var selfId = iAmCaller ? caller : receiver;
// var selfUser = SocketUser(id: selfId, name: "$selfRole-$selfId", userAgent: DeviceInfo.userAgent, moreInfo: {});
//
// var remoteRole = !iAmCaller ? "Caller" : "Receiver";
// var remoteId = !iAmCaller ? caller : receiver;
// var remoteUser = SocketUser(id: remoteId, name: "$remoteRole-$remoteId", userAgent: DeviceInfo.userAgent, moreInfo: {});
//
// var sessionId = "$caller-$receiver";
// _session = SessionOneToOne(id: sessionId, local_user: selfUser, remote_user: remoteUser);
//
// _signaling = Signaling(host, session: _session);
// await _signaling.connect();
//
// if (_signaling.state == SignalingState.Open) {
// return;
// }
// }
BoxDecoration cardRadius(double radius, {required Color color, double? elevation}) { BoxDecoration cardRadius(double radius, {required Color color, double? elevation}) {
return BoxDecoration( return BoxDecoration(
shape: BoxShape.rectangle, shape: BoxShape.rectangle,
color: color ?? Colors.white, color: color ?? Colors.white,
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(Radius.circular(radius)),
Radius.circular(radius), boxShadow: <BoxShadow>[BoxShadow(color: const Color(0xff000000).withOpacity(.05), blurRadius: elevation ?? 27, offset: const Offset(-2, 3))],
),
boxShadow: <BoxShadow>[
BoxShadow(
color: const Color(
0xff000000,
).withOpacity(
.05,
),
//spreadRadius: 5,
blurRadius: elevation ?? 27,
offset: const Offset(
-2,
3,
),
),
],
); );
} }
} }

@ -0,0 +1,171 @@
// import 'dart:async';
// import 'dart:io';
// import 'package:flutter/material.dart';
//
// class DraggableCam extends StatefulWidget {
// //final Size availableScreenSize;
// final Widget child;
// final double scaleFactor;
// // final Stream<bool> onButtonBarVisible;
// // final Stream<double> onButtonBarHeight;
//
// const DraggableCam({
// Key? key,
// //@required this.availableScreenSize,
// required this.child,
// // @required this.onButtonBarVisible,
// // @required this.onButtonBarHeight,
//
// /// The portion of the screen the DraggableWidget should use.
// this.scaleFactor = .25,
// }) : assert(scaleFactor != null && scaleFactor > 0 && scaleFactor <= .4),
// // assert(availableScreenSize != null),
// // assert(onButtonBarVisible != null),
// // assert(onButtonBarHeight != null),
// super(key: key);
//
// @override
// _DraggablePublisherState createState() => _DraggablePublisherState();
// }
//
// class _DraggablePublisherState extends State<DraggableCam> {
// bool _isButtonBarVisible = true;
// double _buttonBarHeight = 0;
// late double _width;
// late double _height;
// late double _top;
// late double _left;
// late double _viewPaddingTop;
// late double _viewPaddingBottom;
// final double _padding = 8.0;
// final Duration _duration300ms = const Duration(milliseconds: 300);
// final Duration _duration0ms = const Duration(milliseconds: 0);
// late Duration _duration;
// late StreamSubscription _streamSubscription;
// late StreamSubscription _streamHeightSubscription;
//
// @override
// void initState() {
// super.initState();
// _duration = _duration300ms;
// _width = widget.availableScreenSize.width * widget.scaleFactor;
// _height = _width * (widget.availableScreenSize.height / widget.availableScreenSize.width);
// _top = widget.availableScreenSize.height - (_buttonBarHeight + _padding) - _height;
// _left = widget.availableScreenSize.width - _padding - _width;
//
// _streamSubscription = widget.onButtonBarVisible.listen(_buttonBarVisible);
// _streamHeightSubscription = widget.onButtonBarHeight.listen(_getButtonBarHeight);
// }
//
// @override
// void didChangeDependencies() {
// var mediaQuery = MediaQuery.of(context);
// _viewPaddingTop = mediaQuery.viewPadding.top;
// _viewPaddingBottom = mediaQuery.viewPadding.bottom;
// super.didChangeDependencies();
// }
//
// @override
// void dispose() {
// _streamSubscription.cancel();
// _streamHeightSubscription.cancel();
// super.dispose();
// }
//
// void _getButtonBarHeight(double height) {
// setState(() {
// _buttonBarHeight = height;
// _positionWidget();
// });
// }
//
// void _buttonBarVisible(bool visible) {
// if (!mounted) {
// return;
// }
// setState(() {
// _isButtonBarVisible = visible;
// if (_duration == _duration300ms) {
// // only position the widget when we are not currently dragging it around
// _positionWidget();
// }
// });
// }
//
// @override
// Widget build(BuildContext context) {
// return AnimatedPositioned(
// top: _top,
// left: _left,
// width: _width,
// height: _height,
// duration: _duration,
// child: Listener(
// onPointerDown: (_) => _duration = _duration0ms,
// onPointerMove: (PointerMoveEvent event) {
// setState(() {
// _left = (_left + event.delta.dx).roundToDouble();
// _top = (_top + event.delta.dy).roundToDouble();
// });
// },
// onPointerUp: (_) => _positionWidget(),
// onPointerCancel: (_) => _positionWidget(),
// child: ClippedVideo(
// height: _height,
// width: _width,
// child: widget.child,
// ),
// ),
// );
// }
//
// double _getCurrentStatusBarHeight() {
// if (_isButtonBarVisible) {
// return _viewPaddingTop;
// }
// final _defaultViewPaddingTop = Platform.isIOS ? 20.0 : Platform.isAndroid ? 24.0 : 0.0;
// if (_viewPaddingTop > _defaultViewPaddingTop) {
// // There must be a hardware notch in the display.
// return _viewPaddingTop;
// }
// return 0.0;
// }
//
// double _getCurrentButtonBarHeight() {
// if (_isButtonBarVisible) {
// return _buttonBarHeight + _viewPaddingBottom;
// }
// return _viewPaddingBottom;
// }
//
// void _positionWidget() {
// // Determine the center of the object being dragged so we can decide
// // in which corner the object should be placed.
// var dx = (_width / 2) + _left;
// dx = dx < 0 ? 0 : dx >= widget.availableScreenSize.width ? widget.availableScreenSize.width - 1 : dx;
// var dy = (_height / 2) + _top;
// dy = dy < 0 ? 0 : dy >= widget.availableScreenSize.height ? widget.availableScreenSize.height - 1 : dy;
// final draggableCenter = Offset(dx, dy);
//
// setState(() {
// _duration = _duration300ms;
// if (Rect.fromLTRB(0, 0, widget.availableScreenSize.width / 2, widget.availableScreenSize.height / 2).contains(draggableCenter)) {
// // Top-left
// _top = _getCurrentStatusBarHeight() + _padding;
// _left = _padding;
// } else if (Rect.fromLTRB(widget.availableScreenSize.width / 2, 0, widget.availableScreenSize.width, widget.availableScreenSize.height / 2).contains(draggableCenter)) {
// // Top-right
// _top = _getCurrentStatusBarHeight() + _padding;
// _left = widget.availableScreenSize.width - _padding - _width;
// } else if (Rect.fromLTRB(0, widget.availableScreenSize.height / 2, widget.availableScreenSize.width / 2, widget.availableScreenSize.height).contains(draggableCenter)) {
// // Bottom-left
// _top = widget.availableScreenSize.height - (_getCurrentButtonBarHeight() + _padding) - _height;
// _left = _padding;
// } else if (Rect.fromLTRB(widget.availableScreenSize.width / 2, widget.availableScreenSize.height / 2, widget.availableScreenSize.width, widget.availableScreenSize.height).contains(draggableCenter)) {
// // Bottom-right
// _top = widget.availableScreenSize.height - (_getCurrentButtonBarHeight() + _padding) - _height;
// _left = widget.availableScreenSize.width - _padding - _width;
// }
// });
// }
// }

@ -0,0 +1,267 @@
import 'dart:async';
import 'dart:core';
import 'dart:ui';
import 'package:draggable_widget/draggable_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/provider/chat_call_provider.dart';
import 'package:provider/provider.dart';
class StartCallPage extends StatefulWidget {
RTCVideoRenderer localRenderer;
RTCVideoRenderer remoteRenderer;
StartCallPage({required this.localRenderer, required this.remoteRenderer});
@override
_StartCallPageState createState() => _StartCallPageState();
}
class _StartCallPageState extends State<StartCallPage> {
final dragController = DragController();
late ChatCallProvider callProvider;
@override
void initState() {
callProvider = Provider.of<ChatCallProvider>(context, listen: false);
super.initState();
refresh();
}
void refresh() {
Future.delayed(const Duration(seconds: 1), () {
callProvider.notifyListeners();
});
}
@override
Widget build(BuildContext context) {
return Consumer<ChatCallProvider>(builder: (BuildContext context, ChatCallProvider provider, Widget? child) {
return SizedBox(
width: double.infinity,
height: double.infinity,
child: Stack(
alignment: FractionalOffset.center,
children: [
if (provider.isVideoCall)
Positioned.fill(
child: RTCVideoView(
widget.remoteRenderer,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
key: const Key('remote'),
),
),
if (provider.isVideoCall)
DraggableWidget(
bottomMargin: 20,
topMargin: 40,
intialVisibility: true,
horizontalSpace: 20,
shadowBorderRadius: 50,
initialPosition: AnchoringPosition.topLeft,
dragController: dragController,
normalShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0),
draggingShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0),
child: SizedBox(
height: 200,
width: 140,
child: RTCVideoView(
widget.localRenderer,
mirror: true,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
),
),
),
if (!provider.isVideoCall)
Positioned.fill(
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
child: Container(
decoration: BoxDecoration(
color: MyColors.grey57Color.withOpacity(
0.3,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
40.height,
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
margin: const EdgeInsets.all(21.0),
child: Container(
margin: const EdgeInsets.only(
left: 10.0,
right: 10.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
SvgPicture.asset(
"assets/images/user.svg",
height: 70,
width: 70,
fit: BoxFit.cover,
),
10.height,
Text(
callProvider.outGoingCallData.receiverName!,
style: const TextStyle(
fontSize: 21,
decoration: TextDecoration.none,
fontWeight: FontWeight.bold,
color: MyColors.white,
letterSpacing: -1.26,
height: 23 / 12,
),
),
const Text(
"On Call",
style: TextStyle(
fontSize: 16,
decoration: TextDecoration.none,
fontWeight: FontWeight.w600,
color: Color(
0xffC6C6C6,
),
letterSpacing: -0.48,
height: 23 / 24,
),
),
const SizedBox(
height: 2,
),
],
),
),
),
],
),
],
),
),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: const EdgeInsets.only(
bottom: 20,
left: 40,
right: 40,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
// if (provider.isVideoCall)
RawMaterialButton(
constraints: BoxConstraints(),
onPressed: () {
callProvider.loudOn();
},
elevation: 2.0,
fillColor: callProvider.isLoudSpeaker ? MyColors.green2DColor : Colors.grey,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: const Icon(
Icons.volume_up,
color: MyColors.white,
size: 30.0,
),
),
RawMaterialButton(
constraints: BoxConstraints(),
onPressed: () {
provider.camOff();
},
elevation: 2.0,
fillColor: provider.isCamOff ? MyColors.green2DColor : Colors.grey,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: Icon(
provider.isCamOff ? Icons.videocam_off : Icons.videocam,
color: MyColors.white,
size: 30.0,
),
),
RawMaterialButton(
constraints: BoxConstraints(),
onPressed: () {
provider.switchCamera();
},
elevation: 2.0,
fillColor: provider.isFrontCamera ? Colors.grey : MyColors.green2DColor,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: Icon(
provider.isFrontCamera ? Icons.switch_camera_outlined : Icons.switch_camera,
color: MyColors.white,
size: 30.0,
),
),
RawMaterialButton(
constraints: BoxConstraints(),
onPressed: () {
provider.micOff();
},
elevation: 2.0,
fillColor: provider.isMicOff ? MyColors.green2DColor : Colors.grey,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: Icon(
provider.isMicOff ? Icons.mic_off : Icons.mic,
color: MyColors.white,
size: 30.0,
),
),
RawMaterialButton(
constraints: BoxConstraints(),
onPressed: () {
provider.endCall().then((value) {
if (value) {
Navigator.of(context).pop();
}
});
},
elevation: 2.0,
fillColor: MyColors.redA3Color,
padding: const EdgeInsets.all(
10.0,
),
shape: const CircleBorder(),
child: const Icon(
Icons.call_end,
color: MyColors.white,
size: 30.0,
),
),
],
),
),
),
],
),
);
});
}
}

@ -10,11 +10,9 @@ import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/chat/call.dart'; import 'package:mohem_flutter_app/models/chat/call.dart';
import 'package:mohem_flutter_app/models/chat/get_search_user_chat_model.dart'; import 'package:mohem_flutter_app/models/chat/get_search_user_chat_model.dart';
import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart'; import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart';
import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart';
import 'package:mohem_flutter_app/provider/chat_call_provider.dart'; import 'package:mohem_flutter_app/provider/chat_call_provider.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/ui/chat/custom_auto_direction.dart'; import 'package:mohem_flutter_app/ui/chat/custom_auto_direction.dart';
@ -23,9 +21,9 @@ import 'package:mohem_flutter_app/ui/chat/chat_bubble.dart';
import 'package:mohem_flutter_app/ui/chat/common.dart'; import 'package:mohem_flutter_app/ui/chat/common.dart';
import 'package:mohem_flutter_app/widgets/chat_app_bar_widge.dart'; import 'package:mohem_flutter_app/widgets/chat_app_bar_widge.dart';
import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:signalr_netcore/signalr_client.dart';
import 'package:swipe_to/swipe_to.dart'; import 'package:swipe_to/swipe_to.dart';
class ChatDetailedScreenParams { class ChatDetailedScreenParams {
@ -78,7 +76,7 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
params = ModalRoute.of(context)!.settings.arguments as ChatDetailedScreenParams; params = ModalRoute.of(context)!.settings.arguments as ChatDetailedScreenParams;
data = Provider.of<ChatProviderModel>(context, listen: false); data = Provider.of<ChatProviderModel>(context, listen: false);
// callPro = Provider.of<ChatCallProvider>(context, listen: false); // callPro = Provider.of<ChatCallProvider>(context, listen: false);
if (params != null) { if (params != null) {
data.getSingleUserChatHistory( data.getSingleUserChatHistory(
senderUID: AppState().chatDetails!.response!.id!.toInt(), senderUID: AppState().chatDetails!.response!.id!.toInt(),
@ -98,14 +96,28 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
showTyping: true, showTyping: true,
chatUser: params!.chatUser, chatUser: params!.chatUser,
actions: [ actions: [
// SvgPicture.asset("assets/icons/chat/call.svg", width: 21, height: 23).onPress(() { SvgPicture.asset("assets/icons/chat/call.svg", width: 21, height: 23).onPress(() async {
// makeCall(callType: "AUDIO"); Future<PermissionStatus> micPer = Permission.microphone.request();
// }), if (await micPer.isGranted) {
// 24.width, makeCall(callType: "AUDIO");
// SvgPicture.asset("assets/icons/chat/video_call.svg", width: 21, height: 18).onPress(() { } else {
// makeCall(callType: "VIDEO"); Permission.microphone.request().isGranted.then((value) {
// }), makeCall(callType: "AUDIO");
// 21.width, });
}
}),
24.width,
SvgPicture.asset("assets/icons/chat/video_call.svg", width: 21, height: 18).onPress(() async {
Future<PermissionStatus> camPer = Permission.camera.request();
if (await camPer.isGranted) {
makeCall(callType: "VIDEO");
} else {
Permission.camera.request().isGranted.then((value) {
makeCall(callType: "VIDEO");
});
}
}),
21.width,
], ],
), ),
body: SafeArea( body: SafeArea(
@ -149,7 +161,7 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
); );
}, },
).onPress(() async { ).onPress(() async {
logger.w(m.userChatHistory[i].toJson()); // logger.w(m.userChatHistory[i].toJson());
if (m.userChatHistory[i].fileTypeResponse != null && m.userChatHistory[i].fileTypeId != null) { if (m.userChatHistory[i].fileTypeResponse != null && m.userChatHistory[i].fileTypeId != null) {
if (m.userChatHistory[i].fileTypeId! == 1 || if (m.userChatHistory[i].fileTypeId! == 1 ||
m.userChatHistory[i].fileTypeId! == 5 || m.userChatHistory[i].fileTypeId! == 5 ||
@ -352,29 +364,21 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
} }
void makeCall({required String callType}) async { void makeCall({required String callType}) async {
callPro.initCallListeners();
print("================== Make call Triggered ============================");
Map<String, dynamic> json = { Map<String, dynamic> json = {
"callerID": AppState().chatDetails!.response!.id!.toString(), "callerID": AppState().chatDetails!.response!.id,
"callerDetails": AppState().chatDetails!.toJson(), "callerName": AppState().chatDetails!.response!.userName,
"receiverID": params!.chatUser!.id.toString(), "callerEmail": AppState().chatDetails!.response!.email,
"receiverDetails": params!.chatUser!.toJson(), "callerTitle": AppState().chatDetails!.response!.title,
"callerPhone": AppState().chatDetails!.response!.phone,
"receiverID": params!.chatUser!.id,
"receiverName": params!.chatUser!.userName,
"receiverEmail": params!.chatUser!.email,
"receiverTitle": params!.chatUser!.title,
"receiverPhone": params!.chatUser!.phone,
"title": params!.chatUser!.userName!.replaceAll(".", " "), "title": params!.chatUser!.userName!.replaceAll(".", " "),
"calltype": callType == "VIDEO" ? "Video" : "Audio", "callType": callType == "VIDEO" ? "Video" : "Audio",
}; };
logger.w(json);
CallDataModel callData = CallDataModel.fromJson(json); CallDataModel callData = CallDataModel.fromJson(json);
await Navigator.push( await Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => OutGoingCall(isVideoCall: callType == "VIDEO" ? true : false, outGoingCallData: callData)));
context,
MaterialPageRoute(
builder: (BuildContext context) => OutGoingCall(
isVideoCall: callType == "VIDEO" ? true : false,
outGoingCallData: callData,
),
),
).then((value) {
print("then");
callPro.stopListeners();
});
} }
} }

@ -7,6 +7,7 @@ import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/provider/chat_call_provider.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/ui/chat/chat_home_screen.dart'; import 'package:mohem_flutter_app/ui/chat/chat_home_screen.dart';
import 'package:mohem_flutter_app/ui/chat/favorite_users_screen.dart'; import 'package:mohem_flutter_app/ui/chat/favorite_users_screen.dart';
@ -27,12 +28,15 @@ class _ChatHomeState extends State<ChatHome> {
int tabIndex = 0; int tabIndex = 0;
PageController controller = PageController(); PageController controller = PageController();
late ChatProviderModel data; late ChatProviderModel data;
late ChatCallProvider callProvider;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
data = Provider.of<ChatProviderModel>(context, listen: false); data = Provider.of<ChatProviderModel>(context, listen: false);
callProvider = Provider.of<ChatCallProvider>(context, listen: false);
data.registerEvents(); data.registerEvents();
// callProvider.initCallListeners();
} }
@override @override
@ -44,7 +48,7 @@ class _ChatHomeState extends State<ChatHome> {
void fetchAgain() { void fetchAgain() {
if (chatHubConnection.state != HubConnectionState.Connected) { if (chatHubConnection.state != HubConnectionState.Connected) {
data.getUserAutoLoginToken().whenComplete(() async { data.getUserAutoLoginToken().whenComplete(() async {
await data.buildHubConnection(); await data.buildHubConnection(context: context, ccProvider: callProvider);
data.getUserRecentChats(); data.getUserRecentChats();
}); });
return; return;
@ -55,7 +59,7 @@ class _ChatHomeState extends State<ChatHome> {
// String isAppOpendByChat = await Utils.getStringFromPrefs("isAppOpendByChat"); // String isAppOpendByChat = await Utils.getStringFromPrefs("isAppOpendByChat");
// String notificationData = await Utils.getStringFromPrefs("notificationData"); // String notificationData = await Utils.getStringFromPrefs("notificationData");
// if (isAppOpendByChat != "null" || isAppOpendByChat == "true" && notificationData != "null") { // if (isAppOpendByChat != "null" || isAppOpendByChat == "true" && notificationData != "null") {
// data.openChatByNoti(context); // data.openChatByNoti(context);
// } // }
}); });
} }

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:math'; import 'dart:math';
import 'package:audio_waveforms/audio_waveforms.dart'; import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -187,3 +188,40 @@ class WaveBubble extends StatelessWidget {
); );
} }
} }
class CallTimer extends StatefulWidget {
const CallTimer({Key? key}) : super(key: key);
@override
State<CallTimer> createState() => _CallTimerState();
}
class _CallTimerState extends State<CallTimer> {
late Timer _timer;
int _timeExpandedBySeconds = 0;
@override
void initState() {
// TODO: implement initState
super.initState();
_timer = Timer.periodic(const Duration(seconds: 1), (Timer timer) {
setState(() {
_timeExpandedBySeconds += 1;
});
});
}
@override
Widget build(BuildContext context) {
return Text(
_timeExpandedBySeconds.toString(),
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold,
color: MyColors.white,
letterSpacing: -1.26,
height: 23 / 12,
),
);
}
}

@ -18,6 +18,7 @@ import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart'; import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart';
import 'package:mohem_flutter_app/provider/chat_call_provider.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
import 'package:mohem_flutter_app/ui/landing/widget/app_drawer.dart'; import 'package:mohem_flutter_app/ui/landing/widget/app_drawer.dart';
@ -48,6 +49,7 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
late DashboardProviderModel data; late DashboardProviderModel data;
late MarathonProvider marathonProvider; late MarathonProvider marathonProvider;
late ChatProviderModel cProvider; late ChatProviderModel cProvider;
late ChatCallProvider chatCallProvider;
final GlobalKey<ScaffoldState> _scaffoldState = GlobalKey(); final GlobalKey<ScaffoldState> _scaffoldState = GlobalKey();
final RefreshController _refreshController = RefreshController(initialRefresh: false); final RefreshController _refreshController = RefreshController(initialRefresh: false);
@ -62,6 +64,7 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
data = Provider.of<DashboardProviderModel>(context, listen: false); data = Provider.of<DashboardProviderModel>(context, listen: false);
marathonProvider = Provider.of<MarathonProvider>(context, listen: false); marathonProvider = Provider.of<MarathonProvider>(context, listen: false);
cProvider = Provider.of<ChatProviderModel>(context, listen: false); cProvider = Provider.of<ChatProviderModel>(context, listen: false);
chatCallProvider = Provider.of<ChatCallProvider>(context, listen: false);
_bHubCon(); _bHubCon();
_onRefresh(true); _onRefresh(true);
}); });
@ -97,13 +100,13 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
String isAppOpendByChat = await Utils.getStringFromPrefs("isAppOpendByChat"); String isAppOpendByChat = await Utils.getStringFromPrefs("isAppOpendByChat");
if (isAppOpendByChat != null && isAppOpendByChat == "true") { if (isAppOpendByChat != null && isAppOpendByChat == "true") {
Utils.showLoading(context); Utils.showLoading(context);
cProvider.buildHubConnection(); cProvider.buildHubConnection(context: context,ccProvider: chatCallProvider);
Future.delayed(const Duration(seconds: 2), () async { Future.delayed(const Duration(seconds: 2), () async {
cProvider.invokeChatCounter(userId: AppState().chatDetails!.response!.id!); cProvider.invokeChatCounter(userId: AppState().chatDetails!.response!.id!);
gotoChat(context); gotoChat(context);
}); });
} else { } else {
cProvider.buildHubConnection(); cProvider.buildHubConnection(context: context, ccProvider: chatCallProvider);
Future.delayed(const Duration(seconds: 2), () { Future.delayed(const Duration(seconds: 2), () {
cProvider.invokeChatCounter(userId: AppState().chatDetails!.response!.id!); cProvider.invokeChatCounter(userId: AppState().chatDetails!.response!.id!);
}); });

@ -90,8 +90,8 @@ dependencies:
signalr_netcore: ^1.3.3 signalr_netcore: ^1.3.3
logging: ^1.0.1 logging: ^1.0.1
swipe_to: ^1.0.2 swipe_to: ^1.0.2
flutter_webrtc: ^0.9.16 flutter_webrtc: ^0.9.20
camera: ^0.10.3 draggable_widget: ^2.0.0
flutter_local_notifications: any flutter_local_notifications: any
#firebase_analytics: any #firebase_analytics: any

Loading…
Cancel
Save