Hibok
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

510 lines
17 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. _sendFile(File(widget.enterContent));
  162. } else if (widget.enterType == 2) {
  163. //转发消息
  164. MsgModel originMsg = widget.enterContent;
  165. MsgModel msg = MsgHandler.createSendMsg(
  166. ChatType.valueOf(originMsg.msgType), originMsg.msgContent);
  167. msg.extraInfo = originMsg.extraInfo;
  168. msg.extraFile = originMsg.extraFile;
  169. msg.localFile = originMsg.localFile;
  170. msg.friendId = widget.friendId;
  171. if (msg.localFile != null) {
  172. msg.state = MsgState.Uploaded;
  173. }
  174. sendMsg(msg);
  175. }
  176. });
  177. }
  178. @override
  179. Widget build(BuildContext context) {
  180. print('build chatpage');
  181. if (friendInfo == null) {
  182. return Scaffold(
  183. backgroundColor: const Color(0xFFE2E9F1),
  184. body: SafeArea(
  185. child: Center(
  186. child: CircularProgressIndicator(),
  187. ),
  188. ),
  189. );
  190. }
  191. List<Widget> actions = [];
  192. int voucher = Provider.of<VoucherChangeProvider>(context).voucher;
  193. actions.add(Row(
  194. children: <Widget>[
  195. CustomUI.buildImageLabel("assets/images/voucher.png", voucher,
  196. imgOpc: 0.5, imgHeight: 13),
  197. CustomUI.buildImageLabel(
  198. R.assetsImagesCoin, Provider.of<MoneyChangeProvider>(context).money,
  199. isLeft: false)
  200. ],
  201. ));
  202. actions.add(TranslateSateWidget(friendId: friendInfo.userId));
  203. actions.add(Container(
  204. margin: EdgeInsets.only(top: 1),
  205. child: myPop.PopupMenuButton(
  206. icon: Icon(
  207. Icons.more_horiz,
  208. size: 24,
  209. ),
  210. offset: Offset(0, 100),
  211. onSelected: (int index) {
  212. if (index == 2) {
  213. AppNavigator.pushInformUserPage(
  214. context, friendInfo.sex == 1, friendInfo.userId);
  215. } else if (index == 1) {
  216. nickNameController.text = Provider.of<RefNameProvider>(context)
  217. .getRefName(friendInfo.userId, friendInfo.nickName);
  218. var confirm = CustomUI.buildConfirmBotton(
  219. I18n.of(context).determine, () async {
  220. nickNameController.text = nickNameController.text.trim();
  221. if (nickNameController.text == null ||
  222. nickNameController.text.length > 25) {
  223. showToast(I18n.of(context).only1_8);
  224. return;
  225. }
  226. Provider.of<RefNameProvider>(context).changeRefName(
  227. friendInfo.userId, nickNameController.text, () {
  228. Navigator.of(context).pop();
  229. });
  230. });
  231. var tip = Column(
  232. children: <Widget>[
  233. Container(
  234. margin: EdgeInsets.only(top: 20),
  235. child: Text(
  236. I18n.of(context).setRemark,
  237. textScaleFactor: 1.0,
  238. style: TextStyle(
  239. color: Constants.BlackTextColor, fontSize: 16),
  240. ),
  241. ),
  242. Container(
  243. margin: EdgeInsets.only(top: 23, bottom: 25),
  244. decoration: BoxDecoration(
  245. color: Colors.grey[200],
  246. borderRadius: BorderRadius.all(Radius.circular(8))),
  247. child: TextField(
  248. keyboardAppearance: Brightness.light,
  249. controller: nickNameController,
  250. textAlign: TextAlign.center,
  251. textInputAction: TextInputAction.search,
  252. style: TextStyle(
  253. textBaseline: TextBaseline.alphabetic,
  254. fontSize: 14),
  255. decoration: InputDecoration(
  256. hintText: friendInfo.nickName,
  257. hintStyle: TextStyle(fontSize: 12),
  258. filled: true,
  259. contentPadding: EdgeInsets.only(top: 10, bottom: 10),
  260. fillColor: Colors.transparent,
  261. border: InputBorder.none,
  262. ),
  263. maxLines: 1,
  264. inputFormatters: [LengthLimitingTextInputFormatter(15)],
  265. ),
  266. )
  267. ],
  268. );
  269. var content = CustomUI.buildConfirmContent(tip, confirm);
  270. CustomUI.buildTip(context, '', content);
  271. }
  272. },
  273. itemBuilder: (BuildContext context) {
  274. return <myPop.PopupMenuItem<int>>[
  275. myPop.PopupMenuItem(
  276. child: Container(
  277. alignment: Alignment.center,
  278. color: Colors.white,
  279. padding: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
  280. child: Text(I18n.of(context).anonymous_report,
  281. textScaleFactor: 1.0,
  282. maxLines: 1,
  283. style: TextStyle(
  284. color: Color(AppColors.AppBarColor), fontSize: 12)),
  285. ),
  286. value: 2,
  287. ),
  288. myPop.PopupMenuItem(
  289. child: Container(
  290. decoration: BoxDecoration(
  291. border: Border(
  292. top: BorderSide(width: 1, color: Colors.grey[300])),
  293. color: Colors.white,
  294. ),
  295. alignment: Alignment.center,
  296. padding: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
  297. child: Text(I18n.of(context).Remark,
  298. textScaleFactor: 1.0,
  299. maxLines: 1,
  300. style: TextStyle(
  301. color: Color(AppColors.AppBarColor), fontSize: 12)),
  302. ),
  303. value: 1,
  304. ),
  305. ];
  306. })));
  307. return MultiProvider(
  308. providers: [
  309. ChangeNotifierProvider(create: (_) => _keyboardIndexProvider),
  310. Provider<bool>.value(value: false),
  311. Provider<int>.value(value: widget.friendId),
  312. ],
  313. child: GestureDetector(
  314. onTap: hideKeyBoard,
  315. child: ExtendedTextSelectionPointerHandler(
  316. ///选择文字,消除弹窗
  317. builder: (states) {
  318. return Listener(
  319. child: Scaffold(
  320. resizeToAvoidBottomInset: false,
  321. backgroundColor: const Color(0xFFE2E9F1),
  322. appBar: AppBar(
  323. title: Text(
  324. '${Provider.of<RefNameProvider>(context).getRefName(friendInfo.userId, friendInfo.nickName)}',
  325. textScaleFactor: 1.0,
  326. style: TextStyle(
  327. color: Constants.BlackTextColor,
  328. fontSize: 16.47),
  329. ),
  330. leading: CustomUI.buildCustomLeading(context),
  331. titleSpacing: -10,
  332. centerTitle: false,
  333. elevation: 1,
  334. actions: actions),
  335. body: SafeArea(
  336. child: Column(
  337. children: <Widget>[
  338. NetStateWidget(),
  339. Expanded(child: _buildMessageList()),
  340. InputBar(sendMsg: sendMsg),
  341. ],
  342. ))),
  343. behavior: HitTestBehavior.translucent,
  344. onPointerDown: (value) {
  345. for (var state in states) {
  346. if (!state.containsPosition(value.position)) {
  347. //clear other selection
  348. state.clearSelection();
  349. }
  350. }
  351. },
  352. onPointerMove: (value) {
  353. //clear other selection
  354. for (var state in states) {
  355. if (!state.containsPosition(value.position)) {
  356. //clear other selection
  357. state.clearSelection();
  358. }
  359. }
  360. },
  361. );
  362. },
  363. )));
  364. }
  365. Widget _buildMessageList() {
  366. return Container(
  367. alignment: Alignment.topCenter,
  368. child: msgList.length == 0
  369. ? Padding(
  370. padding: EdgeInsets.all(8),
  371. child: Text(
  372. I18n.of(context).chat_tips,
  373. textAlign: TextAlign.center,
  374. textScaleFactor: 1.0,
  375. style: TextStyle(color: Colors.grey, fontSize: 12),
  376. ))
  377. : NotificationListener(
  378. child: Scrollbar(
  379. child: ListView.builder(
  380. reverse: true,
  381. shrinkWrap: true,
  382. itemCount: msgList.length,
  383. controller: _scrollCtrl,
  384. padding: EdgeInsets.all(8.0),
  385. itemBuilder: _buildItem,
  386. )),
  387. onNotification: (notification) {
  388. if (notification is ScrollNotification) {
  389. // var offset = notification.metrics.pixels;
  390. // print('滚动事件 offset $offset');
  391. }
  392. return true;
  393. },
  394. ),
  395. );
  396. }
  397. hideKeyBoard() {
  398. _keyboardIndexProvider.changeSelectIndex(-1);
  399. }
  400. readOnly() {
  401. _keyboardIndexProvider.changeReadOnlyKey(true);
  402. }
  403. sendMsg(MsgModel msg) {
  404. print('对方是否拉黑你 ${friendInfo.isBlackened}');
  405. if (BlacklistMgr.isBlack(friendInfo.userId)) {
  406. return;
  407. }
  408. if (!friendInfo.isCanStrangerNews &&
  409. !FriendListMgr().isMyFriend(friendInfo.userId)) {
  410. showToast(I18n.of(context).stranger_close_tips);
  411. return;
  412. }
  413. MsgHandler.insertMsgToDB(msg);
  414. MsgHandler.sendChatMsg(msg);
  415. if (mounted) {
  416. setState(() {});
  417. if (_scrollCtrl.hasClients) {
  418. _scrollCtrl.animateTo(0,
  419. duration: new Duration(milliseconds: 500), curve: Curves.ease);
  420. }
  421. }
  422. }
  423. void receiveMsg(args) {
  424. if (mounted) {
  425. setState(() {});
  426. if (_scrollCtrl.hasClients) {
  427. _scrollCtrl.animateTo(0,
  428. duration: new Duration(milliseconds: 500), curve: Curves.ease);
  429. }
  430. }
  431. }
  432. _deleteItem(msg) {
  433. MessageMgr().emit('Cancel Request', msg);
  434. print('#### 开始删除--');
  435. msgList.remove(msg);
  436. setState(() {});
  437. SqlUtil().deleteSigleRecordWith(msg.sessionId, msg.time);
  438. }
  439. Widget _buildItem(BuildContext context, int index) {
  440. var lastMsgTime;
  441. if (index < msgList.length - 1) {
  442. lastMsgTime = msgList[index + 1].time;
  443. }
  444. MsgModel msg = msgList[index];
  445. return ChatPageItem(
  446. key: Key(msg.time.toString()),
  447. msg: msg,
  448. hideKeyboard: readOnly,
  449. friendInfo: friendInfo,
  450. lastMsgTime: lastMsgTime);
  451. }
  452. }