chat_room.dart 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'dart:io';
  4. import 'package:flustars/flustars.dart' as flustars;
  5. import 'package:flutter/material.dart';
  6. import 'package:flutter_screenutil/flutter_screenutil.dart';
  7. import 'package:fluttertoast/fluttertoast.dart';
  8. import 'package:image_picker/image_picker.dart';
  9. import 'package:liftmanager/common/common.dart';
  10. import 'package:liftmanager/internal/bbs/page/jubao_page.dart';
  11. import 'package:liftmanager/net/api_service.dart';
  12. import 'package:liftmanager/res/iconfont.dart';
  13. import 'package:liftmanager/utils/fast_notification.dart';
  14. import 'package:liftmanager/utils/oss_upload.dart';
  15. import 'package:liftmanager/utils/toast.dart';
  16. import 'package:liftmanager/widgets/app_bar.dart';
  17. import 'package:liftmanager/widgets/chat_list_view.dart';
  18. import 'package:path_provider/path_provider.dart';
  19. import 'package:provider/provider.dart';
  20. // import 'package:shared_preferences/shared_preferences.dart';
  21. import './chat_detail/chat_content_view.dart';
  22. import '../model/conversation.dart';
  23. import '../provide/websocket.dart';
  24. import 'package:permission_handler/permission_handler.dart';
  25. import 'package:flutter_sound/flutter_sound.dart';
  26. import 'package:flutter_sound/public/flutter_sound_player.dart';
  27. import 'package:flutter_sound/public/flutter_sound_recorder.dart';
  28. class ChatDetailPage extends StatefulWidget {
  29. ChatDetailPage(
  30. {this.id,
  31. this.type,
  32. this.toUserId,
  33. this.jubaoToUserId,
  34. this.title = '聊天室'});
  35. final String id;
  36. final String type;
  37. final String toUserId;
  38. final String title;
  39. String jubaoToUserId;
  40. @override
  41. _ChatDetailPageState createState() => _ChatDetailPageState();
  42. // _ChatDetailPageState createState() => _ChatDetailPageState(type,index);
  43. }
  44. class _ChatDetailPageState extends State<ChatDetailPage> {
  45. bool hasText = false;
  46. bool isAutoJump = true;
  47. bool isDownKeyboard = true;
  48. int type = 0;
  49. int index = 1;
  50. var msgList = [];
  51. var msgListBack = [];
  52. var assList = [];
  53. bool showEmoji = false;
  54. bool hasMore = true;
  55. int _page = 1;
  56. int pageSize = 10;
  57. Conversation data;
  58. // _ChatDetailPageState(this.type0,this.index0);
  59. WebSocketProvide provider = WebSocketProvide();
  60. ScrollController _scrollController;
  61. var userList;
  62. var storyList = [];
  63. var storyListUserOnline = [];
  64. //避免从附近的人打招呼重复的initRoom
  65. bool isTrim = false;
  66. int dataTable;
  67. final controller = TextEditingController();
  68. bool voiceModeOn = false;
  69. bool voiceRecordingOn = false;
  70. FlutterSoundPlayer _player = FlutterSoundPlayer();
  71. FlutterSoundRecorder _recorder = FlutterSoundRecorder();
  72. bool _playerIsInited = false;
  73. bool _recorderIsInited = false;
  74. bool _playbackReady = true;
  75. String _audioFilePath;
  76. int recordingDuration = 0;
  77. Timer recordingTimer;
  78. AnimationController _animationController;
  79. void _handleSubmitted(context, String text) {
  80. // FocusScope.of(context).requestFocus(FocusNode());
  81. if (controller.text.length > 0) {
  82. // FocusScope.of(context).requestFocus(FocusNode());
  83. print('发送${text}');
  84. print(type);
  85. if (Provider.of<WebSocketProvide>(context, listen: false)
  86. .socketIsConnect) {
  87. controller.clear(); //清空输入框
  88. }
  89. Provider.of<WebSocketProvide>(context, listen: false).sendMessage(context,
  90. msg: text,
  91. id: widget.id,
  92. msgType: 1,
  93. dataTable: dataTable,
  94. toUserId: widget.toUserId);
  95. showEmoji = false;
  96. hasMore = true;
  97. _page = 1;
  98. setState(() {});
  99. // FocusScope.of(context).requestFocus(FocusNode());
  100. } else {
  101. toasts("请输入");
  102. }
  103. }
  104. void _jumpBottom() {
  105. _scrollController.animateTo(_scrollController.position.maxScrollExtent,
  106. curve: Curves.easeOut, duration: Duration(milliseconds: 200));
  107. }
  108. String emoji =
  109. "😀,😁,😂,😃,😄,😅,😆,😉,😊,😋,😎,😍,😘,😗,😙,😚,😇,😐,😑,😶,😏,😣,😥,😮,😯,😪,😫,😴,😌,😛,😜,😝,😒,😓,😔,😕,😲,😷,😖,😞,😟,😤,😢,😭,😦,😧,😨,😬,😰,😱,😳,😵,😡,😠";
  110. var emojiList = <String>[];
  111. void setWebSocket() async {
  112. // SharedPreferences prefs = await SharedPreferences.getInstance();
  113. new Future.delayed(Duration(milliseconds: 500), () {
  114. Provider.of<WebSocketProvide>(context, listen: false)
  115. .incomeRoom(widget.id);
  116. // getStringEvent(widget.id);
  117. });
  118. }
  119. void initJump() async {
  120. new Future.delayed(Duration(milliseconds: 500), () {
  121. FocusScope.of(context).requestFocus(FocusNode());
  122. _jumpBottom();
  123. });
  124. }
  125. void disposeJump() async {
  126. new Future.delayed(Duration(milliseconds: 500), () {
  127. Provider.of<WebSocketProvide>(context, listen: false).closeWebSocket();
  128. });
  129. }
  130. // void getMsgList() async {
  131. // new Future.delayed(Duration(milliseconds: 2000), () {
  132. // setState(() {
  133. // msgList = Provider.of<WebSocketProvide>(context, listen: false)
  134. // .historyMessageqqq;
  135. // print('getMsgList msgList ===== $msgList');
  136. // _jumpBottom();
  137. // });
  138. // });
  139. // }
  140. // 输入框的焦点实例
  141. FocusNode _focusNode;
  142. @override
  143. void initState() {
  144. _player.openAudioSession().then((value) {
  145. setState(() {
  146. _playerIsInited = true;
  147. });
  148. });
  149. openTheRecorder().then((value) {
  150. setState(() {
  151. _recorderIsInited = true;
  152. });
  153. });
  154. emojiList = emoji.split(',');
  155. if (widget.type == "nearToOne") {
  156. dataTable = 3;
  157. } else {
  158. dataTable = null;
  159. var roomIds = flustars.SpUtil.getStringList('showAlert');
  160. if (!roomIds.contains(widget.id)) {
  161. Fluttertoast.showToast(
  162. msg: '欢迎使用,请在使用过程中注意个人信息及财产安全',
  163. backgroundColor: Colors.amber,
  164. textColor: Colors.black,
  165. fontSize: 13,
  166. toastLength: Toast.LENGTH_LONG,
  167. gravity: ToastGravity.TOP,
  168. timeInSecForIosWeb: 3);
  169. } else {
  170. var newRoodIds = [widget.id];
  171. newRoodIds.addAll(roomIds);
  172. flustars.SpUtil.putStringList('showAlert', newRoodIds);
  173. }
  174. }
  175. print(widget.id);
  176. FastNotification.addListener("chat_room", (historyMessageqqq) {
  177. if (mounted) {
  178. setState(() {
  179. msgList = historyMessageqqq;
  180. assList = historyMessageqqq;
  181. print('chat_room msgList ===== $msgList');
  182. });
  183. _jumpBottom();
  184. }
  185. });
  186. FastNotification.addListener("chat_room_chat", (historyMessageqqq) {
  187. if (mounted) {
  188. setState(() {
  189. msgList = historyMessageqqq;
  190. assList = historyMessageqqq;
  191. print('chat_room_chat msgList ===== $msgList');
  192. });
  193. _jumpBottom();
  194. }
  195. });
  196. FastNotification.addListener("set_user", (setUserList) {
  197. if (mounted) {
  198. new Future.delayed(Duration(milliseconds: 1000), () {
  199. // setState(() {
  200. // userList = setUserList;
  201. // });
  202. });
  203. }
  204. });
  205. FastNotification.addListener("initSocket", (initThisSocket) {
  206. if (mounted) {
  207. isTrim = true;
  208. setWebSocket();
  209. _jumpBottom();
  210. FocusScope.of(context).requestFocus(FocusNode());
  211. toasts("聊天室已重连!");
  212. }
  213. });
  214. if (!Provider.of<WebSocketProvide>(context, listen: false)
  215. .socketIsConnect) {
  216. Provider.of<WebSocketProvide>(context, listen: false).createWebsocket(
  217. Provider.of<WebSocketProvide>(context, listen: false).roomId);
  218. }
  219. super.initState();
  220. _scrollController = new ScrollController();
  221. _scrollController.addListener(() {
  222. // FocusScope.of(context).requestFocus(FocusNode());
  223. // print(_controller.offset);
  224. });
  225. setWebSocket();
  226. _focusNode = FocusNode();
  227. _focusNode.addListener(() {
  228. // _jumpBottom();
  229. Timer(Duration(milliseconds: 300), () => _jumpBottom());
  230. if (!_focusNode.hasFocus && !isDownKeyboard) {
  231. FocusScope.of(context).requestFocus(_focusNode);
  232. }
  233. if (!_focusNode.hasFocus) {
  234. setState(() {
  235. showEmoji = false;
  236. });
  237. }
  238. isDownKeyboard = true;
  239. });
  240. }
  241. @override
  242. void dispose() {
  243. stopPlayer();
  244. _player.closeAudioSession();
  245. _player = null;
  246. stopRecorder();
  247. _recorder.closeAudioSession();
  248. _recorder = null;
  249. if (_audioFilePath != null) {
  250. var outputFile = File(_audioFilePath);
  251. if (outputFile.existsSync()) {
  252. outputFile.delete();
  253. }
  254. }
  255. super.dispose();
  256. }
  257. @override
  258. void didChangeDependencies() {
  259. super.didChangeDependencies();
  260. }
  261. void getStringEvent(roomId) async {
  262. // SharedPreferences prefs = await SharedPreferences.getInstance();
  263. var storyList = [];
  264. var storyListUserOnline = [];
  265. // if (prefs.getStringList(roomId) != null) {
  266. // List<String> hisString = prefs.getStringList(roomId);
  267. // storyList = hisString.map((item) => jsonDecode(item)).toList();
  268. // } else {
  269. // prefs.setStringList(roomId, []);
  270. // }
  271. // if (flustars.SpUtil.getString(roomId + "userOnline") != null &&
  272. // flustars.SpUtil.getString(roomId + "userOnline") != "") {
  273. // // if(prefs.getString(roomId+"userOnline")!=null){
  274. // // String hisStringUserOnline = prefs.getString(roomId+"userOnline");
  275. // // flustars.SpUtil.putString(roomId+"userOnline", res.token);
  276. // String hisStringUserOnline =
  277. // flustars.SpUtil.getString(roomId + "userOnline");
  278. // for (var value in JsonDecoder().convert(hisStringUserOnline)) {
  279. // print(value);
  280. // storyListUserOnline.add(value);
  281. // }
  282. // ;
  283. // // storyListUserOnline = JsonDecoder().convert(hisStringUserOnline);
  284. // // storyListUserOnline = hisStringUserOnline.map((item)=>jsonDecode(item)).toList();
  285. // } else {
  286. // // prefs.setString(roomId+"userOnline",'');
  287. // }
  288. // print(JsonEncoder().convert(storyList));
  289. // print(JsonEncoder().convert(storyListUserOnline));
  290. new Future.delayed(Duration(milliseconds: 1000), () {
  291. setState(() {
  292. if (storyList.length > 0) {
  293. msgList = storyList;
  294. print('storyList msgList ===== $msgList');
  295. }
  296. if (storyListUserOnline.length > 0) {
  297. userList = storyListUserOnline;
  298. }
  299. print(JsonEncoder().convert(msgList));
  300. _jumpBottom();
  301. });
  302. });
  303. }
  304. // @override
  305. // void didChangeDependencies() {
  306. // // Provider.of<WebSocketProvide>(context).closeWebSocket();
  307. // Provider.of<WebSocketProvide>(context,listen: false).closeWebSocket();
  308. // super.didChangeDependencies();
  309. // }
  310. FocusNode blankNode = FocusNode();
  311. ///选择图片
  312. void selectPicker() {
  313. showDialog(
  314. context: context,
  315. builder: (BuildContext context) {
  316. return SimpleDialog(
  317. title: Text("选择方式"),
  318. children: ["拍照", '从手机相册选择'].map((String value) {
  319. print("$value");
  320. return SimpleDialogOption(
  321. child: Text(
  322. "${value}",
  323. style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
  324. ),
  325. onPressed: () {
  326. _getImage(value == '拍照' ? 1 : 0);
  327. Navigator.of(context).pop();
  328. },
  329. );
  330. }).toList());
  331. });
  332. }
  333. var imagesList = <String>[];
  334. var imagesListLast = <String>[];
  335. void _getImage(int key) async {
  336. try {
  337. var _imageFile = await ImagePicker.pickImage(
  338. source: key == 1 ? ImageSource.camera : ImageSource.gallery,
  339. maxWidth: 800,
  340. imageQuality: 95);
  341. print(_imageFile);
  342. print(3333);
  343. if (_imageFile != null) {
  344. // images.add(_imageFile);
  345. upLoadFileOnce(_imageFile.path);
  346. // setState(() {});
  347. }
  348. } catch (e) {
  349. toasts("没有权限,无法打开相册!");
  350. }
  351. }
  352. upLoadFileOnce(path) {
  353. showLoading(context, "正在发送...");
  354. NewApiService().upload(path, onSuccess: (res) {
  355. // imagesUrl.add(res.path);
  356. dismissLoading(context);
  357. String imagesUrl;
  358. imagesUrl = (res.pathUrl);
  359. Provider.of<WebSocketProvide>(context, listen: false).sendMessage(context,
  360. msg: imagesUrl,
  361. id: widget.id,
  362. msgType: 2,
  363. dataTable: dataTable,
  364. toUserId: widget.toUserId);
  365. }, onError: (code, msg) {
  366. dismissLoading(context);
  367. toasts(msg);
  368. });
  369. }
  370. @override
  371. Widget build(BuildContext context) {
  372. msgList = processMsgList(msgList);
  373. imagesList = [];
  374. msgListBack = [];
  375. msgList.forEach((element) {
  376. msgListBack.insert(0, element);
  377. });
  378. // print(JsonEncoder().convert(msgListBack));
  379. print('msgList ======= $msgList');
  380. print('msgList count ======= ${msgList.length}');
  381. try {
  382. if (msgList.length > 0) {
  383. msgList.forEach((element) {
  384. if (element["type"] == 2) {
  385. imagesList.add(element["msg"]);
  386. }
  387. });
  388. }
  389. } catch (e) {
  390. print('msgList e ======= $e');
  391. }
  392. if (isAutoJump) {
  393. Timer(Duration(milliseconds: 500), () {
  394. _jumpBottom();
  395. });
  396. } else {
  397. isAutoJump = true;
  398. }
  399. return
  400. // ChangeNotifierProvider<WebSocketProvide>(
  401. // create: (_) => provider,
  402. // child:
  403. Scaffold(
  404. appBar: MyAppBar(
  405. centerTitle: widget.title,
  406. // actions: [
  407. // new FlatButton(
  408. // onPressed: () {
  409. // Navigator.of(context).push(MaterialPageRoute(
  410. // builder: (context) => JuBaoPage(
  411. // toUserId: widget.jubaoToUserId,
  412. // )));
  413. // },
  414. // child: Text("举报"),
  415. // ),
  416. // ],
  417. // onPressed:(){
  418. // NavigatorUtils.push(context, FriendsRouter.friendsList);
  419. // }
  420. isFun: true,
  421. fun: () {
  422. Navigator.popUntil(context, ModalRoute.withName('/home'));
  423. },
  424. ),
  425. body: SafeArea(
  426. child: Stack(
  427. alignment: Alignment.center,
  428. children: [
  429. GestureDetector(
  430. onTap: () {
  431. // 点击空白页面关闭键盘
  432. // FocusScope.of(context).requestFocus(_focusNode);
  433. FocusScope.of(context).requestFocus(FocusNode());
  434. setState(() {
  435. showEmoji = false;
  436. });
  437. },
  438. child: Container(
  439. padding: EdgeInsets.only(top: 10),
  440. color: Color(0xffF6F6F6),
  441. child: Column(
  442. children: <Widget>[
  443. Expanded(
  444. child: ChatListView(
  445. key: Key('chat_list'),
  446. scrollController: _scrollController,
  447. onRefresh: _loadMore,
  448. pageSize: 10,
  449. itemCount: msgList.length,
  450. hasMore: hasMore,
  451. itemBuilder: (_, index) {
  452. int typeUser;
  453. try {
  454. if ('${msgList[index]["fromUser"]}' ==
  455. flustars.SpUtil.getString(Constant.userId)) {
  456. typeUser = 1;
  457. } else {
  458. typeUser = 0;
  459. }
  460. return ChatContentView(
  461. contentIndex: index,
  462. urlList: imagesList,
  463. type: typeUser,
  464. text: msgList[index]["msg"],
  465. msgType: msgList[index]["type"],
  466. avatar: msgList[index]["avatarUrl"],
  467. username: msgList[index]["name"],
  468. remarks: msgList[index]["remarks"],
  469. isNetwork: msgList[index]["avatarUrl"] != null
  470. ? true
  471. : false,
  472. recordingDuration: msgList[index]["dura"],
  473. onTap: () async {
  474. if (_player.isPlaying) {
  475. await stopPlayer();
  476. }
  477. print('play ===== ${msgList[index]['msg']}');
  478. play(msgList[index]['msg'], () {
  479. print('stopped ===== ');
  480. });
  481. });
  482. } catch (e) {
  483. print('e ==== $e');
  484. }
  485. return null;
  486. },
  487. ),
  488. ),
  489. showEmoji
  490. ? Container(
  491. padding: EdgeInsets.only(
  492. left: ScreenUtil().setWidth(6),
  493. top: ScreenUtil().setWidth(10),
  494. bottom: ScreenUtil().setWidth(10),
  495. right: ScreenUtil().setWidth(6)),
  496. color: Colors.white,
  497. height: ScreenUtil().setWidth(200),
  498. child: GridView.extent(
  499. //横轴的最大长度
  500. maxCrossAxisExtent: 35,
  501. //内边距
  502. padding: EdgeInsets.all(4.0),
  503. //垂直方向的间距
  504. mainAxisSpacing: 4.0,
  505. //水平方向的间距
  506. crossAxisSpacing: 4.0,
  507. children: emojiList.map((element) {
  508. return GestureDetector(
  509. onTap: () {
  510. controller.text += element;
  511. },
  512. child: Text(
  513. element,
  514. style: TextStyle(
  515. color: Color(0xff000000),
  516. fontSize: ScreenUtil().setSp(22)),
  517. textAlign: TextAlign.center,
  518. ),
  519. );
  520. }).toList(),
  521. ))
  522. : Container(
  523. child: null,
  524. ),
  525. Container(
  526. padding: EdgeInsets.only(
  527. top: ScreenUtil().setHeight(2.0),
  528. bottom: ScreenUtil().setHeight(2.0),
  529. left: 0,
  530. right: 0),
  531. color: Color(0xffFBFBFB),
  532. child: Row(
  533. mainAxisAlignment: MainAxisAlignment.end,
  534. crossAxisAlignment: CrossAxisAlignment.center,
  535. children: <Widget>[
  536. IconButton(
  537. icon: voiceModeOn
  538. ? Icon(
  539. Iconfont.b040jianpan,
  540. size: 21,
  541. )
  542. : Icon(
  543. Iconfont.yuyin2,
  544. size: 21,
  545. ),
  546. onPressed: () {
  547. setState(() {
  548. voiceModeOn = !voiceModeOn;
  549. });
  550. }),
  551. if (voiceModeOn)
  552. Expanded(
  553. child: GestureDetector(
  554. onLongPressStart: (details) {
  555. setState(() {
  556. voiceRecordingOn = true;
  557. });
  558. if (_player.isPlaying) {
  559. stopPlayer();
  560. }
  561. record();
  562. startCountingRecording();
  563. },
  564. onLongPressEnd: (details) async {
  565. setState(() {
  566. voiceRecordingOn = false;
  567. });
  568. // print('localPosition ===== ${details.localPosition}');
  569. if (recordingTimer != null) {
  570. recordingTimer.cancel();
  571. }
  572. await stopRecorder();
  573. if (details.localPosition.dy > -50) {
  574. await uploadAudioFile(_audioFilePath);
  575. } else {
  576. toasts("已取消");
  577. }
  578. },
  579. child: Text(
  580. voiceRecordingOn ? '松开结束' : '按住说话',
  581. textAlign: TextAlign.center,
  582. ),
  583. ),
  584. ),
  585. if (!voiceModeOn)
  586. Expanded(
  587. child: Container(
  588. // height: 40,
  589. alignment: Alignment.center,
  590. decoration: BoxDecoration(
  591. borderRadius:
  592. BorderRadius.all(Radius.circular(5.0)),
  593. color: Colors.white),
  594. child: TextFormField(
  595. controller: controller,
  596. // decoration: InputDecoration.collapsed(hintText: null),
  597. decoration: InputDecoration(
  598. border: InputBorder.none,
  599. ),
  600. maxLines: 5,
  601. minLines: 1,
  602. autocorrect: true,
  603. autofocus: true,
  604. focusNode: _focusNode,
  605. textAlign: TextAlign.start,
  606. textInputAction: TextInputAction.send,
  607. style: TextStyle(color: Colors.black),
  608. cursorColor: Colors.green,
  609. onFieldSubmitted: (term) {
  610. isDownKeyboard = false;
  611. // 这里进行事件处理
  612. // print('点击点击点击');
  613. if (controller.text == null ||
  614. controller.text == '') {
  615. toasts("请输入内容");
  616. } else {
  617. _handleSubmitted(context, controller.text);
  618. }
  619. },
  620. onChanged: (text) {
  621. setState(() {
  622. hasText = text.length > 0 ? true : false;
  623. });
  624. // print('change=================== $text');
  625. },
  626. // onSubmitted:_handleSubmitted,
  627. enabled: true, //bu禁用
  628. ),
  629. )),
  630. Container(
  631. width: ScreenUtil().setWidth(30.0),
  632. child: IconButton(
  633. icon: Icon(
  634. Iconfont.biaoqing,
  635. size: 21,
  636. ),
  637. //发送按钮图标
  638. onPressed: () {
  639. print('打开表情面板');
  640. setState(() {
  641. showEmoji = !showEmoji;
  642. voiceModeOn = false;
  643. });
  644. }),
  645. ),
  646. Container(
  647. width: ScreenUtil().setWidth(30.0),
  648. margin: EdgeInsets.only(
  649. right: ScreenUtil().setWidth(10.0)),
  650. child: IconButton(
  651. icon: Icon(Iconfont.tianjia, size: 24),
  652. onPressed: () {
  653. selectPicker();
  654. FocusScope.of(context)
  655. .requestFocus(FocusNode());
  656. }),
  657. ),
  658. Container(
  659. width: ScreenUtil().setWidth(30.0),
  660. margin: EdgeInsets.only(
  661. right: ScreenUtil().setWidth(10.0)),
  662. child: IconButton(
  663. //发送按钮或者+按钮
  664. icon: Icon(Iconfont.fasong,
  665. size: 18, color: Color(0xff3978F7)),
  666. onPressed: () {
  667. // isDownKeyboard = false;
  668. if (controller.text == null ||
  669. controller.text == '') {
  670. toasts("请输入内容");
  671. } else {
  672. _handleSubmitted(context, controller.text);
  673. }
  674. }),
  675. )
  676. ],
  677. ),
  678. )
  679. ],
  680. ),
  681. ),
  682. ),
  683. if (voiceRecordingOn)
  684. Container(
  685. width: 140,
  686. height: 140,
  687. decoration: BoxDecoration(
  688. color: Color.fromRGBO(0, 0, 0, 0.5),
  689. borderRadius: BorderRadius.all(Radius.circular(8)),
  690. ),
  691. child: Column(
  692. mainAxisAlignment: MainAxisAlignment.spaceAround,
  693. children: [
  694. Row(
  695. mainAxisAlignment: MainAxisAlignment.center,
  696. crossAxisAlignment: CrossAxisAlignment.end,
  697. children: [
  698. Icon(
  699. Iconfont.yuyin,
  700. size: 40,
  701. color: Colors.white,
  702. ),
  703. SignalBars(),
  704. ],
  705. ),
  706. Text(
  707. '手指上划,取消发送',
  708. style: TextStyle(color: Colors.white, fontSize: 12),
  709. ),
  710. ],
  711. ),
  712. ),
  713. ],
  714. ),
  715. ),
  716. // ),
  717. );
  718. }
  719. processMsgList(var list) {
  720. return list.map((e) {
  721. if (e is String) {
  722. return JsonDecoder().convert(e);
  723. }
  724. return e;
  725. }).toList();
  726. }
  727. Future _loadMore() async {
  728. isAutoJump = false;
  729. if (hasMore) {
  730. //pge++;
  731. await NewApiService().getMsgHistoryList({
  732. "pageNum": _page,
  733. "pageSize": 10,
  734. // "pageSize":pageSize,
  735. "sessionid": widget.id,
  736. }, onSuccess: (res) {
  737. _page++;
  738. print(JsonEncoder().convert(res));
  739. if (res != null && res.length > 0) {
  740. res = processMsgList(res);
  741. print("初始化数据");
  742. print('res ==== $res');
  743. var ass = [];
  744. ass.addAll(res);
  745. if (assList.length == 0) {
  746. ass.addAll(msgList);
  747. assList = ass;
  748. } else {
  749. ass.addAll(assList);
  750. }
  751. setState(() {
  752. msgList = ass;
  753. print('_loadMore msgList ===== $msgList');
  754. hasMore = true;
  755. // _jumpBottom();
  756. });
  757. } else {
  758. setState(() {
  759. hasMore = false;
  760. });
  761. }
  762. }, onError: (code, msg) {
  763. toasts(msg);
  764. });
  765. } else {
  766. toasts("无更多历史记录!");
  767. }
  768. print("加载更多了");
  769. }
  770. Future<void> openTheRecorder() async {
  771. final permissions = await PermissionHandler()
  772. .requestPermissions([PermissionGroup.microphone]);
  773. if (permissions[PermissionGroup.microphone] != PermissionStatus.granted) {
  774. toasts('需要麦克风权限!');
  775. }
  776. var tempDir = await getTemporaryDirectory();
  777. _audioFilePath = '${tempDir.path}/audio_message.aac';
  778. var outputFile = File(_audioFilePath);
  779. if (outputFile.existsSync()) {
  780. await outputFile.delete();
  781. }
  782. await _recorder.openAudioSession();
  783. _recorderIsInited = true;
  784. }
  785. Future<void> record() async {
  786. assert(_recorderIsInited && _player.isStopped);
  787. await _recorder.startRecorder(
  788. toFile: _audioFilePath,
  789. codec: Codec.aacADTS,
  790. );
  791. setState(() {});
  792. }
  793. Future<void> stopRecorder() async {
  794. await _recorder.stopRecorder();
  795. _playbackReady = true;
  796. }
  797. void play(String fileUrl, Function onFinished) async {
  798. assert(_playerIsInited &&
  799. _playbackReady &&
  800. _recorder.isStopped &&
  801. _player.isStopped);
  802. await _player.startPlayer(
  803. fromURI: fileUrl,
  804. codec: Codec.aacADTS,
  805. whenFinished: () {
  806. setState(() {});
  807. if (onFinished != null) {
  808. onFinished();
  809. }
  810. });
  811. setState(() {});
  812. }
  813. Future<void> stopPlayer() async {
  814. await _player.stopPlayer();
  815. }
  816. uploadAudioFile(String filePath) async {
  817. String uploadName = OssUtil.instance.getImageUploadName(filePath);
  818. await NewApiService.uploadImage(context, uploadName, filePath).then((data) {
  819. if (data.statusCode == 200) {
  820. String str = NewApiUrl.URL_UPLOAD_IMAGE_OSS + "/" + uploadName;
  821. print("str:" + str);
  822. if (str != null) {
  823. Map obj = {"uploadName": uploadName, "success": true};
  824. FastNotification.push("percent", obj);
  825. }
  826. Provider.of<WebSocketProvide>(context, listen: false).sendMessage(
  827. context,
  828. msg: str,
  829. id: widget.id,
  830. msgType: 4,
  831. dataTable: dataTable,
  832. toUserId: widget.toUserId,
  833. recordingDuration: recordingDuration);
  834. } else {
  835. Map obj = {"uploadName": uploadName, "success": false};
  836. FastNotification.push("percent", obj);
  837. }
  838. }).catchError((data) {
  839. Map obj = {"uploadName": uploadName, "success": false};
  840. FastNotification.push("percent", obj);
  841. });
  842. }
  843. void startCountingRecording() {
  844. recordingDuration = 0;
  845. if (recordingTimer != null) {
  846. recordingTimer.cancel();
  847. }
  848. recordingTimer = Timer.periodic(Duration(seconds: 1), (timer) async {
  849. if (recordingDuration >= 30) {
  850. recordingTimer.cancel();
  851. await stopRecorder();
  852. // await uploadAudioFile(_audioFilePath);
  853. setState(() {
  854. voiceRecordingOn = false;
  855. });
  856. } else {
  857. recordingDuration++;
  858. }
  859. });
  860. }
  861. }
  862. class SignalBars extends StatefulWidget {
  863. @override
  864. State<SignalBars> createState() => SignalBarsState();
  865. }
  866. class SignalBarsState extends State<SignalBars> {
  867. double bar1Opactity = 0;
  868. double bar2Opactity = 0;
  869. double bar3Opactity = 0;
  870. int loopCount = 0;
  871. Timer _timer;
  872. @override
  873. void initState() {
  874. _timer = Timer.periodic(Duration(milliseconds: 400), (timer) {
  875. setState(() {
  876. if (loopCount == 1) {
  877. bar1Opactity = 1;
  878. } else if (loopCount == 2) {
  879. bar2Opactity = 1;
  880. } else if (loopCount == 3) {
  881. bar3Opactity = 1;
  882. } else {
  883. bar1Opactity = 0;
  884. bar2Opactity = 0;
  885. bar3Opactity = 0;
  886. loopCount = 0;
  887. }
  888. loopCount++;
  889. });
  890. });
  891. super.initState();
  892. }
  893. @override
  894. void dispose() {
  895. if (_timer != null) {
  896. _timer.cancel();
  897. }
  898. super.dispose();
  899. }
  900. @override
  901. Widget build(BuildContext context) {
  902. return Container(
  903. height: 35,
  904. child: Column(
  905. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  906. crossAxisAlignment: CrossAxisAlignment.start,
  907. children: [
  908. Container(
  909. height: 4,
  910. width: 18,
  911. color: Color.fromRGBO(255, 255, 255, bar3Opactity)),
  912. Container(
  913. height: 4,
  914. width: 14,
  915. color: Color.fromRGBO(255, 255, 255, bar2Opactity)),
  916. Container(
  917. height: 4,
  918. width: 10,
  919. color: Color.fromRGBO(255, 255, 255, bar1Opactity)),
  920. Container(
  921. height: 4, width: 6, color: Color.fromRGBO(255, 255, 255, 1)),
  922. ],
  923. ),
  924. );
  925. }
  926. }