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.
 
 
 
 
 
 

498 lines
17 KiB

  1. import 'package:chat/data/UserData.dart';
  2. import 'package:chat/data/WebData.dart';
  3. import 'package:chat/data/constants.dart';
  4. import 'package:chat/generated/i18n.dart';
  5. import 'package:chat/models/ref_name_provider.dart';
  6. import 'package:chat/utils/CustomUI.dart';
  7. import 'package:chat/utils/HttpUtil.dart';
  8. import 'package:chat/utils/MessageMgr.dart';
  9. import 'package:chat/utils/TokenMgr.dart';
  10. import 'package:chat/utils/conversation_table.dart';
  11. import 'package:chat/utils/screen.dart';
  12. import 'package:dio/dio.dart';
  13. import 'package:cached_network_image/cached_network_image.dart';
  14. import 'package:flutter/material.dart';
  15. import 'package:flutter/services.dart';
  16. import 'package:provider/provider.dart';
  17. import 'package:chat/models/friends_info.dart';
  18. import 'ProfilePage.dart';
  19. import 'package:permission_handler/permission_handler.dart';
  20. import 'dart:io';
  21. import 'address_book.dart';
  22. import 'new_addfriends.dart';
  23. class _ContactItem extends StatelessWidget {
  24. _ContactItem(
  25. {@required this.avatar,
  26. @required this.title,
  27. @required this.userId,
  28. this.groupTitle,
  29. this.onPressed,
  30. this.gradient,
  31. this.iconCode,
  32. this.applyId,
  33. this.isShowDivder: true,
  34. this.state});
  35. final int userId;
  36. final int iconCode;
  37. final String avatar;
  38. final String title;
  39. final String groupTitle;
  40. final VoidCallback onPressed;
  41. final Gradient gradient;
  42. final bool isShowDivder;
  43. final int state;
  44. final int applyId;
  45. static const double MARGIN_VERTICAL = 10.0;
  46. static const double GROUP_TITLE_HEIGHT = 24.0;
  47. @override
  48. Widget build(BuildContext context) {
  49. Widget _avatarIcon;
  50. if (iconCode == null) {
  51. _avatarIcon = ClipRRect(
  52. borderRadius: BorderRadius.circular(6),
  53. child: CachedNetworkImage(
  54. imageUrl: this.avatar,
  55. placeholder: (context, url) => Image.asset(
  56. Constants.DefaultHeadImgUrl,
  57. width: Constants.ContactAvatarSize,
  58. height: Constants.ContactAvatarSize,
  59. ),
  60. width: Constants.ContactAvatarSize,
  61. height: Constants.ContactAvatarSize,
  62. ));
  63. } else {
  64. _avatarIcon = Container(
  65. width: Constants.ContactAvatarSize,
  66. height: Constants.ContactAvatarSize,
  67. decoration: BoxDecoration(
  68. gradient: gradient, borderRadius: BorderRadius.circular(6)),
  69. child: Icon(
  70. IconData(this.iconCode, fontFamily: Constants.IconFontFamily),
  71. color: Colors.white,
  72. ),
  73. );
  74. }
  75. Widget _button = Container(
  76. padding: const EdgeInsets.symmetric(
  77. vertical: MARGIN_VERTICAL, horizontal: 16.0),
  78. decoration: BoxDecoration(color: Colors.white),
  79. child: Row(
  80. children: <Widget>[
  81. _avatarIcon,
  82. SizedBox(width: 10.0),
  83. Expanded(
  84. child: Text(
  85. title,
  86. textScaleFactor: 1.0,
  87. )),
  88. state == 1
  89. ? Container(
  90. padding: EdgeInsets.symmetric(horizontal: 21, vertical: 7),
  91. child: Text(
  92. I18n.of(context).added,
  93. textScaleFactor: 1.0,
  94. style: TextStyle(color: const Color(0xFF8A8B8B)),
  95. ),
  96. )
  97. : (state == 0
  98. ? Container(
  99. padding:
  100. EdgeInsets.symmetric(horizontal: 21, vertical: 7),
  101. child: Text(
  102. I18n.of(context).check,
  103. textScaleFactor: 1.0,
  104. style: TextStyle(color: Constants.BlueTextColor),
  105. ),
  106. )
  107. : Container(
  108. padding:
  109. EdgeInsets.symmetric(horizontal: 21, vertical: 7),
  110. child: Text(
  111. I18n.of(context).rejected,
  112. textScaleFactor: 1.0,
  113. style: TextStyle(color: const Color(0xFF8A8B8B)),
  114. ),
  115. )),
  116. ],
  117. ),
  118. );
  119. //分组标签
  120. Widget _itemBody;
  121. if (this.groupTitle != null) {
  122. _itemBody = Column(
  123. children: <Widget>[
  124. Container(
  125. height: GROUP_TITLE_HEIGHT,
  126. padding: EdgeInsets.only(left: 16.0, right: 16.0),
  127. color: const Color(AppColors.ContactGroupTitleBgColor),
  128. alignment: Alignment.centerLeft,
  129. child: Text(
  130. this.groupTitle,
  131. style: AppStyles.GroupTitleItemTextStyle,
  132. textScaleFactor: 1.0,
  133. ),
  134. ),
  135. _button,
  136. ],
  137. );
  138. } else {
  139. _itemBody = _button;
  140. }
  141. return InkWell(
  142. onTap: () {
  143. Navigator.of(context).push(
  144. new MaterialPageRoute(
  145. builder: (context) {
  146. return ProfilePage(
  147. userId: userId,
  148. addMode: state == 0 ? 2 : 0,
  149. applyId: applyId,
  150. );
  151. },
  152. ),
  153. );
  154. },
  155. child: Container(
  156. color: Colors.white,
  157. child: Column(
  158. children: <Widget>[
  159. _itemBody,
  160. isShowDivder
  161. ? Container(
  162. height: 1,
  163. color: const Color(0xFFF3F3F3),
  164. margin: EdgeInsets.only(
  165. left: 26 + Constants.ContactAvatarSize),
  166. )
  167. : Container()
  168. ],
  169. )));
  170. }
  171. }
  172. class NewFriendsPage extends StatefulWidget {
  173. @override
  174. _NewFriendsPageState createState() => _NewFriendsPageState();
  175. }
  176. class _NewFriendsPageState extends State<NewFriendsPage> {
  177. String _currentLetter = '';
  178. ScrollController _scrollController;
  179. TextEditingController _txtCtrl = new TextEditingController();
  180. bool _hasdeleteIcon = false;
  181. List<FriendModel> searchList = [];
  182. List<FriendModel> friendList = [];
  183. List<Widget> _functionButtons = [];
  184. @override
  185. void initState() {
  186. super.initState();
  187. print('NewFriendsPage initState');
  188. getNewFriendList();
  189. _scrollController = new ScrollController();
  190. MessageMgr().on('do_friend_apply', messageApply);
  191. _functionButtons = [
  192. FriendsInfo(
  193. avatar: '',
  194. userId: 0,
  195. iconCode: 0xe67a,
  196. title: I18n.of(Constants.getCurrentContext()).contact_add,
  197. gradient: LinearGradient(
  198. begin: Alignment.topCenter,
  199. end: Alignment.bottomCenter,
  200. colors: <Color>[
  201. const Color(0xFF1AC59E),
  202. const Color(0xFF088E76),
  203. ]),
  204. onPressed: () async {
  205. final PermissionStatus addStatus = await PermissionHandler()
  206. .checkPermissionStatus(PermissionGroup.contacts);
  207. if ((addStatus == PermissionStatus.unknown ||
  208. addStatus == PermissionStatus.denied) &&
  209. Platform.isIOS) {
  210. CustomUI.buildContacts(context, I18n.of(context).privacyAgreement,
  211. I18n.of(context).determine, () {
  212. Navigator.of(context).pop();
  213. showPermission();
  214. }, title: I18n.of(context).tip);
  215. } else {
  216. showPermission();
  217. }
  218. }),
  219. ];
  220. }
  221. showPermission() async {
  222. if (await CustomUI.showPermissionSetting(
  223. Constants.getCurrentContext(),
  224. PermissionGroup.contacts,
  225. I18n.of(Constants.getCurrentContext()).contact_permission)) {
  226. MessageMgr().emit('PostContact');
  227. Navigator.push(Constants.getCurrentContext(),
  228. MaterialPageRoute<void>(builder: (BuildContext context) {
  229. return ContactsPage();
  230. }));
  231. }
  232. }
  233. messageApply(data) {
  234. for (int i = 0; i < friendList.length; i++) {
  235. if (friendList[i].friendId == data['userId']) {
  236. setState(() {
  237. friendList[i].state = data['state'];
  238. });
  239. break;
  240. }
  241. }
  242. }
  243. getNewFriendList() async {
  244. Map data = {
  245. "userId": UserData().basicInfo.userId,
  246. "type": 2,
  247. };
  248. data['sign'] = TokenMgr().getSign(data);
  249. Response res =
  250. await HttpUtil().post('friendship/newFriends/record', data: data);
  251. Map resData = res.data;
  252. print(resData['data']);
  253. if (resData['code'] == 0 && resData['data'] != null) {
  254. resData['data'].forEach((f) {
  255. var friend = FriendModel.fromApplyServerJson(f);
  256. if (friend.friendId != UserData().basicInfo.userId)
  257. friendList.add(friend);
  258. });
  259. setState(() {});
  260. }
  261. }
  262. @override
  263. void dispose() {
  264. _scrollController.dispose();
  265. super.dispose();
  266. MessageMgr().off('do_friend_apply', messageApply);
  267. }
  268. @override
  269. Widget build(BuildContext context) {
  270. final List<Widget> _body = [];
  271. if (!_hasdeleteIcon) {
  272. _body.add(ListView.builder(
  273. controller: _scrollController,
  274. itemBuilder: (BuildContext context, int index) {
  275. if (index < _functionButtons.length) {
  276. if (index == 0) {
  277. return Padding(
  278. padding: EdgeInsets.only(top: 7),
  279. child: _functionButtons[index],
  280. );
  281. } else {
  282. return _functionButtons[index];
  283. }
  284. }
  285. int _contactIndex = index - _functionButtons.length;
  286. bool _isGroupTitle = true;
  287. FriendModel _contact = friendList[_contactIndex];
  288. bool isThreeDay = WebData().isThreeDayAgo(_contact.creatTime);
  289. if (_contactIndex >= 1 &&
  290. WebData().isThreeDayAgo(_contact.creatTime) ==
  291. WebData()
  292. .isThreeDayAgo(friendList[_contactIndex - 1].creatTime)) {
  293. _isGroupTitle = false;
  294. }
  295. return _ContactItem(
  296. userId: _contact.friendId,
  297. avatar: _contact.avatar,
  298. title: Provider.of<RefNameProvider>(context)
  299. .getRefName(_contact.friendId, _contact.name),
  300. state: _contact.state,
  301. isShowDivder: true,
  302. applyId: _contact.applyId,
  303. groupTitle: _isGroupTitle
  304. ? (isThreeDay
  305. ? I18n.of(context).before_three_day
  306. : I18n.of(context).after_three_day)
  307. : null);
  308. },
  309. itemCount: friendList.length + _functionButtons.length,
  310. ));
  311. } else {
  312. _body.add(ListView.builder(
  313. controller: _scrollController,
  314. itemBuilder: (BuildContext context, int index) {
  315. FriendModel _contact = searchList[index];
  316. return _ContactItem(
  317. userId: _contact.friendId,
  318. avatar: _contact.avatar,
  319. title: _contact.name,
  320. state: _contact.state,
  321. isShowDivder: true,
  322. applyId: _contact.applyId,
  323. groupTitle: null);
  324. },
  325. itemCount: searchList.length,
  326. ));
  327. }
  328. if (_currentLetter != null &&
  329. _currentLetter.isNotEmpty &&
  330. !_hasdeleteIcon) {
  331. _body.add(Center(
  332. child: Container(
  333. width: Constants.IndexLetterBoxSize,
  334. height: Constants.IndexLetterBoxSize,
  335. decoration: BoxDecoration(
  336. color: AppColors.IndexLetterBoxBgColor,
  337. borderRadius: BorderRadius.all(
  338. Radius.circular(Constants.IndexLetterBoxRadius)),
  339. ),
  340. child: Center(
  341. child: Text(
  342. _currentLetter,
  343. style: AppStyles.IndexLetterBoxTextStyle,
  344. textScaleFactor: 1.0,
  345. ),
  346. ),
  347. ),
  348. ));
  349. }
  350. return Scaffold(
  351. resizeToAvoidBottomPadding: false,
  352. appBar: AppBar(
  353. backgroundColor: AppColors.NewAppbarBgColor,
  354. title: Text(
  355. I18n.of(context).new_friends,
  356. textScaleFactor: 1.0,
  357. style: TextStyle(color: AppColors.NewAppbarTextColor),
  358. ),
  359. centerTitle: true,
  360. leading: CustomUI.buildCustomLeading(context),
  361. elevation: 1,
  362. actions: <Widget>[
  363. Container(
  364. child: IconButton(
  365. icon: CircleAvatar(
  366. backgroundColor: Constants.GreyBackgroundColor,
  367. radius: 13.75,
  368. child: Padding(
  369. padding: EdgeInsets.only(bottom: 1.5),
  370. child: Icon(
  371. IconData(0xe662,
  372. fontFamily: Constants.IconFontFamily),
  373. color: Constants.BlackTextColor,
  374. size: 21,
  375. ))),
  376. onPressed: () {
  377. Navigator.of(context).push(
  378. new MaterialPageRoute(
  379. builder: (context) {
  380. return NewAddFriendsPage();
  381. },
  382. ),
  383. );
  384. },
  385. ),
  386. ),
  387. ],
  388. bottom: PreferredSize(
  389. preferredSize: Size.fromHeight(49),
  390. child: Container(
  391. alignment: Alignment.center,
  392. margin: EdgeInsets.only(bottom: 14, left: 12.5, right: 12.5),
  393. height: 35,
  394. decoration: BoxDecoration(
  395. color: const Color(0xFFEEEEEE),
  396. borderRadius: BorderRadius.all(Radius.circular(8))),
  397. child: TextField(
  398. keyboardAppearance: Brightness.light,
  399. keyboardType: TextInputType.text,
  400. textInputAction: TextInputAction.search,
  401. controller: _txtCtrl,
  402. maxLines: 1,
  403. style: TextStyle(
  404. textBaseline: TextBaseline.alphabetic, fontSize: 14.5),
  405. autofocus: false,
  406. inputFormatters: [
  407. LengthLimitingTextInputFormatter(50),
  408. ],
  409. decoration: InputDecoration(
  410. hintText: I18n.of(context).search,
  411. hintStyle: TextStyle(fontSize: 14.5),
  412. contentPadding: EdgeInsets.only(
  413. left: 20,
  414. top: (UserData().language == LanguageType.English ||
  415. UserData().language ==
  416. LanguageType.Vietnamese)
  417. ? 3
  418. : 10.5,
  419. bottom: 10.5),
  420. prefixIcon: Icon(
  421. IconData(
  422. 0xe664,
  423. fontFamily: Constants.IconFontFamily,
  424. ),
  425. color: const Color(0xFFA0A0A0),
  426. size: 18,
  427. ),
  428. suffixIcon: Padding(
  429. padding: EdgeInsetsDirectional.only(
  430. start: 2.0, end: _hasdeleteIcon ? 20.0 : 0),
  431. child: _hasdeleteIcon
  432. ? new InkWell(
  433. onTap: (() {
  434. setState(() {
  435. WidgetsBinding.instance
  436. .addPostFrameCallback(
  437. (_) => _txtCtrl.clear());
  438. _hasdeleteIcon = false;
  439. });
  440. }),
  441. child: Icon(
  442. Icons.clear,
  443. size: 18.0,
  444. color: Constants.BlackTextColor,
  445. ))
  446. : new Text('')),
  447. filled: true,
  448. fillColor: Colors.transparent,
  449. border: InputBorder.none,
  450. ),
  451. onChanged: (str) async {
  452. setState(() {
  453. if (str.isEmpty) {
  454. _hasdeleteIcon = false;
  455. } else {
  456. _hasdeleteIcon = true;
  457. searchList = CustomUI().getSearchResult(
  458. str, friendList == null ? [] : friendList);
  459. }
  460. });
  461. },
  462. onEditingComplete: () {}),
  463. )),
  464. ),
  465. body: Stack(
  466. children: _body,
  467. ));
  468. }
  469. }