Hibok
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

534 line
17 KiB

  1. import 'dart:io';
  2. import 'package:cached_network_image/cached_network_image.dart';
  3. import 'package:chat/home/global_search.dart';
  4. import 'package:chat/home/new_friends.dart';
  5. import 'package:chat/home/search_newfriend.dart';
  6. import 'package:chat/models/friends_info.dart';
  7. import 'package:flutter/material.dart';
  8. import 'package:chat/data/UserData.dart';
  9. import 'package:chat/data/constants.dart';
  10. import 'package:chat/generated/i18n.dart';
  11. import 'package:chat/models/ref_name_provider.dart';
  12. import 'package:chat/utils/CustomUI.dart';
  13. import 'package:chat/utils/HttpUtil.dart';
  14. import 'package:chat/utils/LoadingDialog.dart';
  15. import 'package:chat/utils/MessageMgr.dart';
  16. import 'package:chat/utils/TokenMgr.dart';
  17. import 'package:chat/utils/conversation_table.dart';
  18. import 'package:chat/utils/friend_list_mgr.dart';
  19. import 'package:chat/utils/screen.dart';
  20. import 'package:dio/dio.dart';
  21. import 'package:oktoast/oktoast.dart';
  22. import 'package:permission_handler/permission_handler.dart';
  23. import 'package:provider/provider.dart';
  24. import 'package:shared_preferences/shared_preferences.dart';
  25. import 'address_book.dart';
  26. List showUserList = [];
  27. bool isHaveNewFriends() {
  28. return showUserList.length > 0;
  29. }
  30. class FriendPage extends StatefulWidget {
  31. @override
  32. _FriendPageState createState() => _FriendPageState();
  33. }
  34. class _FriendPageState extends State<FriendPage> {
  35. String _currentLetter = '';
  36. ScrollController _scrollController;
  37. List<FriendModel> friendList = [];
  38. List<Widget> _functionButtons = [];
  39. final Map _letterPosMap = {INDEX_BAR_WORDS[0]: 0.0};
  40. Set<String> userIdSet = new Set();
  41. //var _contactsFuture;
  42. @override
  43. void initState() {
  44. super.initState();
  45. print('FriendPage initState');
  46. initNewFriendsList([]);
  47. getFriendList();
  48. getNewFriendList(null);
  49. _scrollController = new ScrollController();
  50. MessageMgr().on('Add friend', msgAddfrend);
  51. MessageMgr().on('Delete friend', msgDeletefrend);
  52. MessageMgr().on('do_friend_apply', messageApply);
  53. MessageMgr().on('goto_new_friends', messageNew);
  54. }
  55. messageApply(data) {
  56. getNewFriendList(data);
  57. }
  58. messageNew(data) {
  59. gotoNewFriendsPage();
  60. }
  61. gotoNewFriendsPage() async {
  62. Navigator.of(context).push(
  63. new MaterialPageRoute(
  64. builder: (context) {
  65. return NewFriendsPage();
  66. },
  67. ),
  68. );
  69. SharedPreferences prefs = await SharedPreferences.getInstance();
  70. prefs.setStringList(Constants.NewFriendsList, userIdSet.toList());
  71. showUserList = [];
  72. initNewFriendsList([]);
  73. setState(() {});
  74. }
  75. Widget _buildNewFriends(list) {
  76. if (list.length == 0) {
  77. return FriendsInfo(
  78. avatar: '',
  79. userId: 0,
  80. iconCode: 0xe66c,
  81. title: I18n.of(LoadingManage.context).new_friends,
  82. gradient: LinearGradient(
  83. begin: Alignment.topCenter,
  84. end: Alignment.bottomCenter,
  85. colors: <Color>[
  86. const Color(0xFF58B7F5),
  87. const Color(0xFF1874C9),
  88. ]),
  89. onPressed: gotoNewFriendsPage);
  90. } else {
  91. return _buildFriendsItem(list);
  92. }
  93. }
  94. Widget _buildAvatar(url) {
  95. return ClipRRect(
  96. borderRadius: BorderRadius.circular(6),
  97. child: CachedNetworkImage(
  98. imageUrl: url,
  99. width: Constants.ContactAvatarSize,
  100. height: Constants.ContactAvatarSize,
  101. ));
  102. }
  103. Widget _buildFriendsItem(list) {
  104. Widget unreadRedDot = Container(
  105. margin: EdgeInsets.only(right: 15),
  106. width: Constants.UnReadMsgNotifyDotSize,
  107. height: Constants.UnReadMsgNotifyDotSize,
  108. alignment: Alignment.center,
  109. decoration: BoxDecoration(
  110. borderRadius:
  111. BorderRadius.circular(Constants.UnReadMsgNotifyDotSize / 2.0),
  112. color: Color(0xFFFF5454),
  113. ),
  114. child: Text(list.length > 99 ? "99+" : list.length.toString(),
  115. textScaleFactor: 1.0, style: AppStyles.UnreadMsgCountDotStyle),
  116. );
  117. var left;
  118. if (list.length == 1) {
  119. left = Row(
  120. children: <Widget>[
  121. _buildAvatar(list[0]['ApplyUrl']),
  122. SizedBox(width: 10.0),
  123. Expanded(
  124. child: Column(
  125. crossAxisAlignment: CrossAxisAlignment.start,
  126. children: <Widget>[
  127. Text(list[0]['ApplyName'], style: TextStyle(fontSize: 12.66)),
  128. SizedBox(
  129. height: 5,
  130. ),
  131. Text(I18n.of(context).apply_fro_friends,
  132. style:
  133. TextStyle(fontSize: 10, color: const Color(0xFF6A6A6A))),
  134. ],
  135. )),
  136. unreadRedDot,
  137. ],
  138. );
  139. } else {
  140. left = Row(
  141. children: <Widget>[
  142. Expanded(
  143. child: Container(
  144. height: Constants.ContactAvatarSize,
  145. constraints: BoxConstraints(maxWidth: Screen.width - 100),
  146. child: ListView(
  147. scrollDirection: Axis.horizontal,
  148. children: list
  149. .map<Widget>((f) => Container(
  150. child: _buildAvatar(f['ApplyUrl']),
  151. margin: EdgeInsets.only(right: 5),
  152. ))
  153. .toList(),
  154. ))),
  155. unreadRedDot,
  156. ],
  157. );
  158. }
  159. return InkWell(
  160. onTap: gotoNewFriendsPage,
  161. child: Container(
  162. padding: const EdgeInsets.symmetric(
  163. vertical: MARGIN_VERTICAL, horizontal: 16.0),
  164. decoration: BoxDecoration(color: Colors.white),
  165. child: left,
  166. ));
  167. }
  168. showPermission() async {
  169. if (await CustomUI.showPermissionSetting(
  170. LoadingManage.context,
  171. PermissionGroup.contacts,
  172. I18n.of(LoadingManage.context).contact_permission)) {
  173. MessageMgr().emit('PostContact');
  174. Navigator.push(LoadingManage.context,
  175. MaterialPageRoute<void>(builder: (BuildContext context) {
  176. return ContactsPage();
  177. }));
  178. }
  179. }
  180. getFriendList() async {
  181. friendList = await FriendListMgr().getFriendList();
  182. if (mounted) {
  183. setState(() {});
  184. }
  185. }
  186. getNewFriendList(userdata) async {
  187. Map data = {
  188. "userId": UserData().basicInfo.userId,
  189. "type": 1,
  190. };
  191. data['sign'] = TokenMgr().getSign(data);
  192. Response res =
  193. await HttpUtil().post('friendship/newFriends/record', data: data);
  194. var resData = res.data;
  195. if (resData['code'] == 0) {
  196. if (resData['data'] != null) {
  197. SharedPreferences prefs = await SharedPreferences.getInstance();
  198. List<String> list = prefs.getStringList(Constants.NewFriendsList);
  199. if (list != null) {
  200. userIdSet = new Set.from(list);
  201. } else {
  202. userIdSet = new Set();
  203. }
  204. if (userdata != null && userIdSet.contains(userdata['userId'])) {
  205. userIdSet.remove(userdata['userId']);
  206. }
  207. showUserList = [];
  208. for (int i = 0; i < resData['data'].length; i++) {
  209. if (!userIdSet
  210. .contains(resData['data'][i]['ApplyUserId'].toString())) {
  211. showUserList.add(resData['data'][i]);
  212. userIdSet.add(resData['data'][i]['ApplyUserId'].toString());
  213. }
  214. }
  215. initNewFriendsList(showUserList);
  216. if (mounted) {
  217. setState(() {});
  218. }
  219. }
  220. } else {
  221. showToast(resData['msg']);
  222. }
  223. }
  224. initNewFriendsList(showUserList) {
  225. _functionButtons = [
  226. FriendsInfo(
  227. avatar: '',
  228. userId: 0,
  229. iconCode: 0xe659,
  230. title: I18n.of(LoadingManage.context).scan,
  231. gradient: LinearGradient(
  232. begin: Alignment.topCenter,
  233. end: Alignment.bottomCenter,
  234. colors: <Color>[
  235. const Color(0xFF6171F1),
  236. const Color(0xFF3441C1),
  237. ]),
  238. onPressed: () {
  239. CustomUI().goScanPage(LoadingManage.context);
  240. }),
  241. FriendsInfo(
  242. avatar: '',
  243. userId: 0,
  244. iconCode: 0xe67a,
  245. isShowDivder: true,
  246. title: I18n.of(LoadingManage.context).contact_add,
  247. gradient: LinearGradient(
  248. begin: Alignment.topCenter,
  249. end: Alignment.bottomCenter,
  250. colors: <Color>[
  251. const Color(0xFF1AC59E),
  252. const Color(0xFF088E76),
  253. ]),
  254. onPressed: () async {
  255. final PermissionStatus addStatus = await PermissionHandler()
  256. .checkPermissionStatus(PermissionGroup.contacts);
  257. if ((addStatus == PermissionStatus.unknown ||
  258. addStatus == PermissionStatus.denied) &&
  259. Platform.isIOS) {
  260. CustomUI.buildContacts(context, I18n.of(context).privacyAgreement,
  261. I18n.of(context).determine, () {
  262. Navigator.of(context).pop();
  263. showPermission();
  264. }, title: I18n.of(context).tip);
  265. } else {
  266. showPermission();
  267. }
  268. }),
  269. FriendsInfo(
  270. avatar: '',
  271. userId: 0,
  272. iconCode: 0xe662,
  273. isShowDivder: true,
  274. title: I18n.of(LoadingManage.context).add_friends,
  275. gradient: LinearGradient(
  276. begin: Alignment.topCenter,
  277. end: Alignment.bottomCenter,
  278. colors: <Color>[
  279. const Color(0xFF9E58E5),
  280. const Color(0xFF6118A6),
  281. ]),
  282. onPressed: () async {
  283. Navigator.push(LoadingManage.context,
  284. MaterialPageRoute<void>(builder: (BuildContext context) {
  285. return SearchNewFriendsPage();
  286. }));
  287. }),
  288. ];
  289. _functionButtons.insert(0, _buildNewFriends(showUserList));
  290. }
  291. msgAddfrend(data) {
  292. // setState(() {
  293. // _contactsFuture = FriendListMgr().getFriendList();
  294. // });
  295. getFriendList();
  296. }
  297. msgDeletefrend(data) {
  298. // setState(() {
  299. // _contactsFuture = FriendListMgr().getFriendList();
  300. // });
  301. getFriendList();
  302. }
  303. updateIndexPos(List<FriendModel> friendList) {
  304. //计算用于 IndexBar 进行定位的关键通讯录列表项的位置
  305. var _totalPos = _functionButtons.length * FriendsInfo.height(false);
  306. for (var i = 0; i < friendList.length; i++) {
  307. bool _hasGroupTitle = true;
  308. if (i > 0 &&
  309. friendList[i].nameTag.compareTo(friendList[i - 1].nameTag) == 0) {
  310. _hasGroupTitle = false;
  311. }
  312. if (_hasGroupTitle) {
  313. _letterPosMap[friendList[i].nameTag] = _totalPos;
  314. }
  315. _totalPos += FriendsInfo.height(_hasGroupTitle);
  316. }
  317. }
  318. @override
  319. void dispose() {
  320. _scrollController.dispose();
  321. print('FriendPage dispose');
  322. MessageMgr().off('Add friend', msgAddfrend);
  323. MessageMgr().off('Delete friend', msgDeletefrend);
  324. MessageMgr().off('do_friend_apply', messageApply);
  325. MessageMgr().off('goto_new_friends', messageNew);
  326. super.dispose();
  327. }
  328. String getLetter(BuildContext context, double tileHeight, Offset globalPos) {
  329. RenderBox _box = context.findRenderObject();
  330. var local = _box.globalToLocal(globalPos);
  331. int index = (local.dy ~/ tileHeight).clamp(0, INDEX_BAR_WORDS.length - 1);
  332. return INDEX_BAR_WORDS[index];
  333. }
  334. void _jumpToIndex(String letter) {
  335. if (_letterPosMap.isNotEmpty) {
  336. final _pos = _letterPosMap[letter];
  337. if (_pos != null) {
  338. _scrollController.animateTo(_letterPosMap[letter],
  339. curve: Curves.easeInOut, duration: Duration(microseconds: 200));
  340. }
  341. }
  342. }
  343. Widget _buildIndexBar(BuildContext context, BoxConstraints constraints) {
  344. final List<Widget> _letters = INDEX_BAR_WORDS.map((String word) {
  345. return Expanded(
  346. child: Container(
  347. margin: EdgeInsets.only(right: 5),
  348. decoration: BoxDecoration(
  349. shape: BoxShape.circle,
  350. color:
  351. _currentLetter == word ? Colors.blue : Colors.transparent,
  352. ),
  353. alignment: Alignment.center,
  354. padding: EdgeInsets.all(2),
  355. width: 20,
  356. child: Text(
  357. word,
  358. style: TextStyle(
  359. fontSize: 10,
  360. color:
  361. _currentLetter == word ? Colors.white : Colors.black),
  362. )));
  363. }).toList();
  364. final _totalHeight = constraints.biggest.height;
  365. final _tileHeight = _totalHeight / _letters.length;
  366. return GestureDetector(
  367. onVerticalDragDown: (DragDownDetails details) {
  368. setState(() {
  369. _currentLetter =
  370. getLetter(context, _tileHeight, details.globalPosition);
  371. _jumpToIndex(_currentLetter);
  372. });
  373. },
  374. onVerticalDragEnd: (DragEndDetails details) {
  375. setState(() {
  376. //_indexBarBgColor = Colors.transparent;
  377. _currentLetter = null;
  378. });
  379. },
  380. onVerticalDragCancel: () {
  381. setState(() {
  382. //_indexBarBgColor = Colors.transparent;
  383. _currentLetter = null;
  384. });
  385. },
  386. onVerticalDragUpdate: (DragUpdateDetails details) {
  387. setState(() {
  388. //var _letter = getLetter(context, _tileHeight, details.globalPosition);
  389. _currentLetter =
  390. getLetter(context, _tileHeight, details.globalPosition);
  391. _jumpToIndex(_currentLetter);
  392. });
  393. },
  394. child: Column(
  395. children: _letters,
  396. ),
  397. );
  398. }
  399. @override
  400. Widget build(BuildContext context) {
  401. final List<Widget> _body = [];
  402. friendList.sort((a, b) => a.nameTag.compareTo(b.nameTag));
  403. updateIndexPos(friendList);
  404. _body.addAll([
  405. ListView.builder(
  406. controller: _scrollController,
  407. itemBuilder: (BuildContext context, int index) {
  408. if (index < _functionButtons.length) {
  409. if (index == 0) {
  410. return Padding(
  411. padding: EdgeInsets.only(top: 7),
  412. child: _functionButtons[index],
  413. );
  414. } else {
  415. return _functionButtons[index];
  416. }
  417. }
  418. int _contactIndex = index - _functionButtons.length;
  419. bool _isGroupTitle = true;
  420. FriendModel _contact = friendList[_contactIndex];
  421. if (_contactIndex >= 1 &&
  422. _contact.nameTag == friendList[_contactIndex - 1].nameTag) {
  423. _isGroupTitle = false;
  424. }
  425. return FriendsInfo(
  426. userId: _contact.friendId,
  427. avatar: _contact.avatar,
  428. title: Provider.of<RefNameProvider>(context)
  429. .getRefName(_contact.friendId, _contact.name),
  430. isShowDivder: _isGroupTitle,
  431. groupTitle: _isGroupTitle ? _contact.nameTag : null);
  432. },
  433. itemCount: friendList.length + _functionButtons.length,
  434. ),
  435. Positioned(
  436. width: Constants.IndexBarWidth,
  437. right: 0.0,
  438. top: 0.0,
  439. bottom: 0.0,
  440. child: Container(
  441. //color: _indexBarBgColor,
  442. child: LayoutBuilder(
  443. builder: _buildIndexBar,
  444. ),
  445. ),
  446. )
  447. ]);
  448. if (_currentLetter != null && _currentLetter.isNotEmpty) {
  449. _body.add(Center(
  450. child: Container(
  451. width: Constants.IndexLetterBoxSize,
  452. height: Constants.IndexLetterBoxSize,
  453. decoration: BoxDecoration(
  454. color: AppColors.IndexLetterBoxBgColor,
  455. borderRadius: BorderRadius.all(
  456. Radius.circular(Constants.IndexLetterBoxRadius)),
  457. ),
  458. child: Center(
  459. child:
  460. Text(_currentLetter, style: AppStyles.IndexLetterBoxTextStyle),
  461. ),
  462. ),
  463. ));
  464. }
  465. return Scaffold(
  466. resizeToAvoidBottomPadding: false,
  467. appBar: AppBar(
  468. backgroundColor: AppColors.NewAppbarBgColor,
  469. title: Text(I18n.of(context).contact,
  470. textScaleFactor: 1.0, style: Constants.MainTitleStyle),
  471. centerTitle: false,
  472. elevation: 1,
  473. bottom: CustomUI.buildSearchButton(context, () {
  474. Navigator.of(context).push(
  475. new MaterialPageRoute(
  476. builder: (context) {
  477. return GlobalSearchPage(
  478. type: GlobalSearchPageType.SearchMyFriends,
  479. );
  480. },
  481. ),
  482. );
  483. })),
  484. body: Stack(
  485. children: _body,
  486. ));
  487. }
  488. }