Hibok
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 
 

551 řádky
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/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. CustomUI.buildContacts(context, I18n.of(context).privacyAgreement,
  265. I18n.of(context).determine, () {
  266. Navigator.of(context).pop();
  267. showPermission();
  268. }, title: I18n.of(context).tip);
  269. return;
  270. final PermissionStatus addStatus = await PermissionHandler()
  271. .checkPermissionStatus(PermissionGroup.contacts);
  272. if ((addStatus == PermissionStatus.unknown ||
  273. addStatus == PermissionStatus.denied) &&
  274. Platform.isIOS) {
  275. CustomUI.buildContacts(context, I18n.of(context).privacyAgreement,
  276. I18n.of(context).determine, () {
  277. Navigator.of(context).pop();
  278. showPermission();
  279. }, title: I18n.of(context).tip);
  280. } else {
  281. showPermission();
  282. }
  283. }),
  284. FriendsInfo(
  285. avatar: '',
  286. userId: 0,
  287. iconCode: 0xe662,
  288. isShowDivder: true,
  289. title: I18n.of(Constants.getCurrentContext()).add_friends,
  290. gradient: LinearGradient(
  291. begin: Alignment.topCenter,
  292. end: Alignment.bottomCenter,
  293. colors: <Color>[
  294. const Color(0xFF9E58E5),
  295. const Color(0xFF6118A6),
  296. ]),
  297. onPressed: () async {
  298. Navigator.push(Constants.getCurrentContext(),
  299. MaterialPageRoute<void>(builder: (BuildContext context) {
  300. return SearchNewFriendsPage();
  301. }));
  302. }),
  303. ];
  304. _functionButtons.insert(0, _buildNewFriends(showUserList));
  305. }
  306. msgAddfrend(data) {
  307. // setState(() {
  308. // _contactsFuture = FriendListMgr().getFriendList();
  309. // });
  310. getFriendList();
  311. }
  312. msgDeletefrend(data) {
  313. // setState(() {
  314. // _contactsFuture = FriendListMgr().getFriendList();
  315. // });
  316. getFriendList();
  317. }
  318. updateIndexPos(List<FriendModel> friendList) {
  319. //计算用于 IndexBar 进行定位的关键通讯录列表项的位置
  320. var _totalPos = _functionButtons.length * FriendsInfo.height(false);
  321. for (var i = 0; i < friendList.length; i++) {
  322. bool _hasGroupTitle = true;
  323. if (i > 0 &&
  324. friendList[i].nameTag.compareTo(friendList[i - 1].nameTag) == 0) {
  325. _hasGroupTitle = false;
  326. }
  327. if (_hasGroupTitle) {
  328. _letterPosMap[friendList[i].nameTag] = _totalPos;
  329. }
  330. _totalPos += FriendsInfo.height(_hasGroupTitle);
  331. }
  332. }
  333. @override
  334. void dispose() {
  335. _scrollController.dispose();
  336. print('FriendPage dispose');
  337. MessageMgr().off('Add friend', msgAddfrend);
  338. MessageMgr().off('Delete friend', msgDeletefrend);
  339. MessageMgr().off('do_friend_apply', messageApply);
  340. MessageMgr().off('goto_new_friends', messageNew);
  341. super.dispose();
  342. }
  343. String getLetter(BuildContext context, double tileHeight, Offset globalPos) {
  344. RenderBox _box = context.findRenderObject();
  345. var local = _box.globalToLocal(globalPos);
  346. int index = (local.dy ~/ tileHeight).clamp(0, INDEX_BAR_WORDS.length - 1);
  347. return INDEX_BAR_WORDS[index];
  348. }
  349. void _jumpToIndex(String letter) {
  350. if (_letterPosMap.isNotEmpty) {
  351. final _pos = _letterPosMap[letter];
  352. if (_pos != null) {
  353. _scrollController.animateTo(_letterPosMap[letter],
  354. curve: Curves.easeInOut, duration: Duration(microseconds: 200));
  355. }
  356. }
  357. }
  358. Widget _buildIndexBar(BuildContext context, BoxConstraints constraints) {
  359. final List<Widget> _letters = INDEX_BAR_WORDS.map((String word) {
  360. return Expanded(
  361. child: Container(
  362. margin: EdgeInsets.only(right: 5),
  363. decoration: BoxDecoration(
  364. shape: BoxShape.circle,
  365. color:
  366. _currentLetter == word ? Colors.blue : Colors.transparent,
  367. ),
  368. alignment: Alignment.center,
  369. padding: EdgeInsets.all(2),
  370. width: 20,
  371. child: Text(
  372. word,
  373. textScaleFactor: 1.0,
  374. style: TextStyle(
  375. fontSize: 10,
  376. color:
  377. _currentLetter == word ? Colors.white : Colors.black),
  378. )));
  379. }).toList();
  380. final _totalHeight = constraints.biggest.height;
  381. final _tileHeight = _totalHeight / _letters.length;
  382. return GestureDetector(
  383. onVerticalDragDown: (DragDownDetails details) {
  384. setState(() {
  385. _currentLetter =
  386. getLetter(context, _tileHeight, details.globalPosition);
  387. _jumpToIndex(_currentLetter);
  388. });
  389. },
  390. onVerticalDragEnd: (DragEndDetails details) {
  391. setState(() {
  392. //_indexBarBgColor = Colors.transparent;
  393. _currentLetter = null;
  394. });
  395. },
  396. onVerticalDragCancel: () {
  397. setState(() {
  398. //_indexBarBgColor = Colors.transparent;
  399. _currentLetter = null;
  400. });
  401. },
  402. onVerticalDragUpdate: (DragUpdateDetails details) {
  403. setState(() {
  404. //var _letter = getLetter(context, _tileHeight, details.globalPosition);
  405. _currentLetter =
  406. getLetter(context, _tileHeight, details.globalPosition);
  407. _jumpToIndex(_currentLetter);
  408. });
  409. },
  410. child: Column(
  411. children: _letters,
  412. ),
  413. );
  414. }
  415. @override
  416. Widget build(BuildContext context) {
  417. final List<Widget> _body = [];
  418. friendList.sort((a, b) => a.nameTag.compareTo(b.nameTag));
  419. updateIndexPos(friendList);
  420. _body.addAll([
  421. ListView.builder(
  422. controller: _scrollController,
  423. itemBuilder: (BuildContext context, int index) {
  424. if (index < _functionButtons.length) {
  425. if (index == 0) {
  426. return Padding(
  427. padding: EdgeInsets.only(top: 7),
  428. child: _functionButtons[index],
  429. );
  430. } else {
  431. return _functionButtons[index];
  432. }
  433. }
  434. int _contactIndex = index - _functionButtons.length;
  435. bool _isGroupTitle = true;
  436. FriendModel _contact = friendList[_contactIndex];
  437. if (_contactIndex >= 1 &&
  438. _contact.nameTag == friendList[_contactIndex - 1].nameTag) {
  439. _isGroupTitle = false;
  440. }
  441. return FriendsInfo(
  442. userId: _contact.friendId,
  443. avatar: _contact.avatar,
  444. title: Provider.of<RefNameProvider>(context)
  445. .getRefName(_contact.friendId, _contact.name),
  446. isShowDivder: _isGroupTitle,
  447. groupTitle: _isGroupTitle ? _contact.nameTag : null);
  448. },
  449. itemCount: friendList.length + _functionButtons.length,
  450. ),
  451. Positioned(
  452. width: Constants.IndexBarWidth,
  453. right: 0.0,
  454. top: 0.0,
  455. bottom: 0.0,
  456. child: Container(
  457. //color: _indexBarBgColor,
  458. child: LayoutBuilder(
  459. builder: _buildIndexBar,
  460. ),
  461. ),
  462. )
  463. ]);
  464. if (_currentLetter != null && _currentLetter.isNotEmpty) {
  465. _body.add(Center(
  466. child: Container(
  467. width: Constants.IndexLetterBoxSize,
  468. height: Constants.IndexLetterBoxSize,
  469. decoration: BoxDecoration(
  470. color: AppColors.IndexLetterBoxBgColor,
  471. borderRadius: BorderRadius.all(
  472. Radius.circular(Constants.IndexLetterBoxRadius)),
  473. ),
  474. child: Center(
  475. child:
  476. Text(_currentLetter, textScaleFactor: 1.0,style: AppStyles.IndexLetterBoxTextStyle),
  477. ),
  478. ),
  479. ));
  480. }
  481. return Scaffold(
  482. resizeToAvoidBottomPadding: false,
  483. appBar: AppBar(
  484. backgroundColor: AppColors.NewAppbarBgColor,
  485. title: Text(I18n.of(context).contact,
  486. textScaleFactor: 1.0, style: Constants.MainTitleStyle),
  487. centerTitle: false,
  488. elevation: 1,
  489. bottom: CustomUI.buildSearchButton(context, () {
  490. Navigator.of(context).push(
  491. new MaterialPageRoute(
  492. builder: (context) {
  493. return GlobalSearchPage(
  494. type: GlobalSearchPageType.SearchMyFriends,
  495. );
  496. },
  497. ),
  498. );
  499. })),
  500. body: Stack(
  501. children: _body,
  502. ));
  503. }
  504. }