chat_room.dart 33 KB

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