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

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