chat_room.dart 33 KB

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