Hibok
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

536 行
18 KiB

  1. import 'dart:io';
  2. import 'package:chat/chat/translate_state.dart';
  3. import 'package:chat/data/UserData.dart';
  4. import 'package:chat/data/chat_data_mgr.dart';
  5. import 'package:chat/data/constants.dart';
  6. import 'package:chat/generated/i18n.dart';
  7. import 'package:chat/models/ChatMsg.dart';
  8. import 'package:chat/models/UserInfo.dart';
  9. import 'package:chat/models/keyboard_provider.dart';
  10. import 'package:chat/models/ref_name_provider.dart';
  11. import 'package:chat/models/voucher_change.dart';
  12. import 'package:chat/proto/all.pbserver.dart';
  13. import 'package:chat/utils/CustomUI.dart';
  14. import 'package:chat/utils/HttpUtil.dart';
  15. import 'package:chat/utils/MessageMgr.dart';
  16. import 'package:chat/utils/TokenMgr.dart';
  17. import 'package:chat/utils/analyze_utils.dart';
  18. import 'package:chat/utils/app_navigator.dart';
  19. import 'package:chat/utils/blacklist_mgr.dart';
  20. import 'package:chat/utils/friend_list_mgr.dart';
  21. import 'package:chat/utils/msgHandler.dart';
  22. import 'package:chat/utils/net_state_widget.dart';
  23. import 'package:chat/utils/sound_util.dart';
  24. import 'package:chat/utils/sp_utils.dart';
  25. import 'package:chat/utils/sql_util.dart';
  26. import 'package:dio/dio.dart';
  27. import 'package:extended_text/extended_text.dart';
  28. import 'package:flutter/cupertino.dart';
  29. import 'package:flutter/material.dart';
  30. import 'package:flutter/services.dart';
  31. import 'package:oktoast/oktoast.dart';
  32. import 'package:provider/provider.dart';
  33. import '../r.dart';
  34. import 'ChatPageItem.dart';
  35. import 'input_bar.dart';
  36. import 'package:chat/utils/PopUpMenu.dart' as myPop;
  37. import 'package:chat/models/money_change.dart';
  38. class ChatPage extends StatefulWidget {
  39. final int friendId;
  40. final int enterType; // 0默认 1图片
  41. final dynamic enterContent;
  42. ChatPage({Key key, this.friendId, this.enterType = 0, this.enterContent})
  43. : super(key: key);
  44. _ChatPageState createState() => _ChatPageState();
  45. }
  46. class _ChatPageState extends State<ChatPage> {
  47. ScrollController _scrollCtrl = ScrollController();
  48. MessageMgr msgMgr = MessageMgr();
  49. UserInfo friendInfo;
  50. List<MsgModel> msgList;
  51. KeyboardIndexProvider _keyboardIndexProvider = KeyboardIndexProvider();
  52. TextEditingController nickNameController = new TextEditingController();
  53. //统计聊天时长
  54. int startTime;
  55. @override
  56. void dispose() {
  57. var endTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
  58. AnalyzeUtils.commitChatDuration(startTime, endTime);
  59. MessageMgr().off('Send CoinBag', _sendCoin);
  60. msgMgr.off('New Chat Message', receiveMsg);
  61. msgMgr.off('Keyboard Hide', dealWithKeyboardHide);
  62. msgMgr.off('Delete Select Message', _deleteItem);
  63. MsgHandler.curActiveSession = 0;
  64. SoundUtils().stop();
  65. nickNameController.dispose();
  66. _scrollCtrl.dispose();
  67. super.dispose();
  68. }
  69. @override
  70. void initState() {
  71. super.initState();
  72. print('init chatpage');
  73. getDefaultSetting();
  74. getUserInfo();
  75. startTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
  76. msgList = ChatDataMgr().getRecord();
  77. msgMgr.on('New Chat Message', receiveMsg);
  78. msgMgr.on('Keyboard Hide', dealWithKeyboardHide);
  79. msgMgr.on('Send CoinBag', _sendCoin);
  80. msgMgr.on('Delete Select Message', _deleteItem);
  81. }
  82. void _sendFile(File file) async {
  83. // File file = await FilePicker.getFile();
  84. int fileSize = file.lengthSync();
  85. print('选择的文件 ${file.path} 大小 $fileSize');
  86. if (fileSize > 33 * 1024 * 1024) {
  87. showToast('文件大于33M');
  88. return;
  89. }
  90. var fileName = file.path.split('/').last;
  91. print('fileName $fileName');
  92. var ext = '';
  93. var extList = fileName.split('.');
  94. if (extList.length > 1) {
  95. ext = extList.last;
  96. }
  97. print('ext $ext');
  98. var fileMsg = FileChat.create();
  99. fileMsg.type = ext;
  100. fileMsg.size = fileSize;
  101. fileMsg.name = fileName;
  102. var msg = MsgHandler.createSendMsg(
  103. ChatType.FileChatType, fileMsg.writeToBuffer(),
  104. friendId: widget.friendId,
  105. localFile: file.path,
  106. channelType: ChatChannelType.Session);
  107. sendMsg(msg);
  108. }
  109. _sendCoin(args) async {
  110. var res = await _checkCoinBeforeSend(args['amount']);
  111. if (res != null) {
  112. args['redNo'] = res['redNo'];
  113. args['friendId'] = widget.friendId;
  114. var msg = MsgHandler.createCoinBagMsg(args);
  115. sendMsg(msg);
  116. }
  117. }
  118. //校验
  119. _checkCoinBeforeSend(int amount) async {
  120. Map data = {
  121. "userId": UserData().basicInfo.userId,
  122. "rUserId": friendInfo.userId,
  123. "price": amount,
  124. };
  125. data['sign'] = TokenMgr().getSign(data);
  126. Response res = await HttpUtil().post('red/packet/send', data: data);
  127. if (res == null) {
  128. return null;
  129. }
  130. Map resData = res.data;
  131. if (resData['code'] == 0) {
  132. print(resData['data']);
  133. return resData['data'];
  134. }
  135. return null;
  136. }
  137. void getDefaultSetting() async {
  138. bool soundPlayMode =
  139. (await SPUtils.getBool(Constants.SOUND_PLAY_MODE)) ?? false;
  140. _keyboardIndexProvider.init(soundPlayMode);
  141. }
  142. dealWithKeyboardHide(args) {
  143. if (_keyboardIndexProvider.curKeyboardIndex == 0) {
  144. hideKeyBoard();
  145. }
  146. }
  147. getUserInfo() async {
  148. //先从本地获取用户信息
  149. friendInfo = await HttpUtil().getFriendInfo(widget.friendId, true);
  150. //如果是新的聊天,向服务器发送创建会话消息
  151. if (msgList.length == 0) {
  152. MsgHandler.getActiveSesstion(
  153. [friendInfo.userId, UserData().basicInfo.userId]);
  154. }
  155. if (mounted) {
  156. setState(() {});
  157. }
  158. WidgetsBinding.instance.addPostFrameCallback((_) {
  159. if (widget.enterType == 1) {
  160. print('接收到的:${widget.enterContent}');
  161. File file = new File(widget.enterContent);
  162. if(file.existsSync()){
  163. print('接收到的文件--存在');
  164. }else{
  165. print('接收到的文件--不存在');
  166. }
  167. _sendFile(File(widget.enterContent));
  168. }
  169. });
  170. }
  171. @override
  172. Widget build(BuildContext context) {
  173. print('build chatpage');
  174. if (friendInfo == null) {
  175. return Scaffold(
  176. backgroundColor: const Color(0xFFE2E9F1),
  177. body: SafeArea(
  178. child: Center(
  179. child: CircularProgressIndicator(),
  180. ),
  181. ),
  182. );
  183. }
  184. List<Widget> actions = [];
  185. int voucher = Provider.of<VoucherChangeProvider>(context).voucher;
  186. actions.add(Row(
  187. children: <Widget>[
  188. CustomUI.buildImageLabel("assets/images/voucher.png", voucher,
  189. imgOpc: 0.5, imgHeight: 13),
  190. CustomUI.buildImageLabel(
  191. R.assetsImagesCoin, Provider.of<MoneyChangeProvider>(context).money,
  192. isLeft: false)
  193. ],
  194. ));
  195. actions.add(TranslateSateWidget(friendId: friendInfo.userId));
  196. actions.add(Container(
  197. margin: EdgeInsets.only(top: 1),
  198. child: myPop.PopupMenuButton(
  199. icon: Icon(
  200. Icons.more_horiz,
  201. size: 24,
  202. ),
  203. offset: Offset(0, 100),
  204. onSelected: (int index) {
  205. if (index == 2) {
  206. AppNavigator.pushInformUserPage(
  207. context, friendInfo.sex == 1, friendInfo.userId);
  208. } else if (index == 1) {
  209. nickNameController.text = Provider.of<RefNameProvider>(context)
  210. .getRefName(friendInfo.userId, friendInfo.nickName);
  211. var confirm = CustomUI.buildConfirmBotton(
  212. I18n.of(context).determine, () async {
  213. nickNameController.text = nickNameController.text.trim();
  214. if (nickNameController.text == null ||
  215. nickNameController.text.length > 25) {
  216. showToast(I18n.of(context).only1_8);
  217. return;
  218. }
  219. Provider.of<RefNameProvider>(context).changeRefName(
  220. friendInfo.userId, nickNameController.text, () {
  221. Navigator.of(context).pop();
  222. });
  223. });
  224. var tip = Column(
  225. children: <Widget>[
  226. Container(
  227. margin: EdgeInsets.only(top: 20),
  228. child: Text(
  229. I18n.of(context).setRemark,
  230. textScaleFactor: 1.0,
  231. style: TextStyle(
  232. color: Constants.BlackTextColor, fontSize: 16),
  233. ),
  234. ),
  235. Container(
  236. margin: EdgeInsets.only(top: 23, bottom: 25),
  237. decoration: BoxDecoration(
  238. color: Colors.grey[200],
  239. borderRadius: BorderRadius.all(Radius.circular(8))),
  240. child: TextField(
  241. keyboardAppearance: Brightness.light,
  242. controller: nickNameController,
  243. textAlign: TextAlign.center,
  244. textInputAction: TextInputAction.search,
  245. style: TextStyle(
  246. textBaseline: TextBaseline.alphabetic,
  247. fontSize: 14),
  248. decoration: InputDecoration(
  249. hintText: friendInfo.nickName,
  250. hintStyle: TextStyle(fontSize: 12),
  251. filled: true,
  252. contentPadding: EdgeInsets.only(top: 10, bottom: 10),
  253. fillColor: Colors.transparent,
  254. border: InputBorder.none,
  255. ),
  256. maxLines: 1,
  257. inputFormatters: [LengthLimitingTextInputFormatter(15)],
  258. ),
  259. )
  260. ],
  261. );
  262. var content = CustomUI.buildConfirmContent(tip, confirm);
  263. CustomUI.buildTip(context, '', content);
  264. }
  265. },
  266. itemBuilder: (BuildContext context) {
  267. return <myPop.PopupMenuItem<int>>[
  268. myPop.PopupMenuItem(
  269. child: Container(
  270. alignment: Alignment.center,
  271. color: Colors.white,
  272. padding: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
  273. child: Text(I18n.of(context).anonymous_report,
  274. textScaleFactor: 1.0,
  275. maxLines: 1,
  276. style: TextStyle(
  277. color: Color(AppColors.AppBarColor), fontSize: 12)),
  278. ),
  279. value: 2,
  280. ),
  281. myPop.PopupMenuItem(
  282. child: Container(
  283. decoration: BoxDecoration(
  284. border: Border(
  285. top: BorderSide(width: 1, color: Colors.grey[300])),
  286. color: Colors.white,
  287. ),
  288. alignment: Alignment.center,
  289. padding: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
  290. child: Text(I18n.of(context).Remark,
  291. textScaleFactor: 1.0,
  292. maxLines: 1,
  293. style: TextStyle(
  294. color: Color(AppColors.AppBarColor), fontSize: 12)),
  295. ),
  296. value: 1,
  297. ),
  298. ];
  299. })));
  300. return MultiProvider(
  301. providers: [
  302. ChangeNotifierProvider(create: (_) => _keyboardIndexProvider),
  303. Provider<bool>.value(value: false),
  304. Provider<int>.value(value: widget.friendId),
  305. ],
  306. child: GestureDetector(
  307. onTap: hideKeyBoard,
  308. child: ExtendedTextSelectionPointerHandler(
  309. ///选择文字,消除弹窗
  310. builder: (states) {
  311. return Listener(
  312. child: Scaffold(
  313. resizeToAvoidBottomInset: false,
  314. backgroundColor: const Color(0xFFE2E9F1),
  315. // appBar: PreferredSize(
  316. // preferredSize: Size.fromHeight(58),
  317. // child: Container(
  318. // color: AppBarTheme.of(context).color,
  319. // child: SafeArea(
  320. // child: Container(
  321. // height: 56,
  322. // child: Row(children: <Widget>[
  323. // SizedBox(width: 10),
  324. // CustomUI.buildCustomLeading(context),
  325. // Text(
  326. // Provider.of<RefNameProvider>(context)
  327. // .getRefName(friendInfo.userId,
  328. // friendInfo.nickName),
  329. // textScaleFactor: 1.0,
  330. // style: TextStyle(
  331. // textBaseline: TextBaseline.ideographic,
  332. // color: Constants.BlackTextColor,
  333. // fontSize: 16.47),
  334. // ),
  335. // Expanded(
  336. // child: Row(
  337. // mainAxisAlignment:
  338. // MainAxisAlignment.end,
  339. // children: actions))
  340. // ]),
  341. // )))),
  342. appBar: AppBar(
  343. title: Text(
  344. '${Provider.of<RefNameProvider>(context).getRefName(friendInfo.userId, friendInfo.nickName)}',
  345. textScaleFactor: 1.0,
  346. style: TextStyle(
  347. color: Constants.BlackTextColor,
  348. fontSize: 16.47),
  349. ),
  350. leading: CustomUI.buildCustomLeading(context),
  351. titleSpacing: -10,
  352. centerTitle: false,
  353. elevation: 1,
  354. actions: actions),
  355. body: SafeArea(
  356. child: Column(
  357. children: <Widget>[
  358. NetStateWidget(),
  359. Expanded(child: _buildMessageList()),
  360. InputBar(sendMsg: sendMsg),
  361. ],
  362. ))),
  363. behavior: HitTestBehavior.translucent,
  364. onPointerDown: (value) {
  365. for (var state in states) {
  366. if (!state.containsPosition(value.position)) {
  367. //clear other selection
  368. state.clearSelection();
  369. }
  370. }
  371. },
  372. onPointerMove: (value) {
  373. //clear other selection
  374. for (var state in states) {
  375. if (!state.containsPosition(value.position)) {
  376. //clear other selection
  377. state.clearSelection();
  378. }
  379. }
  380. },
  381. );
  382. },
  383. )));
  384. }
  385. Widget _buildMessageList() {
  386. return Container(
  387. alignment: Alignment.topCenter,
  388. child: msgList.length == 0
  389. ? Padding(
  390. padding: EdgeInsets.all(8),
  391. child: Text(
  392. I18n.of(context).chat_tips,
  393. textAlign: TextAlign.center,
  394. textScaleFactor: 1.0,
  395. style: TextStyle(color: Colors.grey, fontSize: 12),
  396. ))
  397. : NotificationListener(
  398. child: Scrollbar(
  399. child: ListView.builder(
  400. reverse: true,
  401. shrinkWrap: true,
  402. itemCount: msgList.length,
  403. controller: _scrollCtrl,
  404. padding: EdgeInsets.all(8.0),
  405. itemBuilder: _buildItem,
  406. )),
  407. onNotification: (notification) {
  408. if (notification is ScrollNotification) {
  409. // var offset = notification.metrics.pixels;
  410. // print('滚动事件 offset $offset');
  411. }
  412. return true;
  413. },
  414. ),
  415. );
  416. }
  417. hideKeyBoard() {
  418. _keyboardIndexProvider.changeSelectIndex(-1);
  419. }
  420. readOnly() {
  421. _keyboardIndexProvider.changeReadOnlyKey(true);
  422. }
  423. sendMsg(MsgModel msg) {
  424. print('对方是否拉黑你 ${friendInfo.isBlackened}');
  425. if (BlacklistMgr.isBlaklistMe(friendInfo.userId)) {
  426. showToast(I18n.of(context).you_are_blaklisted);
  427. return;
  428. }
  429. if (BlacklistMgr.isInMyblaklist(friendInfo.userId)) {
  430. showToast(I18n.of(context).reject_message);
  431. return;
  432. }
  433. if (!friendInfo.isCanStrangerNews &&
  434. !FriendListMgr().isMyFriend(friendInfo.userId)) {
  435. showToast(I18n.of(context).stranger_close_tips);
  436. return;
  437. }
  438. MsgHandler.insertMsgToDB(msg);
  439. MsgHandler.sendChatMsg(msg);
  440. if (mounted) {
  441. setState(() {});
  442. if (_scrollCtrl.hasClients) {
  443. _scrollCtrl.animateTo(0,
  444. duration: new Duration(milliseconds: 500), curve: Curves.ease);
  445. }
  446. }
  447. }
  448. void receiveMsg(args) {
  449. if (mounted) {
  450. setState(() {});
  451. if (_scrollCtrl.hasClients) {
  452. _scrollCtrl.animateTo(0,
  453. duration: new Duration(milliseconds: 500), curve: Curves.ease);
  454. }
  455. }
  456. }
  457. _deleteItem(msg) {
  458. MessageMgr().emit('Cancel Request', msg);
  459. print('#### 开始删除--');
  460. msgList.remove(msg);
  461. setState(() {});
  462. SqlUtil().deleteSigleRecordWith(msg.sessionId, msg.time);
  463. }
  464. Widget _buildItem(BuildContext context, int index) {
  465. var lastMsgTime;
  466. if (index < msgList.length - 1) {
  467. lastMsgTime = msgList[index + 1].time;
  468. }
  469. MsgModel msg = msgList[index];
  470. return ChatPageItem(
  471. key: Key(msg.time.toString()),
  472. msg: msg,
  473. hideKeyboard: readOnly,
  474. friendInfo: friendInfo,
  475. lastMsgTime: lastMsgTime);
  476. }
  477. }