user_detail_page.dart 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter/rendering.dart';
  3. import 'package:flutter/services.dart';
  4. import 'package:provider/provider.dart';
  5. import 'package:sport/application.dart';
  6. import 'package:sport/bean/post_user.dart';
  7. import 'package:sport/bean/user_friend.dart';
  8. import 'package:sport/bean/user_info.dart';
  9. import 'package:sport/pages/social/block_user_list_page.dart';
  10. import 'package:sport/pages/social/user_post_page.dart';
  11. import 'package:sport/provider/lib/view_state_lifecycle.dart';
  12. import 'package:sport/provider/user_detail_model.dart';
  13. import 'package:sport/provider/user_model.dart';
  14. import 'package:sport/router/navigator_util.dart';
  15. import 'package:sport/services/api/inject_api.dart';
  16. import 'package:sport/services/userid.dart';
  17. import 'package:sport/utils/toast.dart';
  18. import 'package:sport/widgets/button_primary.dart';
  19. import 'package:sport/widgets/dialog/request_dialog.dart';
  20. import 'package:sport/widgets/image.dart';
  21. import 'package:sport/widgets/misc.dart';
  22. import 'package:sport/widgets/popmenu_bg.dart';
  23. import 'package:sport/widgets/space.dart';
  24. import 'package:sport/widgets/user_widget.dart';
  25. class UserDetailPage extends StatefulWidget {
  26. final PostUser user;
  27. final List<UserFriend>? userFriends;
  28. UserDetailPage(this.user, {this.userFriends});
  29. @override
  30. State<StatefulWidget> createState() => _PageState();
  31. }
  32. class _PageState extends ViewStateLifecycle<UserDetailPage, UserDetailModel> with InjectApi, UserId {
  33. String _genderLabel = "他";
  34. bool _ready = false;
  35. late ValueNotifier<UserInfo?> _valueNotifierUserInfo;
  36. @override
  37. void initState() {
  38. super.initState();
  39. _valueNotifierUserInfo = ValueNotifier(UserInfo.fromJson(widget.user.toJson()));
  40. _genderLabel = selfId == widget.user.id ? "我" : "她";
  41. api.getUserInfo(widget.user.id!).then((value) {
  42. _valueNotifierUserInfo.value = value.data;
  43. _ready = true;
  44. _genderLabel = selfId == widget.user.id
  45. ? "我"
  46. : value.data?.gender == 1
  47. ? "他"
  48. : "她";
  49. });
  50. }
  51. bool isSelf() {
  52. return selfId == widget.user.id;
  53. }
  54. @override
  55. void dispose() {
  56. super.dispose();
  57. _valueNotifierUserInfo.dispose();
  58. }
  59. @override
  60. Widget build(BuildContext context) {
  61. var menu = [
  62. if (isSelf()) menuItemCenter('list', '屏蔽列表'),
  63. if (openSocial() && isSelf()) menuItemCenter('post', '社区帖子'),
  64. if (openSocial())
  65. if (!isSelf()) menuItemCenter('block', _valueNotifierUserInfo.value?.blockedFromMe == true ? '取消屏蔽' : '屏蔽此人')
  66. ];
  67. return Scaffold(
  68. backgroundColor: Colors.black.withOpacity(.9),
  69. appBar: AppBar(
  70. automaticallyImplyLeading: false,
  71. backgroundColor: Colors.transparent,
  72. systemOverlayStyle: SystemUiOverlayStyle.light,
  73. leading: IconButton(
  74. icon: Padding(
  75. padding: const EdgeInsets.all(4.0),
  76. child: Image.asset(
  77. "lib/assets/img/btn_close_white.png",
  78. width: 24,
  79. ),
  80. ),
  81. onPressed: () => Navigator.pop(context, false),
  82. ),
  83. actions: [
  84. if (menu.isNotEmpty)
  85. PopupMenuTheme(
  86. data: PopupMenuThemeData(shape: PopmenuShape(borderRadius: BorderRadius.all(Radius.circular(10.0)))),
  87. child: PopupMenuButton(
  88. icon: iconMore(),
  89. onSelected: (val) async {
  90. switch (val) {
  91. case 'block':
  92. if (_valueNotifierUserInfo.value?.blockedFromMe == true) {
  93. await request(context, () async {
  94. var resp = await model.api.postForumUnBlockUser("${_valueNotifierUserInfo.value?.id}");
  95. if (resp.code == 0) {
  96. ToastUtil.show("取消屏蔽成功");
  97. _valueNotifierUserInfo.value?.blockedFromMe = false;
  98. }
  99. });
  100. } else {
  101. await request(context, () async {
  102. var resp = await model.api.postForumBlockUser("${_valueNotifierUserInfo.value?.id}");
  103. if (resp.code == 0) {
  104. ToastUtil.show("屏蔽成功");
  105. _valueNotifierUserInfo.value?.blockedFromMe = true;
  106. }
  107. });
  108. }
  109. setState(() {});
  110. break;
  111. case 'list':
  112. NavigatorUtil.goPage(context, (context) => BlockUserListPage());
  113. break;
  114. case 'post':
  115. NavigatorUtil.goPage(context, (context) => UserPostPage(widget.user));
  116. break;
  117. }
  118. },
  119. offset: Offset(0, kToolbarHeight / 2 + 15),
  120. itemBuilder: (_) => menu,
  121. ))
  122. ],
  123. ),
  124. body: Container(
  125. width: double.infinity,
  126. height: double.infinity,
  127. child: Center(
  128. child: SingleChildScrollView(
  129. child: Column(
  130. mainAxisSize: MainAxisSize.min,
  131. children: [
  132. Container(
  133. width: double.infinity,
  134. child: ValueListenableBuilder(
  135. valueListenable: _valueNotifierUserInfo,
  136. builder: (BuildContext context, UserInfo? user, Widget? child) {
  137. if (user == null) {
  138. return Container();
  139. }
  140. return Column(
  141. mainAxisSize: MainAxisSize.min,
  142. children: <Widget>[
  143. CircleAvatar(backgroundColor: Colors.black26,
  144. backgroundImage: userAvatarProvider(user.avatar),
  145. radius: 50,
  146. ),
  147. Space(
  148. height: 13,
  149. ),
  150. Row(
  151. mainAxisSize: MainAxisSize.min,
  152. children: [
  153. const SizedBox(
  154. width: 70,
  155. ),
  156. Text(user.name ?? "", style: TextStyle(color: Colors.white, fontSize: 18)),
  157. ConstrainedBox(constraints: BoxConstraints(minWidth: 70.0), child: UserLevelWidget(level: user.levelInfo)),
  158. ],
  159. ),
  160. Space(
  161. height: 10,
  162. ),
  163. ConstrainedBox(
  164. child: _ready != true
  165. ? Container()
  166. : Row(
  167. mainAxisSize: MainAxisSize.min,
  168. children: <Widget>[
  169. Image.asset("lib/assets/img/mine_icon_${user.gender == 1 ? "manl" : "girl"}_white.png"),
  170. Space(
  171. width: 4,
  172. ),
  173. Text(
  174. user.gender == 1 ? "男" : "女",
  175. style: TextStyle(color: Colors.white, fontSize: 12),
  176. ),
  177. Space(
  178. width: 10,
  179. ),
  180. if ((user.age ?? 0) > 0)
  181. Text(
  182. user.age != null ? "${user.age}岁" : "",
  183. style: TextStyle(color: Colors.white, fontSize: 12),
  184. ),
  185. if ((user.districtId ?? 0) != 0)
  186. Padding(
  187. padding: const EdgeInsets.only(left: 10.0),
  188. child: Text(
  189. "${user.province}${user.province == user.city ? '' : user.city}",
  190. style: TextStyle(color: Colors.white, fontSize: 12),
  191. ),
  192. )
  193. ],
  194. ),
  195. constraints: BoxConstraints(minHeight: 30.0),
  196. ),
  197. Container(
  198. width: double.infinity,
  199. margin: EdgeInsets.all(30.0),
  200. padding: EdgeInsets.all(6.0),
  201. child: Center(
  202. child: Text(
  203. "累计运动${user.activeDay ?? 0}天,共计${(user.exerRecordSum?.duration ?? 0) ~/ 60}分钟",
  204. style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Theme.of(context).colorScheme.secondary),
  205. ),
  206. ),
  207. decoration: BoxDecoration(
  208. gradient: LinearGradient(begin: Alignment.centerLeft, end: Alignment.centerRight, colors: [
  209. Color(0xFFFFC400).withOpacity(.0),
  210. Color(0xFFFFC400).withOpacity(.18),
  211. Color(0xFFFFC400).withOpacity(.0),
  212. ])),
  213. ),
  214. if (selfId != widget.user.id)
  215. Padding(
  216. padding: const EdgeInsets.only(top: 5.0),
  217. child: selfId == '${user.id}'
  218. ? Container(height: 35)
  219. : Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
  220. if(openSocial())
  221. PrimaryButton(
  222. width: 120,
  223. height: 35,
  224. callback: () {
  225. // 先尝试换一下
  226. NavigatorUtil.goPage(context, (context) => UserPostPage(PostUser(id: "${user.id}", name: user.name, avatar: user.avatar, gender: user.gender)));
  227. // NavigatorUtil.goPage(context, (context) => PrivateMessagePage(user));
  228. },
  229. content: '',
  230. shadow: false,
  231. child: Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
  232. Image.asset("lib/assets/img/mine_icon_message.png"),
  233. Space(
  234. width: 6,
  235. ),
  236. Text(
  237. "${widget.user.id == Provider.of<UserModel>(context).user.id.toString() ? '我' : '$_genderLabel'}的帖子",
  238. strutStyle: fixedLine,
  239. style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Colors.white),
  240. )
  241. ]),
  242. ),
  243. if(openSocial())
  244. Space(
  245. width: 12,
  246. ),
  247. user.isFriend()
  248. ? GestureDetector(
  249. child: Container(
  250. width: 120,
  251. height: 35,
  252. alignment: Alignment.center,
  253. child: Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
  254. Image.asset("lib/assets/img/mine_icon_followed.png"),
  255. Space(
  256. width: 6,
  257. ),
  258. Text(
  259. "已关注",
  260. strutStyle: fixedLine,
  261. style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Theme.of(context).colorScheme.secondary),
  262. )
  263. ]),
  264. decoration: BoxDecoration(
  265. borderRadius: BorderRadius.circular(20),
  266. border: Border.all(
  267. color: Theme.of(context).colorScheme.secondary,
  268. width: .5,
  269. ),
  270. ),
  271. ),
  272. onTap: () async {
  273. await request(context, () async {
  274. var resp = await model.api.userUnFollow(uid: user.id).catchError((onError) {});
  275. if (resp.code == 0) {
  276. user.followStatus = "none";
  277. widget.userFriends?.firstWhere((element) => element.uid == user.id).isFriends = "0";
  278. setState(() {});
  279. ToastUtil.show("取关成功");
  280. }
  281. });
  282. },
  283. )
  284. : PrimaryButton(
  285. width: 120,
  286. height: 35,
  287. callback: () async {
  288. await request(context, () async {
  289. var resp = await model.api.userFollow(uid: user.id).catchError((onError) {});
  290. if (resp.code == 0) {
  291. user.followStatus = "followed";
  292. var db = widget.userFriends?.firstWhere((element) => element.uid == user.id);
  293. if (db != null) {
  294. db.isFriends = "1";
  295. db.isIgnore = 0;
  296. }
  297. setState(() {});
  298. ToastUtil.show("关注成功");
  299. }
  300. });
  301. },
  302. content: '',
  303. shadow: false,
  304. child: Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
  305. Image.asset("lib/assets/img/mine_icon_follow.png"),
  306. Space(
  307. width: 6,
  308. ),
  309. Text(
  310. "关注",
  311. strutStyle: fixedLine,
  312. style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Colors.white),
  313. )
  314. ]),
  315. ),
  316. ]),
  317. ),
  318. ConstrainedBox(
  319. constraints: BoxConstraints(minHeight: 200.0),
  320. child: (user.achievements != null && user.achievements?.isNotEmpty == true) == true
  321. ? Column(
  322. children: <Widget>[
  323. Padding(
  324. padding: const EdgeInsets.fromLTRB(12.0, 50.0, 12.0, 12.0),
  325. child: Stack(
  326. alignment: Alignment.center,
  327. children: <Widget>[
  328. Image.asset(
  329. "lib/assets/img/mine_image_achievement.png",
  330. fit: BoxFit.fitWidth,
  331. width: 240,
  332. ),
  333. Text(
  334. "${widget.user.id == Provider.of<UserModel>(context).user.id.toString() ? '我' : '$_genderLabel'}的成就",
  335. style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Colors.white),
  336. )
  337. ],
  338. ),
  339. ),
  340. Padding(
  341. padding: const EdgeInsets.all(12.0),
  342. child: Theme(
  343. child: achievementGroupWidget(user.achievements),
  344. data: Theme.of(context).copyWith(textTheme: Theme.of(context).textTheme.copyWith(subtitle1: TextStyle(fontSize: 12.0, color: Colors.white, fontWeight: FontWeight.normal, height: 1.1))),
  345. ),
  346. ),
  347. ],
  348. )
  349. : Container(),
  350. )
  351. ],
  352. );
  353. },
  354. ),
  355. ),
  356. ],
  357. ))),
  358. ),
  359. );
  360. }
  361. @override
  362. createModel() => UserDetailModel(widget.user.id.toString());
  363. }