Hibok
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 
 

516 行
16 KiB

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