meeting.dart 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'dart:io';
  4. import 'package:flutter/foundation.dart';
  5. import 'package:flutter/material.dart';
  6. import 'package:get_it/get_it.dart';
  7. import 'package:ustest/Services/UserService.dart';
  8. import 'package:tencent_trtc_cloud/trtc_cloud_video_view.dart';
  9. import 'package:tencent_trtc_cloud/trtc_cloud.dart';
  10. import 'package:tencent_trtc_cloud/tx_beauty_manager.dart';
  11. import 'package:tencent_trtc_cloud/tx_device_manager.dart';
  12. import 'package:tencent_trtc_cloud/tx_audio_effect_manager.dart';
  13. import 'package:tencent_trtc_cloud/trtc_cloud_def.dart';
  14. import 'package:tencent_trtc_cloud/trtc_cloud_listener.dart';
  15. import 'package:provider/provider.dart';
  16. import 'Services/ConsultationService.dart';
  17. import 'models/meeting.dart';
  18. import 'tool.dart';
  19. const iosAppGroup = 'group.com.tencent.comm.trtc.demo';
  20. const iosExtensionName = 'TRTC Demo Screen';
  21. /// Meeting Page
  22. class MeetingPage extends StatefulWidget {
  23. @override
  24. State<StatefulWidget> createState() => MeetingPageState();
  25. }
  26. class MeetingPageState extends State<MeetingPage> with WidgetsBindingObserver {
  27. MeetingPageState();
  28. final _scaffoldKey = GlobalKey<ScaffoldState>();
  29. var meetModel;
  30. var userInfo = {}; //Multiplayer video user list
  31. bool isOpenMic = true; //whether turn on the microphone
  32. bool isOpenCamera = true; //whether turn on the video
  33. bool isFrontCamera = true; //front camera
  34. bool isSpeak = true;
  35. bool isDoubleTap = false;
  36. bool isShowingWindow = false;
  37. int? localViewId;
  38. bool isShowBeauty = true; //whether enable beauty settings
  39. String curBeauty = 'pitu';
  40. double curBeautyValue = 6; //The default beauty value is 6
  41. String doubleUserId = "";
  42. String doubleUserIdType = "";
  43. late TRTCCloud trtcCloud;
  44. late TXDeviceManager txDeviceManager;
  45. late TXBeautyManager txBeautyManager;
  46. late TXAudioEffectManager txAudioManager;
  47. List userList = [];
  48. List userListLast = [];
  49. List screenUserList = [];
  50. int? meetId;
  51. int quality = TRTCCloudDef.TRTC_AUDIO_QUALITY_DEFAULT;
  52. late ScrollController scrollControl;
  53. @override
  54. initState() {
  55. super.initState();
  56. WidgetsBinding.instance!.addObserver(this);
  57. meetModel = context.read<MeetingModel>();
  58. var userSetting = meetModel.getUserSetting();
  59. meetId = userSetting["meetId"];
  60. userInfo['appId'] = userSetting["appId"];
  61. userInfo['userSig'] = userSetting["userSig"];
  62. userInfo['userId'] = userSetting["userId"];
  63. isOpenCamera = userSetting["enabledCamera"];
  64. isOpenMic = userSetting["enabledMicrophone"];
  65. iniRoom();
  66. initScrollListener();
  67. // var consultationService = GetIt.instance.get<ConsultationService>();
  68. // consultationService.NotificationReceived.subscribe(
  69. // (args) {
  70. // if (args != null) {
  71. // print("Sth. happened: ${args.jsonMessage?.toString()}");
  72. // var sdkAppId = int.tryParse(args.jsonMessage['AppId']) as int;
  73. // var userSign = args.jsonMessage['AppId'] as String;
  74. // var initializeCode = args.jsonMessage['InitiatorCode'] as String;
  75. // userInfo['userId'] = initializeCode;
  76. // print("sdkAppId: $sdkAppId, userSign: $userSign");
  77. // iniRoom(sdkAppId, userSign);
  78. // //initScrollListener();
  79. // }
  80. // },
  81. // );
  82. }
  83. iniRoom() async {
  84. // Create TRTCCloud singleton
  85. trtcCloud = (await TRTCCloud.sharedInstance())!;
  86. // Tencent Cloud Audio Effect Management Module
  87. txDeviceManager = trtcCloud.getDeviceManager();
  88. // Beauty filter and animated effect parameter management
  89. txBeautyManager = trtcCloud.getBeautyManager();
  90. // Tencent Cloud Audio Effect Management Module
  91. txAudioManager = trtcCloud.getAudioEffectManager();
  92. // Register event callback
  93. trtcCloud.registerListener(onRtcListener);
  94. // Enter the room
  95. enterRoom();
  96. initData();
  97. //Set beauty effect
  98. txBeautyManager.setBeautyStyle(TRTCCloudDef.TRTC_BEAUTY_STYLE_NATURE);
  99. txBeautyManager.setBeautyLevel(6);
  100. }
  101. @override
  102. void didChangeAppLifecycleState(AppLifecycleState state) {
  103. switch (state) {
  104. case AppLifecycleState.inactive:
  105. break;
  106. case AppLifecycleState
  107. .resumed: //Switch from the background to the foreground, and the interface is visible
  108. if (!kIsWeb && Platform.isAndroid) {
  109. userListLast = jsonDecode(jsonEncode(userList));
  110. userList = [];
  111. screenUserList = MeetingTool.getScreenList(userList);
  112. this.setState(() {});
  113. const timeout = const Duration(milliseconds: 100); //10ms
  114. Timer(timeout, () {
  115. userList = userListLast;
  116. screenUserList = MeetingTool.getScreenList(userList);
  117. this.setState(() {});
  118. });
  119. }
  120. break;
  121. case AppLifecycleState.paused: // Interface invisible, background
  122. break;
  123. case AppLifecycleState.detached:
  124. break;
  125. }
  126. }
  127. // Enter the trtc room
  128. enterRoom() async {
  129. await trtcCloud.enterRoom(
  130. TRTCParams(
  131. sdkAppId: userInfo['appId'],
  132. userId: userInfo['userId'],
  133. userSig: userInfo['userSig'],
  134. role: TRTCCloudDef.TRTCRoleAnchor,
  135. roomId: meetId!),
  136. TRTCCloudDef.TRTC_APP_SCENE_LIVE);
  137. }
  138. initData() async {
  139. print('initData isOpenCamera:$isOpenCamera');
  140. if (isOpenCamera) {
  141. userList.add({
  142. 'userId': userInfo['userId'],
  143. 'type': 'video',
  144. 'visible': true,
  145. 'size': {'width': 0, 'height': 0}
  146. });
  147. } else {
  148. userList.add({
  149. 'userId': userInfo['userId'],
  150. 'type': 'video',
  151. 'visible': false,
  152. 'size': {'width': 0, 'height': 0}
  153. });
  154. }
  155. if (isOpenMic) {
  156. if (kIsWeb) {
  157. Future.delayed(Duration(seconds: 2), () {
  158. trtcCloud.startLocalAudio(quality);
  159. });
  160. } else {
  161. await trtcCloud.startLocalAudio(quality);
  162. }
  163. }
  164. screenUserList = MeetingTool.getScreenList(userList);
  165. meetModel.setList(userList);
  166. this.setState(() {});
  167. }
  168. destoryRoom() async {
  169. trtcCloud.unRegisterListener(onRtcListener);
  170. await trtcCloud.exitRoom();
  171. await TRTCCloud.destroySharedInstance();
  172. }
  173. @override
  174. dispose() {
  175. WidgetsBinding.instance!.removeObserver(this);
  176. destoryRoom();
  177. scrollControl.dispose();
  178. super.dispose();
  179. }
  180. /// Event callbacks
  181. onRtcListener(type, param) async {
  182. print("onRtcListener");
  183. print("onRtcListener type:$type, param:$param");
  184. if (type == TRTCCloudListener.onError) {
  185. if (param['errCode'] == -1308) {
  186. MeetingTool.toast('Failed to start screen recording', context);
  187. await trtcCloud.stopScreenCapture();
  188. userList[0]['visible'] = true;
  189. isShowingWindow = false;
  190. this.setState(() {});
  191. trtcCloud.startLocalPreview(isFrontCamera, localViewId);
  192. } else {
  193. showErrordDialog(param['errMsg']);
  194. }
  195. }
  196. if (type == TRTCCloudListener.onEnterRoom) {
  197. if (param > 0) {
  198. MeetingTool.toast('Enter room success', context);
  199. }
  200. }
  201. if (type == TRTCCloudListener.onExitRoom) {
  202. if (param > 0) {
  203. MeetingTool.toast('Exit room success', context);
  204. }
  205. }
  206. // Remote user entry
  207. if (type == TRTCCloudListener.onRemoteUserEnterRoom) {
  208. userList.add({
  209. 'userId': param,
  210. 'type': 'video',
  211. 'visible': false,
  212. 'size': {'width': 0, 'height': 0}
  213. });
  214. screenUserList = MeetingTool.getScreenList(userList);
  215. this.setState(() {});
  216. meetModel.setList(userList);
  217. }
  218. // Remote user leaves room
  219. if (type == TRTCCloudListener.onRemoteUserLeaveRoom) {
  220. String userId = param['userId'];
  221. for (var i = 0; i < userList.length; i++) {
  222. if (userList[i]['userId'] == userId) {
  223. userList.removeAt(i);
  224. }
  225. }
  226. //The user who is amplifying the video exit room
  227. if (doubleUserId == userId) {
  228. isDoubleTap = false;
  229. }
  230. screenUserList = MeetingTool.getScreenList(userList);
  231. this.setState(() {});
  232. meetModel.setList(userList);
  233. }
  234. if (type == TRTCCloudListener.onUserVideoAvailable) {
  235. String userId = param['userId'];
  236. if (param['available']) {
  237. for (var i = 0; i < userList.length; i++) {
  238. if (userList[i]['userId'] == userId &&
  239. userList[i]['type'] == 'video') {
  240. userList[i]['visible'] = true;
  241. }
  242. }
  243. } else {
  244. for (var i = 0; i < userList.length; i++) {
  245. if (userList[i]['userId'] == userId &&
  246. userList[i]['type'] == 'video') {
  247. if (isDoubleTap &&
  248. doubleUserId == userList[i]['userId'] &&
  249. doubleUserIdType == userList[i]['type']) {
  250. doubleTap(userList[i]);
  251. }
  252. trtcCloud.stopRemoteView(
  253. userId, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG);
  254. userList[i]['visible'] = false;
  255. }
  256. }
  257. }
  258. screenUserList = MeetingTool.getScreenList(userList);
  259. this.setState(() {});
  260. meetModel.setList(userList);
  261. }
  262. if (type == TRTCCloudListener.onUserSubStreamAvailable) {
  263. String userId = param["userId"];
  264. if (param["available"]) {
  265. userList.add({
  266. 'userId': userId,
  267. 'type': 'subStream',
  268. 'visible': true,
  269. 'size': {'width': 0, 'height': 0}
  270. });
  271. } else {
  272. for (var i = 0; i < userList.length; i++) {
  273. if (userList[i]['userId'] == userId &&
  274. userList[i]['type'] == 'subStream') {
  275. if (isDoubleTap &&
  276. doubleUserId == userList[i]['userId'] &&
  277. doubleUserIdType == userList[i]['type']) {
  278. doubleTap(userList[i]);
  279. }
  280. trtcCloud.stopRemoteView(
  281. userId, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_SUB);
  282. userList.removeAt(i);
  283. }
  284. }
  285. }
  286. screenUserList = MeetingTool.getScreenList(userList);
  287. this.setState(() {});
  288. meetModel.setList(userList);
  289. }
  290. }
  291. // Screen scrolling left and right event
  292. initScrollListener() {
  293. scrollControl = ScrollController();
  294. double lastOffset = 0;
  295. scrollControl.addListener(() async {
  296. double screenWidth = MediaQuery.of(context).size.width;
  297. int pageSize = (scrollControl.offset / screenWidth).ceil();
  298. if (lastOffset < scrollControl.offset) {
  299. scrollControl.animateTo(pageSize * screenWidth,
  300. duration: Duration(milliseconds: 100), curve: Curves.ease);
  301. if (scrollControl.offset == pageSize * screenWidth) {
  302. //Slide from left to right
  303. for (var i = 1; i < pageSize * MeetingTool.screenLen; i++) {
  304. await trtcCloud.stopRemoteView(
  305. userList[i]['userId'],
  306. userList[i]['type'] == "video"
  307. ? TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG
  308. : TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_SUB);
  309. }
  310. }
  311. } else {
  312. scrollControl.animateTo((pageSize - 1) * screenWidth,
  313. duration: Duration(milliseconds: 100), curve: Curves.ease);
  314. if (scrollControl.offset == pageSize * screenWidth) {
  315. var pageScreen = screenUserList[pageSize];
  316. int initI = 0;
  317. if (pageSize == 0) {
  318. initI = 1;
  319. }
  320. for (var i = initI; i < pageScreen.length; i++) {
  321. await trtcCloud.startRemoteView(
  322. pageScreen[i]['userId'],
  323. pageScreen[i]['type'] == "video"
  324. ? TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG
  325. : TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_SUB,
  326. pageScreen[i]['viewId']);
  327. }
  328. }
  329. }
  330. lastOffset = scrollControl.offset;
  331. });
  332. }
  333. Future<bool?> showErrordDialog(errorMsg) {
  334. return showDialog<bool>(
  335. context: context,
  336. barrierDismissible: false,
  337. builder: (context) {
  338. return AlertDialog(
  339. title: Text("Tips"),
  340. content: Text(errorMsg),
  341. actions: <Widget>[
  342. TextButton(
  343. child: Text("Confirm"),
  344. onPressed: () {
  345. // Navigator.push(
  346. // context,
  347. // MaterialPageRoute(
  348. // builder: (context) => IndexPage(),
  349. // ),
  350. // );
  351. },
  352. ),
  353. ],
  354. );
  355. },
  356. );
  357. }
  358. Future<bool?> showExitMeetingConfirmDialog() {
  359. return showDialog<bool>(
  360. context: context,
  361. builder: (context) {
  362. return AlertDialog(
  363. title: Text("Tips"),
  364. content: Text("Are you sure to exit the meeting?"),
  365. actions: <Widget>[
  366. TextButton(
  367. child: Text("Cancel"),
  368. onPressed: () => Navigator.of(context).pop(),
  369. ),
  370. TextButton(
  371. child: Text("Confirm"),
  372. onPressed: () {
  373. Navigator.of(context).pop(true);
  374. },
  375. ),
  376. ],
  377. );
  378. },
  379. );
  380. }
  381. // Double click zoom in and zoom out
  382. doubleTap(item) async {
  383. Size screenSize = MediaQuery.of(context).size;
  384. if (isDoubleTap) {
  385. userList.remove(item);
  386. isDoubleTap = false;
  387. doubleUserId = "";
  388. doubleUserIdType = "";
  389. item['size'] = {'width': 0, 'height': 0};
  390. } else {
  391. userList.remove(item);
  392. isDoubleTap = true;
  393. doubleUserId = item['userId'];
  394. doubleUserIdType = item['type'];
  395. item['size'] = {'width': screenSize.width, 'height': screenSize.height};
  396. }
  397. // userself
  398. if (item['userId'] == userInfo['userId']) {
  399. userList.insert(0, item);
  400. if (!kIsWeb && Platform.isIOS) {
  401. await trtcCloud.stopLocalPreview();
  402. }
  403. } else {
  404. userList.add(item);
  405. if (item['type'] == 'video') {
  406. await trtcCloud.stopRemoteView(
  407. item['userId'], TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG);
  408. } else {
  409. await trtcCloud.stopRemoteView(
  410. item['userId'], TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_SUB);
  411. }
  412. if (isDoubleTap) {
  413. userList[0]['visible'] = false;
  414. } else {
  415. if (!kIsWeb && Platform.isIOS) {
  416. await trtcCloud.stopLocalPreview();
  417. }
  418. if (isOpenCamera) {
  419. userList[0]['visible'] = true;
  420. }
  421. }
  422. }
  423. this.setState(() {});
  424. }
  425. startShare({String shareUserId = '', String shareUserSig = ''}) async {
  426. if (shareUserId == '') await trtcCloud.stopLocalPreview();
  427. trtcCloud.startScreenCapture(
  428. TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_SUB,
  429. TRTCVideoEncParam(
  430. videoFps: 10,
  431. videoResolution: TRTCCloudDef.TRTC_VIDEO_RESOLUTION_1280_720,
  432. videoBitrate: 1600,
  433. videoResolutionMode: TRTCCloudDef.TRTC_VIDEO_RESOLUTION_MODE_PORTRAIT,
  434. ),
  435. appGroup: iosAppGroup,
  436. shareUserId: shareUserId,
  437. shareUserSig: shareUserSig,
  438. );
  439. }
  440. onShareClick() async {
  441. // if (kIsWeb) {
  442. // String shareUserId = 'share-' + userInfo['userId'];
  443. // String shareUserSig = await GenerateTestUserSig.genTestSig(shareUserId);
  444. // await startShare(shareUserId: shareUserId, shareUserSig: shareUserSig);
  445. // } else if (!kIsWeb && Platform.isAndroid) {
  446. // if (!isShowingWindow) {
  447. // await startShare();
  448. // userList[0]['visible'] = false;
  449. // this.setState(() {
  450. // isShowingWindow = true;
  451. // isOpenCamera = false;
  452. // });
  453. // } else {
  454. // await trtcCloud.stopScreenCapture();
  455. // userList[0]['visible'] = true;
  456. // trtcCloud.startLocalPreview(isFrontCamera, localViewId);
  457. // this.setState(() {
  458. // isShowingWindow = false;
  459. // isOpenCamera = true;
  460. // });
  461. // }
  462. // } else {
  463. // await startShare();
  464. // //The screen sharing function can only be tested on the real machine
  465. // ReplayKitLauncher.launchReplayKitBroadcast(iosExtensionName);
  466. // this.setState(() {
  467. // isOpenCamera = false;
  468. // });
  469. // }
  470. //TODO
  471. }
  472. Widget renderView(item, valueKey, width, height) {
  473. if (item['visible']) {
  474. return GestureDetector(
  475. key: valueKey,
  476. onDoubleTap: () {
  477. doubleTap(item);
  478. },
  479. child: TRTCCloudVideoView(
  480. key: valueKey,
  481. viewType: TRTCCloudDef.TRTC_VideoView_TextureView,
  482. // This parameter is required for rendering desktop.(Android/iOS/web no need)
  483. textureParam: CustomRender(
  484. userId: item['userId'],
  485. isLocal: item['userId'] == userInfo['userId'] ? true : false,
  486. streamType: item['type'] == 'video'
  487. ? TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG
  488. : TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_SUB,
  489. width: 72,
  490. height: 120,
  491. ),
  492. onViewCreated: (viewId) async {
  493. if (item['userId'] == userInfo['userId']) {
  494. await trtcCloud.startLocalPreview(isFrontCamera, viewId);
  495. setState(() {
  496. localViewId = viewId;
  497. });
  498. } else {
  499. trtcCloud.startRemoteView(
  500. item['userId'],
  501. item['type'] == 'video'
  502. ? TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG
  503. : TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_SUB,
  504. viewId);
  505. }
  506. item['viewId'] = viewId;
  507. }));
  508. } else {
  509. return Container(
  510. alignment: Alignment.center,
  511. child: ClipOval(
  512. child: Image.asset('images/avatar3_100.20191230.png', scale: 3.5),
  513. ),
  514. );
  515. }
  516. }
  517. /// The user name and sound are displayed on the video layer
  518. Widget videoVoice(item) {
  519. return Positioned(
  520. child: new Container(
  521. child: Row(children: <Widget>[
  522. Text(
  523. item['userId'] == userInfo['userId']
  524. ? item['userId'] + "(me)"
  525. : item['userId'],
  526. style: TextStyle(color: Colors.white),
  527. ),
  528. Container(
  529. margin: EdgeInsets.only(left: 10),
  530. child: Icon(
  531. Icons.signal_cellular_alt,
  532. color: Colors.white,
  533. size: 20,
  534. ),
  535. ),
  536. ])),
  537. left: 24.0,
  538. bottom: 80.0,
  539. );
  540. }
  541. Widget topSetting() {
  542. return new Align(
  543. child: new Container(
  544. margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
  545. child: new Row(
  546. mainAxisAlignment: MainAxisAlignment.spaceAround,
  547. children: <Widget>[
  548. IconButton(
  549. icon: Icon(
  550. isSpeak ? Icons.volume_up : Icons.hearing,
  551. color: Colors.white,
  552. size: 36.0,
  553. ),
  554. onPressed: () async {
  555. if (isSpeak) {
  556. txDeviceManager.setAudioRoute(
  557. TRTCCloudDef.TRTC_AUDIO_ROUTE_EARPIECE);
  558. } else {
  559. txDeviceManager
  560. .setAudioRoute(TRTCCloudDef.TRTC_AUDIO_ROUTE_SPEAKER);
  561. }
  562. setState(() {
  563. isSpeak = !isSpeak;
  564. });
  565. }),
  566. IconButton(
  567. icon: Icon(
  568. Icons.camera_alt,
  569. color: Colors.white,
  570. size: 36.0,
  571. ),
  572. onPressed: () async {
  573. if (isFrontCamera) {
  574. txDeviceManager.switchCamera(false);
  575. } else {
  576. txDeviceManager.switchCamera(true);
  577. }
  578. setState(() {
  579. isFrontCamera = !isFrontCamera;
  580. });
  581. }),
  582. Text(meetId.toString(),
  583. style: TextStyle(fontSize: 20, color: Colors.white)),
  584. ElevatedButton(
  585. //color: Colors.red,
  586. //textColor: Colors.white,
  587. onPressed: () async {
  588. bool? delete = await showExitMeetingConfirmDialog();
  589. if (delete != null) {
  590. Navigator.pop(context);
  591. }
  592. },
  593. child: Text(
  594. "Exit Meeting",
  595. style: TextStyle(fontSize: 16.0),
  596. ),
  597. )
  598. ],
  599. ),
  600. height: 50.0,
  601. color: Color.fromRGBO(200, 200, 200, 0.4),
  602. ),
  603. alignment: Alignment.topCenter);
  604. }
  605. ///Beauty setting floating layer
  606. Widget beautySetting() {
  607. return Positioned(
  608. bottom: 80,
  609. child: Offstage(
  610. offstage: isShowBeauty,
  611. child: Container(
  612. padding: EdgeInsets.all(10),
  613. color: Color.fromRGBO(0, 0, 0, 0.8),
  614. height: 100,
  615. width: MediaQuery.of(context).size.width,
  616. child: Column(
  617. children: [
  618. Row(children: [
  619. Expanded(
  620. flex: 2,
  621. child: Slider(
  622. value: curBeautyValue,
  623. min: 0,
  624. max: 9,
  625. divisions: 9,
  626. onChanged: (double value) {
  627. if (curBeauty == 'smooth' ||
  628. curBeauty == 'nature' ||
  629. curBeauty == 'pitu') {
  630. txBeautyManager.setBeautyLevel(value.round());
  631. } else if (curBeauty == 'whitening') {
  632. txBeautyManager.setWhitenessLevel(value.round());
  633. } else if (curBeauty == 'ruddy') {
  634. txBeautyManager.setRuddyLevel(value.round());
  635. }
  636. this.setState(() {
  637. curBeautyValue = value;
  638. });
  639. },
  640. ),
  641. ),
  642. Text(curBeautyValue.round().toString(),
  643. textAlign: TextAlign.center,
  644. style: TextStyle(color: Colors.white)),
  645. ]),
  646. Padding(
  647. padding: EdgeInsets.only(top: 10),
  648. child: Row(
  649. children: [
  650. GestureDetector(
  651. child: Container(
  652. alignment: Alignment.centerLeft,
  653. width: 80.0,
  654. child: Text(
  655. 'Smooth',
  656. style: TextStyle(
  657. color: curBeauty == 'smooth'
  658. ? Color.fromRGBO(64, 158, 255, 1)
  659. : Colors.white),
  660. ),
  661. ),
  662. onTap: () => this.setState(() {
  663. txBeautyManager.setBeautyStyle(
  664. TRTCCloudDef.TRTC_BEAUTY_STYLE_SMOOTH);
  665. curBeauty = 'smooth';
  666. curBeautyValue = 6;
  667. }),
  668. ),
  669. GestureDetector(
  670. child: Container(
  671. alignment: Alignment.centerLeft,
  672. width: 80.0,
  673. child: Text(
  674. 'Nature',
  675. style: TextStyle(
  676. color: curBeauty == 'nature'
  677. ? Color.fromRGBO(64, 158, 255, 1)
  678. : Colors.white),
  679. ),
  680. ),
  681. onTap: () => this.setState(() {
  682. txBeautyManager.setBeautyStyle(
  683. TRTCCloudDef.TRTC_BEAUTY_STYLE_NATURE);
  684. curBeauty = 'nature';
  685. curBeautyValue = 6;
  686. }),
  687. ),
  688. GestureDetector(
  689. child: Container(
  690. alignment: Alignment.centerLeft,
  691. width: 80.0,
  692. child: Text(
  693. 'Pitu',
  694. style: TextStyle(
  695. color: curBeauty == 'pitu'
  696. ? Color.fromRGBO(64, 158, 255, 1)
  697. : Colors.white),
  698. ),
  699. ),
  700. onTap: () => this.setState(() {
  701. txBeautyManager.setBeautyStyle(
  702. TRTCCloudDef.TRTC_BEAUTY_STYLE_PITU);
  703. curBeauty = 'pitu';
  704. curBeautyValue = 6;
  705. }),
  706. ),
  707. GestureDetector(
  708. child: Container(
  709. alignment: Alignment.centerLeft,
  710. width: 50.0,
  711. child: Text(
  712. 'Ruddy',
  713. style: TextStyle(
  714. color: curBeauty == 'ruddy'
  715. ? Color.fromRGBO(64, 158, 255, 1)
  716. : Colors.white),
  717. ),
  718. ),
  719. onTap: () => this.setState(() {
  720. txBeautyManager.setRuddyLevel(0);
  721. curBeauty = 'ruddy';
  722. curBeautyValue = 0;
  723. }),
  724. ),
  725. ],
  726. ),
  727. ),
  728. ],
  729. ),
  730. ),
  731. ),
  732. );
  733. }
  734. Widget bottomSetting() {
  735. return new Align(
  736. child: new Container(
  737. padding: EdgeInsets.fromLTRB(0, 0, 0, 20),
  738. child: new Row(
  739. mainAxisAlignment: MainAxisAlignment.spaceAround,
  740. children: <Widget>[
  741. IconButton(
  742. icon: Icon(
  743. isOpenMic ? Icons.mic : Icons.mic_off,
  744. color: Colors.white,
  745. size: 36.0,
  746. ),
  747. onPressed: () {
  748. if (isOpenMic) {
  749. trtcCloud.stopLocalAudio();
  750. } else {
  751. trtcCloud.startLocalAudio(quality);
  752. }
  753. setState(() {
  754. isOpenMic = !isOpenMic;
  755. });
  756. }),
  757. IconButton(
  758. icon: Icon(
  759. isOpenCamera ? Icons.videocam : Icons.videocam_off,
  760. color: Colors.white,
  761. size: 36.0,
  762. ),
  763. onPressed: () {
  764. if (isOpenCamera) {
  765. userList[0]['visible'] = false;
  766. trtcCloud.stopLocalPreview();
  767. if (isDoubleTap &&
  768. doubleUserId == userList[0]['userId']) {
  769. doubleTap(userList[0]);
  770. }
  771. } else {
  772. userList[0]['visible'] = true;
  773. }
  774. setState(() {
  775. isOpenCamera = !isOpenCamera;
  776. });
  777. }),
  778. IconButton(
  779. icon: Icon(
  780. Icons.face,
  781. color: Colors.white,
  782. size: 36.0,
  783. ),
  784. onPressed: () {
  785. this.setState(() {
  786. if (isShowBeauty) {
  787. isShowBeauty = false;
  788. } else {
  789. isShowBeauty = true;
  790. }
  791. });
  792. }),
  793. IconButton(
  794. icon: Icon(
  795. Icons.people,
  796. color: Colors.white,
  797. size: 36.0,
  798. ),
  799. onPressed: () {
  800. Navigator.pushNamed(context, '/memberList');
  801. }),
  802. IconButton(
  803. icon: Icon(
  804. Icons.share_rounded,
  805. color: Colors.white,
  806. size: 36.0,
  807. ),
  808. onPressed: () {
  809. this.onShareClick();
  810. },
  811. ),
  812. //SettingPage(),
  813. ],
  814. ),
  815. height: 70.0,
  816. color: Color.fromRGBO(200, 200, 200, 0.4),
  817. ),
  818. alignment: Alignment.bottomCenter);
  819. }
  820. @override
  821. Widget build(BuildContext context) {
  822. return Scaffold(
  823. key: _scaffoldKey,
  824. body: WillPopScope(
  825. onWillPop: () async {
  826. trtcCloud.exitRoom();
  827. return true;
  828. },
  829. child: Stack(
  830. children: <Widget>[
  831. ListView.builder(
  832. scrollDirection: Axis.horizontal,
  833. physics: new ClampingScrollPhysics(),
  834. itemCount: screenUserList.length,
  835. cacheExtent: 0,
  836. controller: scrollControl,
  837. itemBuilder: (BuildContext context, index) {
  838. var item = screenUserList[index];
  839. return Container(
  840. width: MediaQuery.of(context).size.width,
  841. height: MediaQuery.of(context).size.height,
  842. color: Color.fromRGBO(19, 41, 75, 1),
  843. child: Wrap(
  844. children: List.generate(
  845. item.length,
  846. (index) => LayoutBuilder(
  847. key: ValueKey(item[index]['userId'] +
  848. item[index]['type'] +
  849. item[index]['size']['width'].toString()),
  850. builder: (BuildContext context,
  851. BoxConstraints constraints) {
  852. Size size = MeetingTool.getViewSize(
  853. MediaQuery.of(context).size,
  854. userList.length,
  855. index,
  856. item.length);
  857. double width = size.width;
  858. double height = size.height;
  859. if (isDoubleTap) {
  860. //Set the width and height of other video rendering to 1, otherwise the video will not be streamed
  861. if (item[index]['size']['width'] == 0) {
  862. width = 1;
  863. height = 1;
  864. }
  865. }
  866. ValueKey valueKey = ValueKey(item[index]['userId'] +
  867. item[index]['type'] +
  868. (isDoubleTap ? "1" : "0"));
  869. if (item[index]['size']['width'] > 0) {
  870. width = double.parse(
  871. item[index]['size']['width'].toString());
  872. height = double.parse(
  873. item[index]['size']['height'].toString());
  874. }
  875. return Container(
  876. key: valueKey,
  877. height: height,
  878. width: width,
  879. child: Stack(
  880. key: valueKey,
  881. children: <Widget>[
  882. renderView(
  883. item[index], valueKey, width, height),
  884. videoVoice(item[index])
  885. ],
  886. ),
  887. );
  888. },
  889. ),
  890. ),
  891. ),
  892. );
  893. }),
  894. topSetting(),
  895. beautySetting(),
  896. bottomSetting()
  897. ],
  898. ),
  899. ),
  900. );
  901. }
  902. }