Hibok
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 
 
 

805 рядки
29 KiB

  1. import 'dart:async';
  2. import 'dart:ui';
  3. import 'package:agora_rtc_engine/agora_rtc_engine.dart';
  4. import 'package:cached_network_image/cached_network_image.dart';
  5. import 'package:chat/chat/gift_page.dart';
  6. import 'package:chat/data/UserData.dart';
  7. import 'package:chat/data/WebData.dart';
  8. import 'package:chat/data/constants.dart';
  9. import 'package:chat/generated/i18n.dart';
  10. import 'package:chat/models/UserInfo.dart';
  11. import 'package:chat/models/gift_item_model.dart';
  12. import 'package:chat/models/gift_select_provider.dart';
  13. import 'package:chat/models/money_change.dart';
  14. import 'package:chat/models/ref_name_provider.dart';
  15. import 'package:chat/r.dart';
  16. import 'package:chat/utils/ChargeMoney.dart';
  17. import 'package:chat/utils/CustomUI.dart';
  18. import 'package:chat/utils/HttpUtil.dart';
  19. import 'package:chat/utils/MessageMgr.dart';
  20. import 'package:chat/utils/TokenMgr.dart';
  21. import 'package:chat/utils/anim_effect_overlay.dart';
  22. import 'package:chat/utils/counter_overlay.dart';
  23. import 'package:chat/utils/msgHandler.dart';
  24. import 'package:chat/utils/screen.dart';
  25. import 'package:chat/utils/sound_util.dart';
  26. import 'package:chat/utils/sp_utils.dart';
  27. import 'package:dio/dio.dart';
  28. import 'package:flutter/material.dart';
  29. import 'package:oktoast/oktoast.dart';
  30. import 'package:provider/provider.dart';
  31. class AudioChatPage extends StatefulWidget {
  32. final UserInfo userInfo;
  33. final bool isReplay;
  34. final bool isTranslateButler;
  35. final Function translateButlerCloseCallBack;
  36. AudioChatPage({@required this.userInfo, this.isReplay = false, this.isTranslateButler=false,this.translateButlerCloseCallBack});
  37. @override
  38. _AudioChatPageState createState() => _AudioChatPageState();
  39. }
  40. class _AudioChatPageState extends State<AudioChatPage> {
  41. //是否启用扬声器
  42. bool speakPhone = false;
  43. int friendId;
  44. bool isReply; //是否应答模式
  45. bool isChating = false; //是否通话中
  46. //超时挂断
  47. Timer offTimer;
  48. Timer callingTimer;
  49. String channelName;
  50. //打赏礼物信息
  51. List<GiftItemModel> giftList;
  52. final _controller = new PageController();
  53. //礼物数量
  54. int curGiftValue = 1;
  55. bool isQuit = false;
  56. @override
  57. void initState() {
  58. super.initState();
  59. friendId = widget.userInfo.userId;
  60. isReply = widget.isReplay;
  61. MsgHandler.isAudioConnect = true;
  62. getGiftList();
  63. initAgoreSdk();
  64. getDefaultSetting();
  65. //事件监听回调
  66. setAgoreEventListener();
  67. //礼物打赏
  68. MessageMgr().on('Receive Gift', receiveGift);
  69. MessageMgr().on('AudioChat Failed', closeChat);
  70. MessageMgr().on('AudioChat State', refuseAnswer);
  71. }
  72. void getDefaultSetting() async {
  73. bool soundPlayMode =
  74. (await SPUtils.getBool(Constants.SOUND_PLAY_MODE)) ?? false;
  75. setState(() {
  76. speakPhone = soundPlayMode;
  77. });
  78. AgoraRtcEngine.setEnableSpeakerphone(speakPhone);
  79. }
  80. closeChat(args) {
  81. showToast(I18n.of(context).not_online);
  82. _onExit(context);
  83. }
  84. @override
  85. void didChangeDependencies() {
  86. super.didChangeDependencies();
  87. print('test didChangeDependencies');
  88. }
  89. refuseAnswer(args) {
  90. var fdId = args['fdId'];
  91. var isAnswer = args['isAnswer'];
  92. if (fdId == friendId && !isAnswer) {
  93. // showToast('对方暂时无法接听');
  94. _onExit(context);
  95. }
  96. }
  97. receiveGift(gift) {
  98. int giftId = gift.giftId;
  99. int amount = gift.giftAmount;
  100. int total = gift.money;
  101. print(DateTime.now());
  102. var giftModel = giftList[giftId - 1];
  103. print('收到礼物金额:$total');
  104. UserData().incomeMoney += total;
  105. //Provider.of<MoneyChangeProvider>(context, listen: false).addMoney(total);
  106. AnimEffect.showEffect(context, giftId);
  107. AnimEffect.showDashangEffect(context, false, amount, giftModel.name);
  108. }
  109. //本页面即将销毁
  110. @override
  111. void dispose() {
  112. _controller.dispose();
  113. MessageMgr().off('AudioChat State', refuseAnswer);
  114. MessageMgr().off('AudioChat Failed', closeChat);
  115. MessageMgr().off('Receive Gift', receiveGift);
  116. MsgHandler.isAudioConnect = false;
  117. offTimer?.cancel();
  118. callingTimer?.cancel();
  119. SoundUtils().stop();
  120. AgoraRtcEngine.leaveChannel();
  121. //sdk资源释放
  122. AgoraRtcEngine.destroy();
  123. super.dispose();
  124. }
  125. void initAgoreSdk() {
  126. //初始化引擎
  127. AgoraRtcEngine.create(Constants.Agore_appId);
  128. //设置视频为可用 启用音频模块
  129. AgoraRtcEngine.enableAudio();
  130. //每次需要原生视频都要调用_createRendererView
  131. if (!isReply) {
  132. int myId = UserData().basicInfo.userId;
  133. _createRendererView(myId);
  134. }
  135. }
  136. void getGiftList() async {
  137. Map data = {
  138. "userId": UserData().basicInfo.userId,
  139. };
  140. data['sign'] = TokenMgr().getSign(data);
  141. Response res = await HttpUtil().post('prop/get/list', data: data);
  142. if (res == null) {
  143. return;
  144. }
  145. print(res);
  146. Map resData = res.data;
  147. if (resData['code'] == 0) {
  148. //领取成功
  149. giftList = resData['data']
  150. .map<GiftItemModel>((v) => GiftItemModel.fromJson(v))
  151. .toList();
  152. print('giftList length : ${giftList.length}');
  153. }
  154. }
  155. @override
  156. Widget build(BuildContext context) {
  157. return Scaffold(
  158. body: SafeArea(
  159. child: Stack(
  160. children: <Widget>[
  161. ConstrainedBox(
  162. constraints: BoxConstraints.expand(),
  163. child: CachedNetworkImage(
  164. imageUrl: widget.userInfo.headimgurl,
  165. fit: BoxFit.fill,
  166. placeholder: CustomUI.buildImgLoding)),
  167. BackdropFilter(
  168. filter: new ImageFilter.blur(
  169. sigmaX: Screen.width / 8, sigmaY: Screen.width / 8),
  170. child: Container(
  171. color: Colors.black.withOpacity(0.5),
  172. ),
  173. ),
  174. SafeArea(
  175. child: Padding(
  176. padding: EdgeInsets.symmetric(vertical: 40),
  177. child: Column(
  178. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  179. children: <Widget>[
  180. _viewAudio(),
  181. isReply ? _replayToolBar() : _bottomToolBar(),
  182. ],
  183. ))),
  184. widget.isTranslateButler?InkWell(child: Container( width: 60,height: 60,child: Align(child: Icon(
  185. IconData(0xe67f, fontFamily: Constants.IconFontFamily),
  186. color: Colors.white,
  187. size: 28.0,
  188. ),alignment: Alignment.center,),),onTap: (){
  189. widget.translateButlerCloseCallBack(1);
  190. print('内页内页');
  191. },):Container()
  192. ],
  193. )));
  194. }
  195. _viewAudio() {
  196. var url = widget.userInfo.headimgurl;
  197. print('url : $url');
  198. print(url.length);
  199. var userName = Provider.of<RefNameProvider>(context)
  200. .getRefName(widget.userInfo.userId, widget.userInfo.nickName);
  201. var local = WebData().getCity(widget.userInfo.city);
  202. var profession = WebData().getProffesionName(widget.userInfo.occupation);
  203. var birth = widget.userInfo.birthday;
  204. print('birth : $birth');
  205. var birthDay = DateTime.parse(birth);
  206. print(birthDay);
  207. var today = DateTime.now();
  208. var testAge = today.difference(birthDay);
  209. print(testAge.inDays);
  210. var age = testAge.inDays ~/ 365;
  211. var connectTip;
  212. if (isChating) {
  213. connectTip = I18n.of(context).chatting;
  214. } else {
  215. if (isReply) {
  216. connectTip = I18n.of(context).voice_msg.replaceFirst('/s1', userName);
  217. } else {
  218. connectTip = I18n.of(context).waitting_answer;
  219. }
  220. }
  221. return Container(
  222. child: Column(
  223. children: <Widget>[
  224. ClipRRect(
  225. borderRadius: BorderRadius.circular(0),
  226. child: url.length == 0
  227. ? SizedBox(
  228. height: 100,
  229. width: 100,
  230. child: Image.asset(R.assetsImagesDefaultNorAvatar))
  231. : CachedNetworkImage(
  232. width: 100,
  233. height: 100,
  234. imageUrl: url,
  235. placeholder: (context, url) => Padding(
  236. padding: EdgeInsets.all(5),
  237. child: CircularProgressIndicator(
  238. valueColor:
  239. AlwaysStoppedAnimation(Constants.BlueTextColor)),
  240. ),
  241. fit: BoxFit.cover,
  242. ),
  243. ),
  244. SizedBox(height: 5),
  245. Row(
  246. mainAxisAlignment: MainAxisAlignment.center,
  247. children: <Widget>[
  248. Container(
  249. constraints: BoxConstraints(maxWidth: Screen.width - 50),
  250. child: Text(
  251. userName,
  252. style: TextStyle(fontSize: 20, color: Colors.white70),
  253. textScaleFactor: 1.0,
  254. overflow: TextOverflow.ellipsis,
  255. textAlign: TextAlign.center,
  256. )),
  257. fixedText(' $age', fontSize: 20, color: Colors.white70)
  258. ],
  259. ),
  260. SizedBox(height: 5),
  261. Text(
  262. '$local',
  263. style: TextStyle(fontSize: 16, color: Colors.white70),
  264. textScaleFactor: 1.0,
  265. textAlign: TextAlign.center,
  266. ),
  267. SizedBox(height: 5),
  268. Text(
  269. '$profession',
  270. style: TextStyle(fontSize: 16, color: Colors.white70),
  271. textScaleFactor: 1.0,
  272. textAlign: TextAlign.center,
  273. ),
  274. SizedBox(height: 20),
  275. Container(
  276. width: Screen.width - 50,
  277. child: Text(
  278. connectTip,
  279. //maxLines: 2,
  280. style: TextStyle(fontSize: 20, color: Colors.white70),
  281. textScaleFactor: 1.0,
  282. textAlign: TextAlign.center,
  283. )),
  284. ],
  285. ),
  286. );
  287. }
  288. _replayToolBar() {
  289. return Container(
  290. child: Row(
  291. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  292. children: <Widget>[
  293. //接听按钮
  294. RawMaterialButton(
  295. onPressed: () {
  296. MsgHandler.stopAudioRing();
  297. int myId = UserData().basicInfo.userId;
  298. MsgHandler.sendReplyAudioChatReq(friendId, true);
  299. _createRendererView(myId);
  300. },
  301. child: Icon(
  302. Icons.call,
  303. color: Colors.white,
  304. size: 35.0,
  305. ),
  306. shape: CircleBorder(),
  307. elevation: 2.0,
  308. fillColor: Colors.greenAccent,
  309. padding: const EdgeInsets.all(15.0),
  310. ),
  311. //挂断按钮
  312. RawMaterialButton(
  313. onPressed: () {
  314. _onExit(context);
  315. MsgHandler.stopAudioRing();
  316. MsgHandler.sendReplyAudioChatReq(friendId, false);
  317. },
  318. child: Icon(
  319. Icons.call_end,
  320. color: Colors.white,
  321. size: 35.0,
  322. ),
  323. shape: CircleBorder(),
  324. elevation: 2.0,
  325. fillColor: Colors.redAccent,
  326. padding: const EdgeInsets.all(15.0),
  327. )
  328. ]));
  329. }
  330. _bottomToolBar() {
  331. List<Widget> showWidgets = [
  332. Text(I18n.of(context).voicing,
  333. textScaleFactor: 1.0,
  334. style: TextStyle(fontSize: 11, color: Colors.white24),
  335. textAlign: TextAlign.center),
  336. SizedBox(height: 10),
  337. Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[
  338. Offstage(
  339. offstage: !isChating,
  340. child: UserData().giftSwitch > 0
  341. ? RawMaterialButton(
  342. onPressed: _showGiftSheet,
  343. child: Icon(
  344. IconData(0xe64e, fontFamily: Constants.IconFontFamily),
  345. color: Colors.blueAccent,
  346. size: 20.0,
  347. ),
  348. shape: CircleBorder(),
  349. elevation: 2.0,
  350. fillColor: Colors.white,
  351. padding: const EdgeInsets.all(12.0),
  352. )
  353. : SizedBox(width: 36, height: 36)),
  354. //挂断按钮
  355. RawMaterialButton(
  356. onPressed: () {
  357. MsgHandler.sendReplyAudioChatReq(friendId, false);
  358. _onExit(context);
  359. },
  360. child: Icon(
  361. Icons.call_end,
  362. color: Colors.white,
  363. size: 35.0,
  364. ),
  365. shape: CircleBorder(),
  366. elevation: 2.0,
  367. fillColor: Colors.redAccent,
  368. padding: const EdgeInsets.all(15.0),
  369. ),
  370. //是否外放
  371. Offstage(
  372. offstage: !isChating,
  373. child: RawMaterialButton(
  374. onPressed: _isSpeakPhone,
  375. child: new Icon(
  376. speakPhone ? Icons.volume_up : Icons.volume_down,
  377. color: Colors.blueAccent,
  378. size: 20.0,
  379. ),
  380. shape: new CircleBorder(),
  381. elevation: 2.0,
  382. fillColor: Colors.white,
  383. padding: const EdgeInsets.all(12.0),
  384. ))
  385. ])
  386. ];
  387. if (isChating) {
  388. showWidgets.insert(
  389. 0,
  390. Container(
  391. alignment: Alignment.center,
  392. height: 80,
  393. width: 200,
  394. child: CounterOverlay(),
  395. ));
  396. }
  397. return Container(
  398. child: Column(children: showWidgets),
  399. );
  400. }
  401. //创建渲染视图
  402. void _createRendererView(int uId) {
  403. //增加音频会话对象 为了音频布局需要(通过uid和容器信息)
  404. //加入频道 第一个参数是 token 第二个是频道id 第三个参数 频道信息 一般为空 第四个 用户id
  405. int myId = UserData().basicInfo.userId;
  406. setState(() {
  407. print('加入聊天房间');
  408. var channelName = UserData().getSessionId(friendId).toString();
  409. AgoraRtcEngine.joinChannel(null, channelName, null, myId);
  410. });
  411. }
  412. //设置事件监听
  413. void setAgoreEventListener() {
  414. //成功加入房间
  415. AgoraRtcEngine.onJoinChannelSuccess =
  416. (String channel, int uid, int elapsed) {
  417. print("加入成功id为:$uid elapsed:$elapsed");
  418. if (isReply) {
  419. //回应
  420. setState(() {
  421. isReply = false;
  422. isChating = true;
  423. });
  424. } else {
  425. // if(!widget.isTranslateButler){
  426. //
  427. // }
  428. MsgHandler.sendAudioChatReq(widget.userInfo.userId);
  429. callingTimer = Timer.periodic(Duration(milliseconds: 2200), (timer) {
  430. SoundUtils().play(
  431. 'http://testcyhd.chengyouhd.com/Upload/Audio/even_wheat_sound.mp3',
  432. isLocal: false);
  433. });
  434. //30没人接就自动挂断
  435. offTimer = Timer(Duration(seconds: 30), () {
  436. MsgHandler.sendReplyAudioChatReq(friendId, false);
  437. _onExit(context);
  438. });
  439. }
  440. };
  441. //监听是否有新用户加入
  442. AgoraRtcEngine.onUserJoined = (int uid, int elapsed) {
  443. print("新用户所加入的id为:$uid elapsed:$elapsed");
  444. setState(() {
  445. //更新UI布局
  446. SoundUtils().stop();
  447. callingTimer?.cancel();
  448. isChating = true;
  449. offTimer?.cancel();
  450. });
  451. };
  452. //监听用户是否离开这个房间
  453. AgoraRtcEngine.onUserOffline = (int uid, int reason) {
  454. print("用户离开的id为:$uid");
  455. _onExit(context);
  456. };
  457. AgoraRtcEngine.onError = (dynamic errCode) {
  458. print('通话异常');
  459. // _onExit(context);
  460. };
  461. //监听用户是否离开这个频道
  462. AgoraRtcEngine.onLeaveChannel = () {
  463. print("用户离开");
  464. };
  465. }
  466. void _showGiftSheet() {
  467. showModalBottomSheet(
  468. context: context,
  469. elevation: 2.0,
  470. shape: RoundedRectangleBorder(
  471. borderRadius: BorderRadius.only(
  472. topLeft: Radius.circular(20), topRight: Radius.circular(20))),
  473. backgroundColor: Colors.transparent,
  474. builder: (BuildContext context) {
  475. return StatefulBuilder(
  476. builder: (BuildContext context, setBottomSheetState) {
  477. return Container(
  478. height: 400,
  479. width: Screen.width,
  480. decoration: BoxDecoration(
  481. color: Colors.white,
  482. ),
  483. child: giftList == null
  484. ? Center(
  485. child: Text(
  486. I18n.of(context).no_gift,
  487. textScaleFactor: 1.0,
  488. ))
  489. : Column(
  490. children: <Widget>[
  491. Container(
  492. // decoration: BoxDecoration(
  493. // color: Colors.orangeAccent,
  494. // borderRadius: borderRadius
  495. // ),
  496. padding: EdgeInsets.symmetric(
  497. vertical: 10, horizontal: 20),
  498. child: Row(
  499. children: <Widget>[
  500. Text(I18n.of(context).sent_gift,
  501. textScaleFactor: 1.0,
  502. style: TextStyle(
  503. fontSize: 16,
  504. fontWeight: FontWeight.bold,
  505. color: Constants.BlackTextColor)),
  506. Expanded(
  507. child: SizedBox(),
  508. ),
  509. Image.asset(
  510. R.assetsImagesCoin,
  511. scale: 2,
  512. ),
  513. SizedBox(
  514. width: 5,
  515. ),
  516. fixedText(
  517. I18n.of(context)
  518. .available_balance
  519. .replaceFirst('/s1', ''),
  520. color: Constants.GreyTextColor),
  521. Consumer<MoneyChangeProvider>(
  522. builder: (context,
  523. MoneyChangeProvider counter,
  524. child) =>
  525. fixedText(counter.money.toString(),
  526. color: Color(0xFFDB305D)),
  527. ),
  528. fixedText(I18n.of(context).mask_coin,
  529. color: Color(0xFFDB305D)),
  530. ],
  531. ),
  532. ),
  533. Container(
  534. alignment: Alignment.topCenter,
  535. padding: EdgeInsets.all(10),
  536. height: 230,
  537. child: GiftPage(giftList, 0),
  538. ),
  539. Expanded(child: SizedBox()),
  540. Padding(
  541. padding: EdgeInsets.symmetric(
  542. vertical: 5, horizontal: 20),
  543. child: Row(
  544. children: <Widget>[
  545. Expanded(
  546. child: SizedBox(
  547. height: 36,
  548. child: RaisedButton(
  549. padding:
  550. EdgeInsets.symmetric(horizontal: 5),
  551. child: Text(
  552. I18n.of(context).recharge,
  553. textScaleFactor: 1.0,
  554. maxLines: 1,
  555. style: TextStyle(
  556. fontSize: 16,
  557. fontWeight: FontWeight.bold),
  558. ),
  559. elevation: 2.0,
  560. color: Colors.orangeAccent,
  561. textColor: Colors.white,
  562. onPressed: () {
  563. ChargeMoney.showChargeSheet(context,
  564. () {
  565. setState(() {});
  566. });
  567. },
  568. shape: RoundedRectangleBorder(
  569. borderRadius: BorderRadius.all(
  570. Radius.circular(20)),
  571. ),
  572. ),
  573. )),
  574. SizedBox(width: 20),
  575. Expanded(
  576. flex: 2,
  577. child: Container(
  578. height: 36,
  579. decoration: BoxDecoration(
  580. boxShadow: [
  581. BoxShadow(
  582. blurRadius: 2.0,
  583. color: Colors.white24)
  584. ],
  585. borderRadius: BorderRadius.all(
  586. Radius.circular(20)),
  587. border: Border.all(
  588. color: Colors.red)),
  589. child: Row(
  590. children: <Widget>[
  591. Expanded(
  592. child: DropdownButton(
  593. isExpanded: true,
  594. underline: Container(),
  595. value: curGiftValue,
  596. style: TextStyle(fontSize: 16),
  597. iconEnabledColor:
  598. Colors.redAccent,
  599. onChanged: (newValue) {
  600. setBottomSheetState(() {
  601. curGiftValue = newValue;
  602. });
  603. },
  604. items: <int>[1, 6, 8, 66]
  605. .map<DropdownMenuItem>((v) => DropdownMenuItem(
  606. value: v,
  607. child: Container(
  608. padding:
  609. EdgeInsets.only(
  610. left: 20),
  611. child: fixedText(
  612. v.toString(),
  613. color: v == curGiftValue
  614. ? Colors
  615. .redAccent
  616. : Colors
  617. .black))))
  618. .toList(),
  619. )),
  620. Expanded(
  621. child: InkWell(
  622. child: Container(
  623. decoration: BoxDecoration(
  624. color: Color(0xFFFD6E8F),
  625. borderRadius:
  626. BorderRadius.only(
  627. topRight: Radius
  628. .circular(20),
  629. bottomRight:
  630. Radius.circular(
  631. 20)),
  632. ),
  633. height: 36,
  634. alignment: Alignment.center,
  635. child: Text(
  636. I18n.of(context).give,
  637. textScaleFactor: 1.0,
  638. style: TextStyle(
  639. color: Colors.white,
  640. fontSize: 16,
  641. fontWeight:
  642. FontWeight.bold),
  643. ),
  644. ),
  645. onTap: () {
  646. _sendGift(context);
  647. },
  648. ))
  649. ],
  650. )))
  651. ],
  652. ),
  653. )
  654. ],
  655. ));
  656. },
  657. );
  658. });
  659. }
  660. _sendGift(BuildContext context) async {
  661. int curSelectIndex =
  662. Provider.of<GiftSelectProvider>(context).curSelectIndex;
  663. var curGift = giftList[curSelectIndex];
  664. int price = curGift.price;
  665. int giftId = curGift.id;
  666. int total = curGiftValue * price;
  667. if (total > Provider.of<MoneyChangeProvider>(context).money) {
  668. showToast(I18n.of(context).not_enough);
  669. return;
  670. }
  671. int buyCount = 0;
  672. if (curGiftValue > curGift.amount) {
  673. buyCount = curGiftValue - curGift.amount;
  674. }
  675. Map data = {
  676. "userId": UserData().basicInfo.userId,
  677. "rUserId": friendId,
  678. "id": giftId,
  679. "price": price,
  680. "amount": curGiftValue,
  681. "totalPrice": total,
  682. "payNum": buyCount
  683. };
  684. data['sign'] = TokenMgr().getSign(data);
  685. Response res = await HttpUtil().post('reward/gift', data: data);
  686. if (res == null) {
  687. return;
  688. }
  689. Map resData = res.data;
  690. //showToast(resData['msg']);
  691. if (resData['code'] == 0) {
  692. //赠送成功
  693. print('赠送金额:${resData['data']}');
  694. int receiveAmount = resData['data'];
  695. Navigator.pop(context);
  696. Provider.of<MoneyChangeProvider>(context, listen: false).subMoney(total);
  697. AnimEffect.showEffect(context, giftId);
  698. AnimEffect.showDashangEffect(context, true, curGiftValue, curGift.name);
  699. MsgHandler.sendGiftTo(friendId, giftId, curGiftValue, receiveAmount);
  700. setState(() {});
  701. }
  702. }
  703. //是否开启扬声器
  704. void _isSpeakPhone() {
  705. setState(() {
  706. speakPhone = !speakPhone;
  707. });
  708. AgoraRtcEngine.setEnableSpeakerphone(speakPhone);
  709. }
  710. //退出频道 退出本页面
  711. void _onExit(BuildContext context) {
  712. if (!isQuit) {
  713. print('连麦关闭AAAA');
  714. if (Navigator.canPop(context)) {
  715. isQuit = true;
  716. print('连麦关闭BBBB');
  717. if(widget.translateButlerCloseCallBack!=null){
  718. print('连麦关闭CCCCCCCC');
  719. widget.translateButlerCloseCallBack(2);
  720. }else{
  721. Navigator.of(context).pop();
  722. }
  723. SoundUtils().stop();
  724. callingTimer?.cancel();
  725. }
  726. }
  727. }
  728. }