Hibok
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

795 line
28 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,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?Material(child: InkWell(child: Container(color: Colors.yellow,width: 60,height: 60,),onTap: (){
  185. widget.translateButlerCloseCallBack(1);
  186. print('内页内页');
  187. },),):Container()
  188. ],
  189. )));
  190. }
  191. _viewAudio() {
  192. var url = widget.userInfo.headimgurl;
  193. print('url : $url');
  194. print(url.length);
  195. var userName = Provider.of<RefNameProvider>(context)
  196. .getRefName(widget.userInfo.userId, widget.userInfo.nickName);
  197. var local = WebData().getCity(widget.userInfo.city);
  198. var profession = WebData().getProffesionName(widget.userInfo.occupation);
  199. var birth = widget.userInfo.birthday;
  200. print('birth : $birth');
  201. var birthDay = DateTime.parse(birth);
  202. print(birthDay);
  203. var today = DateTime.now();
  204. var testAge = today.difference(birthDay);
  205. print(testAge.inDays);
  206. var age = testAge.inDays ~/ 365;
  207. var connectTip;
  208. if (isChating) {
  209. connectTip = I18n.of(context).chatting;
  210. } else {
  211. if (isReply) {
  212. connectTip = I18n.of(context).voice_msg.replaceFirst('/s1', userName);
  213. } else {
  214. connectTip = I18n.of(context).waitting_answer;
  215. }
  216. }
  217. return Container(
  218. child: Column(
  219. children: <Widget>[
  220. ClipRRect(
  221. borderRadius: BorderRadius.circular(0),
  222. child: url.length == 0
  223. ? SizedBox(
  224. height: 100,
  225. width: 100,
  226. child: Image.asset(R.assetsImagesDefaultNorAvatar))
  227. : CachedNetworkImage(
  228. width: 100,
  229. height: 100,
  230. imageUrl: url,
  231. placeholder: (context, url) => Padding(
  232. padding: EdgeInsets.all(5),
  233. child: CircularProgressIndicator(
  234. valueColor:
  235. AlwaysStoppedAnimation(Constants.BlueTextColor)),
  236. ),
  237. fit: BoxFit.cover,
  238. ),
  239. ),
  240. SizedBox(height: 5),
  241. Row(
  242. mainAxisAlignment: MainAxisAlignment.center,
  243. children: <Widget>[
  244. Container(
  245. constraints: BoxConstraints(maxWidth: Screen.width - 50),
  246. child: Text(
  247. userName,
  248. style: TextStyle(fontSize: 20, color: Colors.white70),
  249. textScaleFactor: 1.0,
  250. overflow: TextOverflow.ellipsis,
  251. textAlign: TextAlign.center,
  252. )),
  253. fixedText(' $age', fontSize: 20, color: Colors.white70)
  254. ],
  255. ),
  256. SizedBox(height: 5),
  257. Text(
  258. '$local',
  259. style: TextStyle(fontSize: 16, color: Colors.white70),
  260. textScaleFactor: 1.0,
  261. textAlign: TextAlign.center,
  262. ),
  263. SizedBox(height: 5),
  264. Text(
  265. '$profession',
  266. style: TextStyle(fontSize: 16, color: Colors.white70),
  267. textScaleFactor: 1.0,
  268. textAlign: TextAlign.center,
  269. ),
  270. SizedBox(height: 20),
  271. Container(
  272. width: Screen.width - 50,
  273. child: Text(
  274. connectTip,
  275. //maxLines: 2,
  276. style: TextStyle(fontSize: 20, color: Colors.white70),
  277. textScaleFactor: 1.0,
  278. textAlign: TextAlign.center,
  279. )),
  280. ],
  281. ),
  282. );
  283. }
  284. _replayToolBar() {
  285. return Container(
  286. child: Row(
  287. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  288. children: <Widget>[
  289. //接听按钮
  290. RawMaterialButton(
  291. onPressed: () {
  292. MsgHandler.stopAudioRing();
  293. int myId = UserData().basicInfo.userId;
  294. MsgHandler.sendReplyAudioChatReq(friendId, true);
  295. _createRendererView(myId);
  296. },
  297. child: Icon(
  298. Icons.call,
  299. color: Colors.white,
  300. size: 35.0,
  301. ),
  302. shape: CircleBorder(),
  303. elevation: 2.0,
  304. fillColor: Colors.greenAccent,
  305. padding: const EdgeInsets.all(15.0),
  306. ),
  307. //挂断按钮
  308. RawMaterialButton(
  309. onPressed: () {
  310. _onExit(context);
  311. MsgHandler.stopAudioRing();
  312. MsgHandler.sendReplyAudioChatReq(friendId, false);
  313. },
  314. child: Icon(
  315. Icons.call_end,
  316. color: Colors.white,
  317. size: 35.0,
  318. ),
  319. shape: CircleBorder(),
  320. elevation: 2.0,
  321. fillColor: Colors.redAccent,
  322. padding: const EdgeInsets.all(15.0),
  323. )
  324. ]));
  325. }
  326. _bottomToolBar() {
  327. List<Widget> showWidgets = [
  328. Text(I18n.of(context).voicing,
  329. textScaleFactor: 1.0,
  330. style: TextStyle(fontSize: 11, color: Colors.white24),
  331. textAlign: TextAlign.center),
  332. SizedBox(height: 10),
  333. Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[
  334. Offstage(
  335. offstage: !isChating,
  336. child: UserData().giftSwitch > 0
  337. ? RawMaterialButton(
  338. onPressed: _showGiftSheet,
  339. child: Icon(
  340. IconData(0xe64e, fontFamily: Constants.IconFontFamily),
  341. color: Colors.blueAccent,
  342. size: 20.0,
  343. ),
  344. shape: CircleBorder(),
  345. elevation: 2.0,
  346. fillColor: Colors.white,
  347. padding: const EdgeInsets.all(12.0),
  348. )
  349. : SizedBox(width: 36, height: 36)),
  350. //挂断按钮
  351. RawMaterialButton(
  352. onPressed: () {
  353. MsgHandler.sendReplyAudioChatReq(friendId, false);
  354. _onExit(context);
  355. },
  356. child: Icon(
  357. Icons.call_end,
  358. color: Colors.white,
  359. size: 35.0,
  360. ),
  361. shape: CircleBorder(),
  362. elevation: 2.0,
  363. fillColor: Colors.redAccent,
  364. padding: const EdgeInsets.all(15.0),
  365. ),
  366. //是否外放
  367. Offstage(
  368. offstage: !isChating,
  369. child: RawMaterialButton(
  370. onPressed: _isSpeakPhone,
  371. child: new Icon(
  372. speakPhone ? Icons.volume_up : Icons.volume_down,
  373. color: Colors.blueAccent,
  374. size: 20.0,
  375. ),
  376. shape: new CircleBorder(),
  377. elevation: 2.0,
  378. fillColor: Colors.white,
  379. padding: const EdgeInsets.all(12.0),
  380. ))
  381. ])
  382. ];
  383. if (isChating) {
  384. showWidgets.insert(
  385. 0,
  386. Container(
  387. alignment: Alignment.center,
  388. height: 80,
  389. width: 200,
  390. child: CounterOverlay(),
  391. ));
  392. }
  393. return Container(
  394. child: Column(children: showWidgets),
  395. );
  396. }
  397. //创建渲染视图
  398. void _createRendererView(int uId) {
  399. //增加音频会话对象 为了音频布局需要(通过uid和容器信息)
  400. //加入频道 第一个参数是 token 第二个是频道id 第三个参数 频道信息 一般为空 第四个 用户id
  401. int myId = UserData().basicInfo.userId;
  402. setState(() {
  403. print('加入聊天房间');
  404. var channelName = UserData().getSessionId(friendId).toString();
  405. AgoraRtcEngine.joinChannel(null, channelName, null, myId);
  406. });
  407. }
  408. //设置事件监听
  409. void setAgoreEventListener() {
  410. //成功加入房间
  411. AgoraRtcEngine.onJoinChannelSuccess =
  412. (String channel, int uid, int elapsed) {
  413. print("加入成功id为:$uid elapsed:$elapsed");
  414. if (isReply) {
  415. //回应
  416. setState(() {
  417. isReply = false;
  418. isChating = true;
  419. });
  420. } else {
  421. MsgHandler.sendAudioChatReq(widget.userInfo.userId);
  422. callingTimer = Timer.periodic(Duration(milliseconds: 2200), (timer) {
  423. SoundUtils().play(
  424. 'http://testcyhd.chengyouhd.com/Upload/Audio/even_wheat_sound.mp3',
  425. isLocal: false);
  426. });
  427. //30没人接就自动挂断
  428. offTimer = Timer(Duration(seconds: 30), () {
  429. MsgHandler.sendReplyAudioChatReq(friendId, false);
  430. _onExit(context);
  431. });
  432. }
  433. };
  434. //监听是否有新用户加入
  435. AgoraRtcEngine.onUserJoined = (int uid, int elapsed) {
  436. print("新用户所加入的id为:$uid elapsed:$elapsed");
  437. setState(() {
  438. //更新UI布局
  439. SoundUtils().stop();
  440. callingTimer?.cancel();
  441. isChating = true;
  442. offTimer?.cancel();
  443. });
  444. };
  445. //监听用户是否离开这个房间
  446. AgoraRtcEngine.onUserOffline = (int uid, int reason) {
  447. print("用户离开的id为:$uid");
  448. _onExit(context);
  449. };
  450. AgoraRtcEngine.onError = (dynamic errCode) {
  451. print('通话异常');
  452. // _onExit(context);
  453. };
  454. //监听用户是否离开这个频道
  455. AgoraRtcEngine.onLeaveChannel = () {
  456. print("用户离开");
  457. };
  458. }
  459. void _showGiftSheet() {
  460. showModalBottomSheet(
  461. context: context,
  462. elevation: 2.0,
  463. shape: RoundedRectangleBorder(
  464. borderRadius: BorderRadius.only(
  465. topLeft: Radius.circular(20), topRight: Radius.circular(20))),
  466. backgroundColor: Colors.transparent,
  467. builder: (BuildContext context) {
  468. return StatefulBuilder(
  469. builder: (BuildContext context, setBottomSheetState) {
  470. return Container(
  471. height: 400,
  472. width: Screen.width,
  473. decoration: BoxDecoration(
  474. color: Colors.white,
  475. ),
  476. child: giftList == null
  477. ? Center(
  478. child: Text(
  479. I18n.of(context).no_gift,
  480. textScaleFactor: 1.0,
  481. ))
  482. : Column(
  483. children: <Widget>[
  484. Container(
  485. // decoration: BoxDecoration(
  486. // color: Colors.orangeAccent,
  487. // borderRadius: borderRadius
  488. // ),
  489. padding: EdgeInsets.symmetric(
  490. vertical: 10, horizontal: 20),
  491. child: Row(
  492. children: <Widget>[
  493. Text(I18n.of(context).sent_gift,
  494. textScaleFactor: 1.0,
  495. style: TextStyle(
  496. fontSize: 16,
  497. fontWeight: FontWeight.bold,
  498. color: Constants.BlackTextColor)),
  499. Expanded(
  500. child: SizedBox(),
  501. ),
  502. Image.asset(
  503. R.assetsImagesCoin,
  504. scale: 2,
  505. ),
  506. SizedBox(
  507. width: 5,
  508. ),
  509. fixedText(
  510. I18n.of(context)
  511. .available_balance
  512. .replaceFirst('/s1', ''),
  513. color: Constants.GreyTextColor),
  514. Consumer<MoneyChangeProvider>(
  515. builder: (context,
  516. MoneyChangeProvider counter,
  517. child) =>
  518. fixedText(counter.money.toString(),
  519. color: Color(0xFFDB305D)),
  520. ),
  521. fixedText(I18n.of(context).mask_coin,
  522. color: Color(0xFFDB305D)),
  523. ],
  524. ),
  525. ),
  526. Container(
  527. alignment: Alignment.topCenter,
  528. padding: EdgeInsets.all(10),
  529. height: 230,
  530. child: GiftPage(giftList, 0),
  531. ),
  532. Expanded(child: SizedBox()),
  533. Padding(
  534. padding: EdgeInsets.symmetric(
  535. vertical: 5, horizontal: 20),
  536. child: Row(
  537. children: <Widget>[
  538. Expanded(
  539. child: SizedBox(
  540. height: 36,
  541. child: RaisedButton(
  542. padding:
  543. EdgeInsets.symmetric(horizontal: 5),
  544. child: Text(
  545. I18n.of(context).recharge,
  546. textScaleFactor: 1.0,
  547. maxLines: 1,
  548. style: TextStyle(
  549. fontSize: 16,
  550. fontWeight: FontWeight.bold),
  551. ),
  552. elevation: 2.0,
  553. color: Colors.orangeAccent,
  554. textColor: Colors.white,
  555. onPressed: () {
  556. ChargeMoney.showChargeSheet(context,
  557. () {
  558. setState(() {});
  559. });
  560. },
  561. shape: RoundedRectangleBorder(
  562. borderRadius: BorderRadius.all(
  563. Radius.circular(20)),
  564. ),
  565. ),
  566. )),
  567. SizedBox(width: 20),
  568. Expanded(
  569. flex: 2,
  570. child: Container(
  571. height: 36,
  572. decoration: BoxDecoration(
  573. boxShadow: [
  574. BoxShadow(
  575. blurRadius: 2.0,
  576. color: Colors.white24)
  577. ],
  578. borderRadius: BorderRadius.all(
  579. Radius.circular(20)),
  580. border: Border.all(
  581. color: Colors.red)),
  582. child: Row(
  583. children: <Widget>[
  584. Expanded(
  585. child: DropdownButton(
  586. isExpanded: true,
  587. underline: Container(),
  588. value: curGiftValue,
  589. style: TextStyle(fontSize: 16),
  590. iconEnabledColor:
  591. Colors.redAccent,
  592. onChanged: (newValue) {
  593. setBottomSheetState(() {
  594. curGiftValue = newValue;
  595. });
  596. },
  597. items: <int>[1, 6, 8, 66]
  598. .map<DropdownMenuItem>((v) => DropdownMenuItem(
  599. value: v,
  600. child: Container(
  601. padding:
  602. EdgeInsets.only(
  603. left: 20),
  604. child: fixedText(
  605. v.toString(),
  606. color: v == curGiftValue
  607. ? Colors
  608. .redAccent
  609. : Colors
  610. .black))))
  611. .toList(),
  612. )),
  613. Expanded(
  614. child: InkWell(
  615. child: Container(
  616. decoration: BoxDecoration(
  617. color: Color(0xFFFD6E8F),
  618. borderRadius:
  619. BorderRadius.only(
  620. topRight: Radius
  621. .circular(20),
  622. bottomRight:
  623. Radius.circular(
  624. 20)),
  625. ),
  626. height: 36,
  627. alignment: Alignment.center,
  628. child: Text(
  629. I18n.of(context).give,
  630. textScaleFactor: 1.0,
  631. style: TextStyle(
  632. color: Colors.white,
  633. fontSize: 16,
  634. fontWeight:
  635. FontWeight.bold),
  636. ),
  637. ),
  638. onTap: () {
  639. _sendGift(context);
  640. },
  641. ))
  642. ],
  643. )))
  644. ],
  645. ),
  646. )
  647. ],
  648. ));
  649. },
  650. );
  651. });
  652. }
  653. _sendGift(BuildContext context) async {
  654. int curSelectIndex =
  655. Provider.of<GiftSelectProvider>(context).curSelectIndex;
  656. var curGift = giftList[curSelectIndex];
  657. int price = curGift.price;
  658. int giftId = curGift.id;
  659. int total = curGiftValue * price;
  660. if (total > Provider.of<MoneyChangeProvider>(context).money) {
  661. showToast(I18n.of(context).not_enough);
  662. return;
  663. }
  664. int buyCount = 0;
  665. if (curGiftValue > curGift.amount) {
  666. buyCount = curGiftValue - curGift.amount;
  667. }
  668. Map data = {
  669. "userId": UserData().basicInfo.userId,
  670. "rUserId": friendId,
  671. "id": giftId,
  672. "price": price,
  673. "amount": curGiftValue,
  674. "totalPrice": total,
  675. "payNum": buyCount
  676. };
  677. data['sign'] = TokenMgr().getSign(data);
  678. Response res = await HttpUtil().post('reward/gift', data: data);
  679. if (res == null) {
  680. return;
  681. }
  682. Map resData = res.data;
  683. //showToast(resData['msg']);
  684. if (resData['code'] == 0) {
  685. //赠送成功
  686. print('赠送金额:${resData['data']}');
  687. int receiveAmount = resData['data'];
  688. Navigator.pop(context);
  689. Provider.of<MoneyChangeProvider>(context, listen: false).subMoney(total);
  690. AnimEffect.showEffect(context, giftId);
  691. AnimEffect.showDashangEffect(context, true, curGiftValue, curGift.name);
  692. MsgHandler.sendGiftTo(friendId, giftId, curGiftValue, receiveAmount);
  693. setState(() {});
  694. }
  695. }
  696. //是否开启扬声器
  697. void _isSpeakPhone() {
  698. setState(() {
  699. speakPhone = !speakPhone;
  700. });
  701. AgoraRtcEngine.setEnableSpeakerphone(speakPhone);
  702. }
  703. //退出频道 退出本页面
  704. void _onExit(BuildContext context) {
  705. if (!isQuit) {
  706. if (Navigator.canPop(context)) {
  707. isQuit = true;
  708. if(widget.translateButlerCloseCallBack!=null){
  709. widget.translateButlerCloseCallBack(2);
  710. }else{
  711. Navigator.of(context).pop();
  712. }
  713. SoundUtils().stop();
  714. callingTimer?.cancel();
  715. }
  716. }
  717. }
  718. }