post_detail_page.dart 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  1. import 'dart:convert';
  2. import 'dart:math';
  3. import 'package:cached_network_image/cached_network_image.dart';
  4. import 'package:flutter/cupertino.dart';
  5. import 'package:flutter/material.dart';
  6. import 'package:flutter/services.dart';
  7. import 'package:flutter_easyrefresh/easy_refresh.dart';
  8. import 'package:provider/provider.dart';
  9. import 'package:sport/bean/comment.dart';
  10. import 'package:sport/bean/post.dart';
  11. import 'package:sport/bean/post_user.dart';
  12. import 'package:sport/pages/social/gallery_photo_view.dart';
  13. import 'package:sport/pages/social/notification.dart';
  14. import 'package:sport/pages/social/post_comment.dart';
  15. import 'package:sport/pages/social/post_share_page.dart';
  16. import 'package:sport/pages/social/share_webview.dart';
  17. import 'package:sport/provider/lib/provider_widget.dart';
  18. import 'package:sport/provider/lib/view_state_lifecycle.dart';
  19. import 'package:sport/provider/post_detail_model.dart';
  20. import 'package:sport/provider/user_model.dart';
  21. import 'package:sport/router/navigator_util.dart';
  22. import 'package:sport/services/userid.dart';
  23. import 'package:sport/utils/DateFormat.dart';
  24. import 'package:sport/utils/toast.dart';
  25. import 'package:sport/widgets/appbar.dart';
  26. import 'package:sport/widgets/button_primary.dart';
  27. import 'package:sport/widgets/decoration.dart';
  28. import 'package:sport/widgets/dialog/alert_dialog.dart';
  29. import 'package:sport/widgets/dialog/bindphone_dialog.dart';
  30. import 'package:sport/widgets/dialog/comment_dialog.dart';
  31. import 'package:sport/widgets/dialog/modal_bottom_action.dart';
  32. import 'package:sport/widgets/dialog/request_dialog.dart';
  33. import 'package:sport/widgets/error.dart';
  34. import 'package:sport/widgets/image.dart';
  35. import 'package:sport/widgets/loading.dart';
  36. import 'package:sport/widgets/misc.dart';
  37. import 'package:sport/widgets/popmenu_bg.dart';
  38. import 'package:sport/widgets/space.dart';
  39. import 'package:sport/widgets/text_input.dart' as input;
  40. import 'chat_page.dart';
  41. class PostDetailPage extends StatefulWidget {
  42. final List? posts;
  43. final Post post;
  44. final bool comment;
  45. PostDetailPage(this.post, this.comment, this.posts);
  46. @override
  47. State<StatefulWidget> createState() => _PageState();
  48. }
  49. class _PageState extends ViewStateLifecycle<PostDetailPage, PostDetailModel> with UserId, TickerProviderStateMixin {
  50. late FocusNode _focusNode;
  51. late ScrollController _controller;
  52. int _inputType = 0;
  53. late Post _post;
  54. static const List<String> _sort = ["最新评论", "点赞最多"];
  55. String _sortType = _sort[0];
  56. final GlobalKey _key = GlobalKey();
  57. double _offset = 0;
  58. var _padding = Space(
  59. width: 5,
  60. );
  61. var _scrollListener;
  62. var _animationController;
  63. var _iconAnimation;
  64. Comment? _toComment;
  65. PostUser? _toUser;
  66. @override
  67. PostDetailModel createModel() => PostDetailModel(widget.post);
  68. @override
  69. void initState() {
  70. super.initState();
  71. _post = widget.post;
  72. _focusNode = FocusNode();
  73. _scrollListener = () {
  74. resetInput();
  75. };
  76. _controller = ScrollController()..addListener(_scrollListener);
  77. WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
  78. RenderObject? renderObject = _key.currentContext?.findRenderObject();
  79. if (renderObject is RenderBox) {
  80. RenderBox box = renderObject;
  81. var offset = box.localToGlobal(Offset.zero);
  82. final double statusBarHeight = MediaQuery.of(context).padding.top;
  83. _offset = offset.dy - kToolbarHeight - statusBarHeight;
  84. }
  85. });
  86. if (widget.comment) {
  87. setState(() {
  88. _inputType = 1;
  89. });
  90. WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
  91. FocusScope.of(context).requestFocus(_focusNode);
  92. });
  93. }
  94. _animationController = AnimationController(duration: Duration(milliseconds: 300), vsync: this);
  95. _iconAnimation = TweenSequence([
  96. TweenSequenceItem(tween: Tween(begin: 1.0, end: 1.5).chain(CurveTween(curve: Curves.easeIn)), weight: 50),
  97. TweenSequenceItem(tween: Tween(begin: 1.5, end: 1.0), weight: 50),
  98. ]).animate(_animationController);
  99. }
  100. void _scrollToComment() {
  101. _controller.animateTo(_offset, duration: Duration(milliseconds: 500), curve: Curves.ease);
  102. }
  103. void resetInput() {
  104. if (_focusNode.hasFocus) {
  105. _focusNode.unfocus();
  106. setState(() {
  107. _toComment = null;
  108. _toUser = null;
  109. _inputType = 0;
  110. });
  111. }
  112. }
  113. void readyInput() {
  114. setState(() {
  115. _inputType = 1;
  116. });
  117. WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
  118. FocusScope.of(context).requestFocus(_focusNode);
  119. });
  120. }
  121. @override
  122. void dispose() {
  123. _controller.dispose();
  124. _focusNode.dispose();
  125. super.dispose();
  126. }
  127. // /**
  128. // * 1: 评论
  129. // * 2: 点赞
  130. // */
  131. _updatePost(int type, int num) {
  132. if (widget.posts == null) {
  133. _post.toggleLike();
  134. return;
  135. }
  136. widget.posts?.forEach((element) {
  137. if (element.id == widget.post.id) {
  138. if (type == 1) {
  139. element.commentCount = element.commentCount + num;
  140. } else {
  141. element.toggleLike();
  142. }
  143. }
  144. });
  145. }
  146. Widget _postWidget(Post post) {
  147. print(post.quoteData);
  148. double width = MediaQuery.of(context).size.width - 24 - 22;
  149. return Column(
  150. crossAxisAlignment: CrossAxisAlignment.start,
  151. children: <Widget>[
  152. RichText(
  153. maxLines: 3,
  154. overflow: TextOverflow.ellipsis,
  155. text: TextSpan(style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16), children: <InlineSpan>[
  156. TextSpan(text: '${post.nickname}:', style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Theme.of(context).accentColor)),
  157. TextSpan(text: '${post.content}', style: Theme.of(context).textTheme.subtitle1!),
  158. ]),
  159. ),
  160. if (post.images?.isNotEmpty == true)
  161. GridView.count(
  162. physics: new NeverScrollableScrollPhysics(),
  163. shrinkWrap: true,
  164. padding: EdgeInsets.only(top: 15),
  165. childAspectRatio: post.images!.length == 1 ? max(16 / 10, post.images![0].getImageAspectRatio()) : 1,
  166. crossAxisSpacing: 10.0,
  167. crossAxisCount: min(3, post.images!.length),
  168. children: post.images!
  169. .asMap()
  170. .keys
  171. .take(min(3, post.images!.length))
  172. .map((i) => GestureDetector(
  173. onTap: () => open(context, i, post.images!),
  174. child: i < 2
  175. ? post.images!.length == 1
  176. ? Row(
  177. mainAxisSize: MainAxisSize.min,
  178. children: <Widget>[
  179. ClipRRect(
  180. borderRadius: BorderRadius.circular(6),
  181. child: Stack(
  182. children: <Widget>[
  183. CachedNetworkImage(
  184. alignment: Alignment.centerLeft,
  185. imageUrl: post.images![i].thumbnail ??"",
  186. fit: BoxFit.cover,
  187. width: post.images![i].getWidth(width),
  188. ),
  189. if (post.images![i].isLongImage())
  190. Positioned(
  191. bottom: 4,
  192. right: 4,
  193. child: Container(
  194. padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2),
  195. decoration:
  196. BoxDecoration(color: Colors.black.withOpacity(.8), borderRadius: BorderRadius.all(Radius.circular(20))),
  197. child: Text(
  198. "长图",
  199. style: Theme.of(context).textTheme.bodyText1!.copyWith(color: Colors.white),
  200. ),
  201. ),
  202. )
  203. ],
  204. ))
  205. ],
  206. )
  207. : ClipRRect(
  208. borderRadius: BorderRadius.circular(6),
  209. child: CachedNetworkImage(alignment: Alignment.centerLeft, imageUrl: post.images![i].thumbnail ??"", fit: BoxFit.cover))
  210. : ClipRRect(
  211. borderRadius: BorderRadius.circular(6),
  212. child: Stack(
  213. fit: StackFit.expand,
  214. children: <Widget>[
  215. CachedNetworkImage(
  216. imageUrl: post.images![i].thumbnail ??"",
  217. fit: BoxFit.cover,
  218. ),
  219. if (post.images!.length - 3 > 0)
  220. Container(
  221. color: Color(0x80000000),
  222. child: Center(
  223. child: Text(
  224. "+${post.images!.length - 3}",
  225. style: TextStyle(color: Colors.white, fontSize: 16),
  226. ),
  227. ),
  228. )
  229. ],
  230. ))))
  231. .toList()),
  232. ],
  233. );
  234. }
  235. Widget _postLink(String quote) {
  236. var data = json.decode(quote);
  237. return Container(
  238. color: Theme.of(context).scaffoldBackgroundColor,
  239. child: Row(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: <Widget>[
  240. Icon(
  241. Icons.link,
  242. size: 60.0,
  243. ),
  244. Space(
  245. width: 5.0,
  246. ),
  247. Expanded(
  248. child: RichText(
  249. maxLines: 3,
  250. overflow: TextOverflow.ellipsis,
  251. text: TextSpan(style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16), children: <InlineSpan>[
  252. TextSpan(text: '${data["username"]["value"]}:', style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Theme.of(context).accentColor)),
  253. TextSpan(text: '分享了他的运动记录,快来围观吧~', style: Theme.of(context).textTheme.subtitle1!),
  254. ]),
  255. ),
  256. ),
  257. ]),
  258. );
  259. }
  260. Widget _buildPostDetailWidget(Post post) {
  261. double width = MediaQuery.of(context).size.width - 24;
  262. return Column(
  263. children: <Widget>[
  264. Container(
  265. width: double.infinity,
  266. padding: EdgeInsets.fromLTRB(12.0, 12.0, 12.0, 6),
  267. child: Column(
  268. crossAxisAlignment: CrossAxisAlignment.start,
  269. children: <Widget>[
  270. GestureDetector(
  271. onTap: () => NavigatorUtil.goSocialUserDetail(context, PostUser(id: post.userId, name: post.nickname, avatar: post.avatar)),
  272. child: Row(
  273. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  274. children: <Widget>[
  275. Row(
  276. mainAxisSize: MainAxisSize.min,
  277. children: <Widget>[
  278. CircleAvatar(backgroundColor: Colors.black26,backgroundImage: userAvatarProvider(post.avatar), radius: 18),
  279. Space(
  280. width: 8,
  281. ),
  282. Column(
  283. crossAxisAlignment: CrossAxisAlignment.start,
  284. children: <Widget>[
  285. Row(
  286. children: <Widget>[
  287. Text(
  288. post.nickname ??"",
  289. style: Theme.of(context).textTheme.subtitle1!.copyWith(fontWeight: FontWeight.w600),
  290. ),
  291. if (post.isOfficial == "1")
  292. // Container(
  293. // decoration: BoxDecoration(
  294. // color: Colors.white,
  295. // borderRadius: BorderRadius.all(Radius.circular(2)),
  296. // border: Border.all(
  297. // color: Theme.of(context).accentColor,
  298. // width: .5,
  299. // )),
  300. // padding: EdgeInsets.fromLTRB(2, 0, 2, 1),
  301. // margin: EdgeInsets.fromLTRB(12.0, 0, 12, 0),
  302. // child: Text(
  303. // "官方认证",
  304. // strutStyle: fixedLine,
  305. // style: Theme.of(context).textTheme.subtitle2.copyWith(color: Theme.of(context).accentColor, fontSize: 11),
  306. // ))
  307. Container(
  308. margin: EdgeInsets.only(left: 4.0),
  309. width: 27.0,
  310. height: 14.0,
  311. decoration: BoxDecoration(
  312. image: DecorationImage(image: AssetImage("lib/assets/img/bbs_icon_official.png")),
  313. ),
  314. ),
  315. ],
  316. ),
  317. Space(
  318. height: 4,
  319. ),
  320. Text(DateFormat.formatTime(post.createTime), style: Theme.of(context).textTheme.bodyText1!)
  321. ],
  322. ),
  323. ],
  324. ),
  325. post.isFriend()
  326. ? GestureDetector(
  327. child: Container(
  328. // width: 70,
  329. height: 35,
  330. alignment: Alignment.center,
  331. child: Text(
  332. "已关注",
  333. strutStyle: fixedLine,
  334. style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Color(0xff999999)),
  335. ),
  336. // Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
  337. // Image.asset("lib/assets/img/mine_icon_followed.png"),
  338. // Space(
  339. // width: 6,
  340. // ),
  341. //
  342. // ]),
  343. // decoration: BoxDecoration(
  344. // borderRadius: BorderRadius.circular(20),
  345. // border: Border.all(
  346. // color: Theme.of(context).accentColor,
  347. // width: .5,
  348. // ),
  349. // ),
  350. ),
  351. onTap: () async {
  352. await request(context, () async {
  353. var resp = await model.api.userUnFollow(uid: int.parse(post.userId!)).catchError((onError) {});
  354. if (resp.code == 0) {
  355. post.followStatus = "none";
  356. // widget.userFriends?.firstWhere((element) => element.uid == user.id)?.isFriends = "0";
  357. setState(() {});
  358. ToastUtil.show("取关成功");
  359. }
  360. });
  361. },
  362. )
  363. : post.isMe(Provider.of<UserModel>(context, listen: false).user.id)
  364. ? Container()
  365. :
  366. // PrimaryButton(
  367. // width: 90,
  368. // height: 35,
  369. // callback: () async {
  370. // await request(context, () async {
  371. // var resp = await model.api.userFollow(uid: int.parse(post.userId)).catchError((onError) {});
  372. // if (resp?.code == 0) {
  373. // post.followStatus = "followed";
  374. // // 修改本地的不知道为什么这里不要 先保留...
  375. //// var db = widget.userFriends?.firstWhere((element) => element.uid == user.id);
  376. //// if (db != null) {
  377. //// db.isFriends = "1";
  378. //// db.isIgnore = 0;
  379. //// }
  380. // setState(() {});
  381. // ToastUtil.show("关注成功");
  382. // }
  383. // });
  384. // },
  385. // content: '',
  386. // shadow: false,
  387. // child: Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
  388. // Image.asset("lib/assets/img/mine_icon_follow.png"),
  389. // Space(
  390. // width: 6,
  391. // ),
  392. // Text(
  393. // "关注",
  394. // strutStyle: fixedLine,
  395. // style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Colors.white),
  396. // )
  397. // ]),
  398. // ),
  399. GestureDetector(
  400. child: Container(
  401. width: 70,
  402. height: 30,
  403. alignment: Alignment.center,
  404. child: Text(
  405. "关注",
  406. strutStyle: fixedLine,
  407. style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Theme.of(context).accentColor),
  408. ),
  409. decoration: BoxDecoration(
  410. borderRadius: BorderRadius.circular(20),
  411. border: Border.all(
  412. color: Theme.of(context).accentColor,
  413. width: .5,
  414. ),
  415. ),
  416. ),
  417. onTap: () async {
  418. await request(context, () async {
  419. var resp = await model.api.userFollow(uid: int.parse(post.userId!)).catchError((onError) {});
  420. if (resp.code == 0) {
  421. post.followStatus = "followed";
  422. // 修改本地的不知道为什么这里不要 先保留...
  423. // var db = widget.userFriends?.firstWhere((element) => element.uid == user.id);
  424. // if (db != null) {
  425. // db.isFriends = "1";
  426. // db.isIgnore = 0;
  427. // }
  428. setState(() {});
  429. ToastUtil.show("关注成功");
  430. }
  431. });
  432. })
  433. ],
  434. ),
  435. ),
  436. if (post.title?.isNotEmpty == true)
  437. Padding(
  438. padding: const EdgeInsets.only(top: 12.0),
  439. child: Text(post.title ??"", style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16, fontWeight: FontWeight.w600)),
  440. ),
  441. if (post.content?.isNotEmpty == true)
  442. GestureDetector(
  443. onLongPress: () async {
  444. await showActionDialog(context, {
  445. "复制文字": () {
  446. Clipboard.setData(ClipboardData(text: post.content));
  447. ToastUtil.show("已复制到粘贴板");
  448. }
  449. });
  450. },
  451. child: Padding(
  452. padding: const EdgeInsets.symmetric(vertical: 12.0),
  453. child: Text(
  454. post.content??"",
  455. style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: post.title?.isNotEmpty == true ? 14 : 16),
  456. strutStyle: StrutStyle(height: 1.8, forceStrutHeight: true),
  457. ),
  458. ),
  459. ),
  460. if (post.images?.isNotEmpty == true)
  461. GridView.count(
  462. physics: new NeverScrollableScrollPhysics(),
  463. shrinkWrap: true,
  464. crossAxisSpacing: 10.0,
  465. mainAxisSpacing: 10.0,
  466. childAspectRatio: post.images!.length == 1 ? max(16 / 10, post.images![0].getImageAspectRatio()) : 1,
  467. crossAxisCount: min(3, post.images!.length),
  468. children: post.images!
  469. .asMap()
  470. .keys
  471. .map((imageIndex) => GestureDetector(
  472. onTap: () => open(context, imageIndex, post.images!),
  473. child: Hero(
  474. tag: post.images![imageIndex].id ?? imageIndex,
  475. child: post.images!.length == 1
  476. ? Row(
  477. mainAxisSize: MainAxisSize.min,
  478. children: <Widget>[
  479. ClipRRect(
  480. borderRadius: BorderRadius.circular(6),
  481. child: Stack(
  482. children: <Widget>[
  483. CachedNetworkImage(
  484. alignment: Alignment.centerLeft,
  485. imageUrl: post.images![imageIndex].thumbnail ??"",
  486. fit: BoxFit.cover,
  487. width: post.images![imageIndex].getWidth(width),
  488. ),
  489. if (post.images![imageIndex].isLongImage())
  490. Positioned(
  491. bottom: 4,
  492. right: 4,
  493. child: Container(
  494. padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2),
  495. decoration: BoxDecoration(
  496. color: Colors.black.withOpacity(.8), borderRadius: BorderRadius.all(Radius.circular(20))),
  497. child: Text(
  498. "长图",
  499. style: Theme.of(context).textTheme.bodyText1!.copyWith(color: Colors.white),
  500. ),
  501. ),
  502. )
  503. ],
  504. ))
  505. ],
  506. )
  507. : ClipRRect(
  508. borderRadius: BorderRadius.circular(6),
  509. child: CachedNetworkImage(
  510. imageUrl: post.images![imageIndex].thumbnail ??"",
  511. fit: BoxFit.cover,
  512. ),
  513. )),
  514. ))
  515. .toList(),
  516. ),
  517. if (post.quoteSubject != null)
  518. GestureDetector(
  519. behavior: HitTestBehavior.opaque,
  520. onTap: () {
  521. Navigator.push(context, MaterialPageRoute(builder: (context) {
  522. return PostDetailPage(post.quoteSubject!, false, []);
  523. }));
  524. },
  525. child: Container(
  526. padding: EdgeInsets.all(11.0),
  527. margin: EdgeInsets.symmetric(vertical: 5.0),
  528. decoration: BoxDecoration(
  529. shape: BoxShape.rectangle, borderRadius: BorderRadius.all(Radius.circular(10)), color: Theme.of(context).scaffoldBackgroundColor),
  530. child: _postWidget(post.quoteSubject!),
  531. ),
  532. ),
  533. if (post.quoteData != null)
  534. GestureDetector(
  535. behavior: HitTestBehavior.opaque,
  536. onTap: () {
  537. Navigator.push(context, MaterialPageRoute(builder: (context) {
  538. return WebViewSharePage(
  539. json.decode(post.quoteData!)["url"]["value"],
  540. hash: json.decode(post.quoteData!)["hash"]["value"],
  541. );
  542. }));
  543. },
  544. child: Container(
  545. padding: EdgeInsets.all(11.0),
  546. margin: EdgeInsets.symmetric(vertical: 5.0),
  547. decoration: BoxDecoration(
  548. shape: BoxShape.rectangle, borderRadius: BorderRadius.all(Radius.circular(10)), color: Theme.of(context).scaffoldBackgroundColor),
  549. child: _postLink(post.quoteData!),
  550. ),
  551. ),
  552. Space(
  553. height: 10,
  554. ),
  555. ],
  556. ),
  557. ),
  558. Divider()
  559. ],
  560. );
  561. }
  562. @override
  563. Widget build(BuildContext context) {
  564. return Scaffold(
  565. backgroundColor: Colors.white,
  566. appBar: buildAppBar(
  567. context,
  568. title: "帖子详情",
  569. actions: <Widget>[
  570. PopupMenuTheme(
  571. data: PopupMenuThemeData(shape: PopmenuShape(borderRadius: BorderRadius.all(Radius.circular(10.0)))),
  572. child: PopupMenuButton(
  573. offset: Offset(0, kToolbarHeight / 2 + 15),
  574. icon: iconMoreGray(),
  575. onSelected: (val) async {
  576. switch (val) {
  577. case "report":
  578. await request(context, () async {
  579. await model.api.postForumReport(subjectId: widget.post.id).catchError((onError) {});
  580. ToastUtil.show("举报成功");
  581. });
  582. break;
  583. case "delete":
  584. if (await showDialog(
  585. context: context,
  586. builder: (context) => CustomAlertDialog(title: '确定删除帖子?', ok: () => Navigator.of(context).pop(true)),
  587. ) ==
  588. true) {
  589. var result = await request(context, () async {
  590. await model.api.postDelSubject(widget.post.id!);
  591. ToastUtil.show("删除成功");
  592. return true;
  593. });
  594. if (result == true) {
  595. Navigator.pop(context, true);
  596. }
  597. }
  598. break;
  599. }
  600. },
  601. itemBuilder: (context) {
  602. if (selfId == widget.post.userId) {
  603. return [menuItemCenter('delete', '删除帖子')];
  604. }
  605. return [menuItemCenter('report', '举报帖子')];
  606. },
  607. ))
  608. ],
  609. ),
  610. body: ProviderWidget<PostDetailModel>(
  611. model: model,
  612. onModelReady: (model) => model.initData(),
  613. builder: (_, model, __) {
  614. return NotificationListener(
  615. onNotification: (notification) {
  616. if (notification is CommentNotification) {
  617. if (notification.type == CommentNotification.TYPE_DEL) {
  618. model.list.removeWhere((element) => element.id == notification.comment.id);
  619. setState(() {});
  620. }
  621. _updatePost(1, notification.type);
  622. return true;
  623. }
  624. if (notification is CommentInputNotification) {
  625. // _toComment = notification.comment;
  626. // _toUser = notification.comment.socialInfo;
  627. // readyInput();
  628. showCommentList(context, notification.comment);
  629. return true;
  630. }
  631. return false;
  632. },
  633. child: Column(
  634. children: <Widget>[
  635. Expanded(
  636. flex: 1,
  637. child: EasyRefresh.custom(
  638. controller: model.refreshController,
  639. enableControlFinishRefresh: true,
  640. enableControlFinishLoad: true,
  641. scrollController: _controller,
  642. onRefresh: () => model.refresh(),
  643. onLoad: model.isIdle ? () => model.loadMore() : null,
  644. header: buildClassicalHeader(),
  645. footer: buildClassicalFooter(),
  646. slivers: <Widget>[
  647. SliverToBoxAdapter(
  648. child: _buildPostDetailWidget(widget.post),
  649. ),
  650. if (model.isBusy)
  651. SliverToBoxAdapter(
  652. child: RequestLoadingWidget(),
  653. ),
  654. if (model.isEmpty)
  655. SliverToBoxAdapter(
  656. child: RequestErrorWidget(
  657. null,
  658. msg: "暂无评论~",
  659. assets: RequestErrorWidget.ASSETS_NO_COMMENT,
  660. ),
  661. ),
  662. if (model.isIdle)
  663. SliverPadding(
  664. padding: EdgeInsets.only(bottom: 12),
  665. sliver: SliverToBoxAdapter(
  666. child: Row(
  667. key: _key,
  668. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  669. children: <Widget>[
  670. Padding(
  671. padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 12.0),
  672. child: Text(
  673. "全部评论",
  674. style: Theme.of(context).textTheme.headline3,
  675. ),
  676. ),
  677. PopupMenuButton(
  678. onSelected: (v) {
  679. setState(() {
  680. if(v is String) {
  681. _sortType = v;
  682. model.sortBy(_sortType);
  683. }
  684. });
  685. },
  686. itemBuilder: (BuildContext context) {
  687. return divideMenus(_sort.map((e) => menuItemSelected(e, e, _sortType == e)).toList());
  688. },
  689. child: Row(
  690. mainAxisSize: MainAxisSize.min,
  691. children: <Widget>[
  692. Text(
  693. _sortType,
  694. style: Theme.of(context).textTheme.bodyText1,
  695. ),
  696. Padding(
  697. padding: const EdgeInsets.fromLTRB(6, 6, 12.0, 6),
  698. child: arrowBottom(),
  699. )
  700. ],
  701. ),
  702. ),
  703. ],
  704. ),
  705. )),
  706. if (model.isIdle)
  707. SliverList(
  708. delegate: SliverChildBuilderDelegate(
  709. (context, index) {
  710. return PostCommentWidget(
  711. model.list[index],
  712. );
  713. },
  714. childCount: model.list.length,
  715. ),
  716. ),
  717. ],
  718. ),
  719. ),
  720. if (_inputType == 0)
  721. Container(
  722. decoration: shadowTop(),
  723. child: SafeArea(
  724. child: Container(
  725. height: 50,
  726. child: Row(
  727. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  728. children: <Widget>[
  729. InkWell(
  730. onTap: () {
  731. NavigatorUtil.goPage(context, (context) => PostSharePage(post: _post));
  732. },
  733. child: Row(
  734. children: <Widget>[
  735. SizedBox(
  736. width: 24.0,
  737. ),
  738. Image.asset("lib/assets/img/bbs_icon_share.png"),
  739. _padding,
  740. Text(
  741. "转发",
  742. style: TextStyle(color: Color(0xff333333)),
  743. ),
  744. ],
  745. ),
  746. ),
  747. GestureDetector(
  748. behavior: HitTestBehavior.opaque,
  749. onTap: () async {
  750. Post post = _post;
  751. await (post.isLiked ?? false
  752. ? model.api.postForumUnLike(post.id!, "subject_id")
  753. : model.api.postForumLike(post.id!, "subject_id"));
  754. if (_iconAnimation.status == AnimationStatus.forward || _iconAnimation.status == AnimationStatus.reverse) {
  755. return;
  756. }
  757. setState(() {
  758. _updatePost(2, 0);
  759. });
  760. if (_iconAnimation.status == AnimationStatus.dismissed) {
  761. _animationController.forward();
  762. } else if (_iconAnimation.status == AnimationStatus.completed) {
  763. _animationController.reverse();
  764. }
  765. },
  766. child: Center(
  767. child: Row(
  768. children: <Widget>[
  769. ScaleTransition(
  770. scale: _iconAnimation,
  771. child: Image.asset("lib/assets/img/bbs_icon_like${_post.isLiked ?? false ? "_complete" : ""}.png")),
  772. _padding,
  773. Text(
  774. "${_post.likeCount}",
  775. style: TextStyle(color: Color(0xff333333)),
  776. ),
  777. ],
  778. ),
  779. ),
  780. ),
  781. Padding(
  782. padding: const EdgeInsets.symmetric(horizontal: 12.0),
  783. child: PrimaryButton(
  784. width: 149,
  785. height: 35,
  786. callback: () async {
  787. if (await showBindPhoneDialog(context) == true) {
  788. readyInput();
  789. }
  790. },
  791. content: "发表评论",
  792. ),
  793. )
  794. ],
  795. ),
  796. ),
  797. ),
  798. ),
  799. if (_inputType == 1)
  800. input.TextInput(
  801. widget.post.id!,
  802. focusNode: _focusNode,
  803. toCommentId: _toComment == null ? null : _toComment!.id,
  804. user: _toUser,
  805. callback: () {
  806. resetInput();
  807. // if (_sortType == _sort[0]) {
  808. // //最新评论
  809. // model.isEmpty ? model.refresh() : model.loadMore();
  810. // } else if (model.list.isEmpty) {
  811. // model.initData();
  812. // }
  813. model.initData();
  814. // model.isEmpty ? model.refresh() : model.loadMore();
  815. },
  816. )
  817. ],
  818. ));
  819. }),
  820. );
  821. }
  822. }