chat_room.dart 33 KB

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