post_detail_page.dart 38 KB

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