call_screen.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_bloc/flutter_bloc.dart';
  3. import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
  4. import 'package:youtube/data/models/call_model.dart';
  5. import 'package:youtube/presentaion/cubit/call/call_cubit.dart';
  6. import '../../shared/constats.dart';
  7. import '../../shared/shared_widgets.dart';
  8. import '../cubit/call/call_state.dart';
  9. import '../widgets/call_widgets/default_circle_image.dart';
  10. import '../widgets/call_widgets/user_info_header.dart';
  11. import 'package:agora_rtc_engine/rtc_local_view.dart' as rtc_local_view;
  12. import 'package:agora_rtc_engine/rtc_remote_view.dart' as rtc_remote_view;
  13. import 'package:permission_handler/permission_handler.dart';
  14. class CallScreen extends StatefulWidget {
  15. final bool isReceiver;
  16. final CallModel callModel;
  17. const CallScreen({Key? key, required this.isReceiver, required this.callModel}) : super(key: key);
  18. @override
  19. State<CallScreen> createState() => _CallScreenState();
  20. }
  21. class _CallScreenState extends State<CallScreen> {
  22. late CallCubit _callCubit;
  23. @override
  24. void initState() {
  25. super.initState();
  26. _callCubit = CallCubit.get(context);
  27. rePermission();
  28. _callCubit.listenToCallStatus(callModel: widget.callModel, context: context,isReceiver: widget.isReceiver);
  29. if(!widget.isReceiver){ //Caller
  30. _callCubit.initAgoraAndJoinChannel(channelToken: widget.callModel.token!,channelName: widget.callModel.channelName!,isCaller:true);
  31. }else{ //Receiver
  32. _callCubit.playContactingRing(isCaller: false);
  33. }
  34. }
  35. @override
  36. void dispose() {
  37. if(_callCubit.engine!=null){
  38. _callCubit.engine!.destroy();
  39. }
  40. _callCubit.assetsAudioPlayer.dispose();
  41. if(!widget.isReceiver){ //Sender
  42. _callCubit.countDownTimer.cancel();
  43. }
  44. _callCubit.performEndCall(callModel: widget.callModel);
  45. super.dispose();
  46. }
  47. Future<void> rePermission() async {
  48. // retrieve permissions
  49. await [Permission.microphone, Permission.camera].request();
  50. }
  51. @override
  52. Widget build(BuildContext context) {
  53. return BlocConsumer<CallCubit,CallState>(
  54. listener: (BuildContext context, Object? state) {
  55. if(state is ErrorUnAnsweredVideoChatState){
  56. showToast(msg: 'UnExpected Error!: ${state.error}');
  57. }
  58. if(state is DownCountCallTimerFinishState){
  59. if(_callCubit.remoteUid==null){
  60. _callCubit.updateCallStatusToUnAnswered(callId: widget.callModel.id);
  61. }
  62. }
  63. if(state is AgoraRemoteUserJoinedEvent){
  64. //remote user join channel
  65. if(!widget.isReceiver){ //Caller
  66. _callCubit.countDownTimer.cancel();
  67. }
  68. _callCubit.assetsAudioPlayer.stop(); //Sender, Receiver
  69. }
  70. //Call States
  71. if(state is CallNoAnswerState){
  72. if(!widget.isReceiver){ //Caller
  73. showToast(msg: 'No response!');
  74. }
  75. Navigator.pop(context);
  76. }
  77. if(state is CallCancelState){
  78. if(widget.isReceiver){ //Receiver
  79. showToast(msg: 'Caller cancel the call!');
  80. }
  81. Navigator.pop(context);
  82. }
  83. if(state is CallRejectState){
  84. if(!widget.isReceiver){ //Caller
  85. showToast(msg: 'Receiver reject the call!');
  86. }
  87. Navigator.pop(context);
  88. }
  89. if(state is CallEndState){
  90. showToast(msg: 'Call ended!');
  91. Navigator.pop(context);
  92. }
  93. },
  94. builder: (BuildContext context, state) {
  95. var cubit = CallCubit.get(context);
  96. return ModalProgressHUD(
  97. inAsyncCall: false,
  98. child: WillPopScope(
  99. onWillPop: () async { return false; },
  100. child: Scaffold(
  101. body: Stack(
  102. alignment: AlignmentDirectional.center,
  103. children: [
  104. cubit.remoteUid == null ? !widget.isReceiver ? Container(color: Colors.red,child: const rtc_local_view.SurfaceView()) : Container( //res
  105. decoration: BoxDecoration(
  106. image: DecorationImage(
  107. image: widget.callModel.callerAvatar!.isNotEmpty ? NetworkImage(
  108. widget.callModel.callerAvatar!,
  109. ) : const NetworkImage(
  110. 'https://picsum.photos/200/300',
  111. ),
  112. fit: BoxFit.cover,
  113. ),
  114. ),
  115. ) : Stack(
  116. children: [
  117. Center(
  118. child: _remoteVideo(remoteUserId: cubit.remoteUid!),
  119. ),
  120. const Align(
  121. alignment: Alignment.bottomRight,
  122. child: SizedBox(
  123. width: 122,
  124. height: 219.0,
  125. child: Center(
  126. child: rtc_local_view.SurfaceView(),
  127. ),
  128. ),
  129. ),
  130. ],
  131. ),
  132. Container(
  133. padding: const EdgeInsets.all(15.0),
  134. child: Column(
  135. crossAxisAlignment: CrossAxisAlignment.center,
  136. children: [
  137. const SizedBox(height: 50.0,),
  138. !widget.isReceiver ? UserInfoHeader( //Caller -> Show Receiver INFO
  139. avatar: widget.callModel.receiverAvatar!,
  140. name: widget.callModel.receiverName!,
  141. ) : UserInfoHeader( //Receiver -> Show Caller INFO
  142. avatar: widget.callModel.callerAvatar!,
  143. name: widget.callModel.callerName!,
  144. ),
  145. const SizedBox(height: 30.0,),
  146. cubit.remoteUid ==null ? Expanded(
  147. child: widget.isReceiver ? Text('${widget.callModel.callerName} is calling you..',style: const TextStyle(color: Colors.white,fontSize: 39.0),)
  148. :const Text('Contacting..',style: TextStyle(color: Colors.white,fontSize: 39.0),),
  149. ) : Expanded(child: Container()),
  150. cubit.remoteUid ==null ? Row(
  151. mainAxisAlignment: MainAxisAlignment.center,
  152. children: [
  153. widget.isReceiver ? Expanded(
  154. child: InkWell(
  155. onTap: (){
  156. //receiverAcceptVideoChat
  157. _callCubit.updateCallStatusToAccept(callModel: widget.callModel);
  158. },
  159. child: Container(
  160. decoration: BoxDecoration(
  161. borderRadius: BorderRadius.circular(15.0),
  162. color: Colors.green,
  163. ),
  164. child: const Center(
  165. child: Padding(
  166. padding: EdgeInsets.symmetric(horizontal: 30.0,vertical: 8.0),
  167. child: Text('Acceptance',style: TextStyle(color: Colors.white,fontSize: 13.0),),
  168. ),
  169. ),
  170. ),
  171. ),
  172. ) : Container(),
  173. widget.isReceiver ? const SizedBox(width: 15.0,) : Container(),
  174. Expanded(
  175. child: InkWell(
  176. onTap: (){
  177. if(widget.isReceiver){
  178. //receiverRejectVideoChat
  179. _callCubit.updateCallStatusToReject(callId: widget.callModel.id);
  180. }else{
  181. //callerCancelVideoChat
  182. _callCubit.updateCallStatusToCancel(callId: widget.callModel.id);
  183. }
  184. },
  185. child: Container(
  186. decoration: BoxDecoration(
  187. borderRadius: BorderRadius.circular(15.0),
  188. color: Colors.red,
  189. ),
  190. child: Center(
  191. child: Padding(
  192. padding: const EdgeInsets.symmetric(horizontal: 30.0,vertical: 8.0),
  193. child: Text(widget.isReceiver ? 'Reject' : 'Cancel',style: const TextStyle(color: Colors.white,fontSize: 13.0),),
  194. ),
  195. ),
  196. ),
  197. ),
  198. )
  199. ],
  200. )
  201. : Row(
  202. mainAxisAlignment: MainAxisAlignment.center,
  203. children: [
  204. Expanded(
  205. child: GestureDetector(
  206. onTap: (){
  207. cubit.switchCamera();
  208. },
  209. child: const DefaultCircleImage(bgColor: Colors.white ,image: Icon(Icons.switch_camera_outlined,color: Colors.black,),center: true,width: 42,height: 42,),
  210. ),
  211. ),
  212. Expanded(
  213. child: GestureDetector(
  214. onTap: (){
  215. cubit.updateCallStatusToEnd(callId: widget.callModel.id);
  216. },
  217. child: const DefaultCircleImage(bgColor: Colors.red ,image: Icon(Icons.call_end_rounded,color: Colors.white,),center: true,width: 55,height: 55,)
  218. ),
  219. ),
  220. Expanded(
  221. child: GestureDetector(
  222. onTap: (){
  223. cubit.toggleMuted();
  224. },
  225. child: DefaultCircleImage(bgColor: Colors.white ,image: cubit.muteIcon ,center: true,width: 42,height: 42,),
  226. ),
  227. ),
  228. ],
  229. ),
  230. ],
  231. ),
  232. ),
  233. ],
  234. ) ,
  235. ),
  236. ),
  237. );
  238. },
  239. );
  240. }
  241. // Display remote user's video
  242. Widget _remoteVideo({required int remoteUserId}) {
  243. return rtc_remote_view.SurfaceView(uid: remoteUserId,channelId: /*widget.callModel.channelName!*/ agoraTestChannelName,);
  244. }
  245. }