meeting.dart 31 KB

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