detail_bottom.dart 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  1. import 'dart:math';
  2. import 'package:cached_network_image/cached_network_image.dart';
  3. import 'package:flutter/cupertino.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter_easyrefresh/easy_refresh.dart';
  6. import 'package:fluttertoast/fluttertoast.dart';
  7. import 'package:photo_view/photo_view.dart';
  8. import 'package:photo_view/photo_view_gallery.dart';
  9. import 'package:provider/provider.dart';
  10. import 'package:sport/bean/comment.dart';
  11. import 'package:sport/bean/comment_post.dart';
  12. import 'package:sport/bean/image.dart' as photo;
  13. import 'package:sport/pages/social/gallery_photo_view.dart';
  14. import 'package:sport/pages/social/post_comment.dart';
  15. import 'package:sport/provider/game_info_model.dart';
  16. import 'package:sport/provider/lib/provider_widget.dart';
  17. import 'package:sport/provider/user_model.dart';
  18. import 'package:sport/services/api/inject_api.dart';
  19. import 'package:sport/services/api/resp.dart';
  20. import 'package:sport/widgets/box.dart';
  21. import 'package:sport/widgets/decoration.dart';
  22. import 'package:sport/widgets/dialog/bindphone_dialog.dart';
  23. import 'package:sport/widgets/dialog/comment_dialog.dart';
  24. import 'package:sport/widgets/error.dart';
  25. import 'package:sport/widgets/loading.dart';
  26. import 'package:sport/widgets/misc.dart';
  27. import 'package:sport/widgets/persistent_header.dart';
  28. import 'package:sport/widgets/small_label.dart';
  29. import 'package:sport/widgets/text_input.dart';
  30. // Tab 详情 左边的tab页
  31. class TabDetail extends StatelessWidget {
  32. List<String> _images;
  33. List<String> _labels;
  34. dynamic _fileSize;
  35. dynamic _publishDate;
  36. dynamic _developCompany;
  37. TabDetail(this._images, this._labels, this._fileSize, this._publishDate, this._developCompany);
  38. @override
  39. Widget build(BuildContext context) {
  40. double _imageWidth = MediaQuery.of(context).size.width / 4 * 3;
  41. double _imageHeight = _imageWidth * 9 / 16.0;
  42. return SingleChildScrollView(
  43. child: Column(
  44. mainAxisSize: MainAxisSize.min,
  45. crossAxisAlignment: CrossAxisAlignment.start,
  46. children: <Widget>[
  47. Padding(
  48. padding: const EdgeInsets.only(top: 12.0),
  49. child: SizedBox(
  50. height: _imageHeight,
  51. child: ListView.builder(
  52. scrollDirection: Axis.horizontal,
  53. shrinkWrap: true,
  54. itemBuilder: (context, index) {
  55. return GestureDetector(
  56. onTap: () => open(context, index),
  57. child: Padding(
  58. padding: EdgeInsets.only(left: (index == 0 ? 12.0 : 10.0), right: (index == _images.length - 1 ? 12.0 : 0.0)),
  59. child: ClipRRect(
  60. child: CachedNetworkImage(
  61. imageUrl: _images[index],
  62. width: _imageWidth,
  63. height: _imageHeight,
  64. fit: BoxFit.cover,
  65. ),
  66. borderRadius: BorderRadius.circular(10),
  67. ),
  68. ),
  69. );
  70. },
  71. itemCount: _images.length),
  72. ),
  73. ),
  74. SizedBox(
  75. height: 10,
  76. ),
  77. Padding(
  78. padding: EdgeInsets.all(12.0),
  79. child: Column(
  80. crossAxisAlignment: CrossAxisAlignment.start,
  81. children: <Widget>[
  82. Padding(
  83. padding: EdgeInsets.only(bottom: 8.0),
  84. child: Row(
  85. children: <Widget>[
  86. Padding(
  87. padding: EdgeInsets.only(right: 2.0),
  88. child: Text(
  89. "运动标签:",
  90. style: TextStyle(color: Color.fromRGBO(102, 102, 102, 1)),
  91. ),
  92. ),
  93. if (_labels != null)
  94. Wrap(
  95. spacing: 4,
  96. runSpacing: 4,
  97. children: _labels.map((e) => gameTag(context, e)).toList(),
  98. ),
  99. ],
  100. ),
  101. ),
  102. // Padding(
  103. // padding: EdgeInsets.only(bottom: 8.0),
  104. // child: Text("文件大小:${_fileSize}M", style: TextStyle(color: Color.fromRGBO(102, 102, 102, 1))),
  105. // ),
  106. // Padding(
  107. // padding: EdgeInsets.only(bottom: 8.0),
  108. // child: Text("发行时间:$_publishDate", style: TextStyle(color: Color.fromRGBO(102, 102, 102, 1))),
  109. // ),
  110. // Padding(
  111. // padding: EdgeInsets.only(bottom: 8.0),
  112. // child: Text("开发产商:$_developCompany", style: TextStyle(color: Color.fromRGBO(102, 102, 102, 1))),
  113. // ),
  114. ],
  115. ),
  116. )
  117. // 第一个
  118. ],
  119. ),
  120. );
  121. }
  122. void open(BuildContext context, final int index) {
  123. Navigator.push(
  124. context,
  125. FadeRoute(
  126. page: GalleryPhotoViewWrapper(
  127. galleryItems: _images.map((e) => photo.Image(id: "${_images.indexOf(e)}", src: e)).toList(),
  128. backgroundDecoration: const BoxDecoration(
  129. color: Colors.black,
  130. ),
  131. initialIndex: index,
  132. scrollDirection: Axis.horizontal,
  133. ),
  134. ),
  135. );
  136. }
  137. }
  138. // 右边的tab
  139. class TabComment extends StatefulWidget {
  140. final int _subjectId;
  141. TabComment(this._subjectId);
  142. @override
  143. createState() => _TabCommentState();
  144. }
  145. class _TabCommentState extends State<TabComment> with InjectApi {
  146. FocusNode _comment = FocusNode();
  147. String _textFieldValue = "";
  148. late CommentListModel _commentListModel;
  149. @override
  150. initState() {
  151. _commentListModel = new CommentListModel('${widget._subjectId}', by: 'created_at');
  152. super.initState();
  153. }
  154. Widget build(BuildContext context) {
  155. return ProviderWidget<CommentListModel>(
  156. model: _commentListModel,
  157. onModelReady: (model) => model.initData(),
  158. builder: (_, model, __) {
  159. return EasyRefresh.custom(
  160. controller: model.refreshController,
  161. enableControlFinishRefresh: true,
  162. enableControlFinishLoad: true,
  163. onRefresh: () => model.refresh(),
  164. onLoad: model.isIdle ? () => model.loadMore() : null,
  165. header: buildClassicalHeader(),
  166. footer: buildClassicalFooter(),
  167. slivers: [
  168. SliverPersistentHeader(
  169. delegate: PersistentHeader(
  170. min: 100,
  171. max: 100,
  172. child: Container(
  173. height: 100,
  174. margin: EdgeInsets.fromLTRB(12, 12, 12, 24),
  175. padding: EdgeInsets.symmetric(horizontal: 12, vertical: 12.0),
  176. decoration: card(),
  177. child: InkWell(
  178. child: Row(
  179. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  180. crossAxisAlignment: CrossAxisAlignment.center,
  181. children: <Widget>[
  182. Row(
  183. children: <Widget>[
  184. Consumer<UserModel>(
  185. builder: (_, model, __) {
  186. return model.user == null
  187. ? Container()
  188. : Row(
  189. children: <Widget>[
  190. CircleAvatar(backgroundColor: Colors.black26,radius: 15, backgroundImage: CachedNetworkImageProvider(model.user.avatar ?? "")),
  191. SizedBox(
  192. width: 12,
  193. ),
  194. Text(model.user.name, style: Theme.of(context).textTheme.subtitle1!.copyWith(fontWeight: FontWeight.w600))
  195. ],
  196. );
  197. },
  198. ),
  199. ],
  200. ),
  201. Row(
  202. children: <Widget>[
  203. Padding(
  204. padding: EdgeInsets.only(right: 0.0),
  205. child: Text(
  206. "发表评论",
  207. style: TextStyle(color: Color.fromRGBO(51, 51, 51, 1)),
  208. ),
  209. ),
  210. Icon(Icons.keyboard_arrow_right)
  211. ],
  212. )
  213. ],
  214. ),
  215. onTap: () async {
  216. if (await showBindPhoneDialog(context) != true) {
  217. return;
  218. }
  219. // 从下面弹出个框来 输入 评论
  220. showModalBottomSheet(
  221. context: context,
  222. isScrollControlled: true, // !important
  223. builder: (BuildContext context) {
  224. return SingleChildScrollView(
  225. // !important
  226. child: Container(
  227. padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), // !important
  228. child: TextInput(
  229. '${widget._subjectId}',
  230. focusNode: _comment,
  231. autoFocus: true,
  232. callback: () {
  233. model.refresh();
  234. Navigator.pop(context);
  235. },
  236. )),
  237. );
  238. },
  239. );
  240. },
  241. ),
  242. )),
  243. pinned: true,
  244. ),
  245. if (model.isBusy)
  246. SliverToBoxAdapter(
  247. child: RequestLoadingWidget(),
  248. ),
  249. if (model.isIdle && model.list.isNotEmpty)
  250. SliverList(
  251. delegate: SliverChildBuilderDelegate((context, index) {
  252. return PostCommentWidget(model.list[index]);
  253. // getComment(
  254. // 1,
  255. // model.list[index],
  256. // // initData: model.initData(),
  257. // );
  258. }, childCount: model.list.length)),
  259. if (model.isEmpty)
  260. SliverFillRemaining(
  261. child: Center(
  262. child: RequestErrorWidget(
  263. null,
  264. msg: "暂无评论~",
  265. assets: RequestErrorWidget.ASSETS_NO_COMMENT,
  266. ),
  267. ),
  268. ),
  269. ]);
  270. },
  271. );
  272. }
  273. }
  274. // type 1 bottom
  275. // type 2 head
  276. class getComment extends StatefulWidget {
  277. int type;
  278. String textType = "big";
  279. Function? initData;
  280. Comment comment;
  281. getComment(this.type, this.comment, {this.initData});
  282. @override
  283. State<StatefulWidget> createState() {
  284. // TODO: implement createState
  285. return _getCommentState();
  286. }
  287. }
  288. class _getCommentState extends State<getComment> with InjectApi {
  289. FocusNode _comment = new FocusNode();
  290. String _textFieldValue = "";
  291. @override
  292. void initState() {
  293. // TODO: implement initState
  294. super.initState();
  295. }
  296. void handleLike({int? type}) async {
  297. if (type == 1) {
  298. RespData<String> _data = await api.postForumLike(widget.comment.id!, "comment_id");
  299. } else if (type == 2) {
  300. RespData<String> _data = await api.postForumUnLike(widget.comment.id!, "comment_id");
  301. }
  302. }
  303. @override
  304. Widget build(BuildContext context) {
  305. // TODO: implement build
  306. return Container(
  307. padding: EdgeInsets.only(left: 12.0, right: 12.0, top: 16.0),
  308. child: Column(
  309. children: <Widget>[
  310. // avatar
  311. Padding(
  312. padding: EdgeInsets.only(bottom: 9.0),
  313. child: Row(
  314. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  315. children: <Widget>[
  316. ClipRRect(
  317. child: CachedNetworkImage(
  318. imageUrl: widget.comment.socialInfo?.avatar ?? "",
  319. width: 40.0,
  320. height: 40.0,
  321. fit: BoxFit.cover,
  322. ),
  323. borderRadius: BorderRadius.all(Radius.circular(50.0)),
  324. ),
  325. Expanded(
  326. child: Container(
  327. padding: EdgeInsets.only(left: 12.0),
  328. alignment: Alignment.centerLeft,
  329. child: Column(
  330. children: <Widget>[
  331. Container(
  332. width: double.infinity,
  333. alignment: Alignment.centerLeft,
  334. child: Text(
  335. "${widget.comment.socialInfo?.name}",
  336. style: TextStyle(color: Color.fromRGBO(51, 51, 51, 1), fontWeight: FontWeight.bold),
  337. ),
  338. ),
  339. Container(
  340. padding: EdgeInsets.only(top: 5.0),
  341. width: double.infinity,
  342. alignment: Alignment.centerLeft,
  343. child: Text("${widget.comment.createdAt}", style: TextStyle(color: Color.fromRGBO(153, 153, 153, 1), fontSize: 12.0)),
  344. ),
  345. ],
  346. ),
  347. ),
  348. ),
  349. widget.type == 2
  350. ? Padding(
  351. padding: EdgeInsets.only(left: 12.0),
  352. child: Row(
  353. children: <Widget>[
  354. widget.comment.isLiked == true
  355. ? InkWell(
  356. child: Image.asset(
  357. "lib/assets/img/bbslist_icon_liked.png",
  358. width: 16.0,
  359. height: 16.0,
  360. ),
  361. onTap: () {
  362. handleLike(type: 2);
  363. widget.initData?.call();
  364. },
  365. )
  366. : InkWell(
  367. child: Image.asset(
  368. "lib/assets/img/bbslist_icon_like.png",
  369. width: 16.0,
  370. height: 16.0,
  371. ),
  372. onTap: () {
  373. handleLike(type: 1);
  374. widget.initData?.call();
  375. },
  376. ),
  377. Padding(
  378. padding: EdgeInsets.only(left: 5.0),
  379. child: Text(
  380. "${widget.comment.likeCount}",
  381. style: TextStyle(color: Color.fromRGBO(153, 153, 153, 1), fontSize: 12.0),
  382. ),
  383. )
  384. ],
  385. ),
  386. )
  387. : Container()
  388. ],
  389. )),
  390. // comment-content
  391. Container(
  392. width: double.infinity,
  393. alignment: Alignment.centerLeft,
  394. child: Text(
  395. "${widget.comment.content}",
  396. textAlign: TextAlign.left,
  397. style: TextStyle(fontSize: widget.textType == "big" ? 16.0 : 14.0, fontWeight: FontWeight.w400, height: 1.5, color: Colors.black),
  398. ),
  399. ),
  400. // comment-bottom
  401. widget.type == 1
  402. ? Padding(
  403. padding: EdgeInsets.only(top: 12.0),
  404. child: Row(
  405. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  406. children: <Widget>[
  407. widget.comment.subList?.isNotEmpty == true
  408. ? Row(
  409. children: <Widget>[
  410. InkWell(
  411. child: Text("查看${widget.comment.subList!.length}条回复",
  412. style: TextStyle(fontSize: 12.0, fontWeight: FontWeight.w400, color: Color.fromRGBO(153, 153, 153, 1))),
  413. onTap: () async {
  414. showCommentList(context, widget.comment);
  415. // if (widget.comment.subList.length != 0) {
  416. // var data = await showModalBottomSheet(
  417. // context: context,
  418. // isScrollControlled: true,
  419. // shape: RoundedRectangleBorder(
  420. // borderRadius: BorderRadius.vertical(
  421. // top: Radius.circular(30))),
  422. // builder: (BuildContext context) {
  423. // return SingleChildScrollView(
  424. // child: Container(
  425. // height: MediaQuery.of(context)
  426. // .size
  427. // .height /
  428. // 3 *
  429. // 2,
  430. // padding: EdgeInsets.only(
  431. // bottom: MediaQuery.of(
  432. // context)
  433. // .viewInsets
  434. // .bottom), // !important
  435. // child: MaskComment(
  436. // widget.comment.id,
  437. // widget.comment.subjectId,
  438. // widget.comment.id,
  439. // widget.initData,
  440. // widget.comment,
  441. // )),
  442. // );
  443. // },
  444. // );
  445. // if (data == null) {
  446. // widget.initData();
  447. // }
  448. // }
  449. // else {
  450. // Fluttertoast.showToast(
  451. // msg: "没有可查看的回复",
  452. // toastLength: Toast.LENGTH_SHORT,
  453. // gravity: ToastGravity.CENTER,
  454. // timeInSecForIosWeb: 1,
  455. // backgroundColor: Colors.white,
  456. // textColor: Colors.black,
  457. // fontSize: 16.0);
  458. // }
  459. },
  460. ),
  461. Icon(
  462. Icons.chevron_right,
  463. color: Color.fromRGBO(153, 153, 153, 1),
  464. size: 16.0,
  465. )
  466. ],
  467. )
  468. : Container(),
  469. Row(
  470. children: <Widget>[
  471. Padding(
  472. padding: EdgeInsets.only(right: 25.0),
  473. child: Row(
  474. children: <Widget>[
  475. widget.comment.isLiked == true
  476. ? InkWell(
  477. child: Image.asset(
  478. "lib/assets/img/bbslist_icon_liked.png",
  479. width: 16.0,
  480. height: 16.0,
  481. ),
  482. onTap: () {
  483. handleLike(type: 2);
  484. widget.initData?.call();
  485. },
  486. )
  487. : InkWell(
  488. child: Image.asset(
  489. "lib/assets/img/bbslist_icon_like.png",
  490. width: 16.0,
  491. height: 16.0,
  492. ),
  493. onTap: () {
  494. handleLike(type: 1);
  495. widget.initData?.call();
  496. },
  497. ),
  498. Padding(
  499. padding: EdgeInsets.only(left: 5.0),
  500. child: Text(
  501. "${widget.comment.likeCount}",
  502. style: TextStyle(color: Color.fromRGBO(153, 153, 153, 1), fontSize: 12.0),
  503. ),
  504. )
  505. ],
  506. ),
  507. ),
  508. Row(
  509. children: <Widget>[
  510. Image.asset(
  511. "lib/assets/img/bbslist_icon_reply.png",
  512. width: 16.0,
  513. height: 16.0,
  514. ),
  515. InkWell(
  516. child: Padding(
  517. padding: EdgeInsets.only(left: 5.0),
  518. child: Text(
  519. "回复",
  520. style: TextStyle(color: Color.fromRGBO(153, 153, 153, 1), fontSize: 12.0),
  521. ),
  522. ),
  523. onTap: () {
  524. // showModalBottomSheet(
  525. // context: context,
  526. // isScrollControlled: true, // !important
  527. // builder: (BuildContext context) {
  528. // return SingleChildScrollView(
  529. // // !important
  530. // child: Container(
  531. // padding: EdgeInsets.only(
  532. // bottom: MediaQuery.of(context)
  533. // .viewInsets
  534. // .bottom), // !important
  535. // child: Container(
  536. // width: 351.0,
  537. // height: 44.0,
  538. // child: Padding(
  539. // padding: EdgeInsets.symmetric(
  540. // horizontal: 15.0),
  541. // child: Row(
  542. // mainAxisAlignment:
  543. // MainAxisAlignment
  544. // .spaceBetween,
  545. // crossAxisAlignment:
  546. // CrossAxisAlignment.center,
  547. // children: <Widget>[
  548. // Image.asset(
  549. // "lib/assets/img/bbs_icon_report.png",
  550. // width: 20.0,
  551. // height: 20.0,
  552. // ),
  553. // Expanded(
  554. // child: Container(
  555. // padding:
  556. // EdgeInsets.only(
  557. // left: 15.0),
  558. // alignment: Alignment
  559. // .centerLeft,
  560. // child: TextField(
  561. // focusNode: _comment,
  562. // autofocus: true,
  563. // style: TextStyle(
  564. // color: Color
  565. // .fromRGBO(
  566. // 153,
  567. // 153,
  568. // 153,
  569. // 1),
  570. // fontSize: 14.0),
  571. // onChanged: (value) {
  572. // setState(() {
  573. // _textFieldValue =
  574. // value;
  575. // });
  576. // },
  577. // decoration:
  578. // InputDecoration(
  579. // hintText:
  580. // '发表你的看法',
  581. // border:
  582. // InputBorder
  583. // .none),
  584. // )),
  585. // ),
  586. // InkWell(
  587. // child: Container(
  588. // width: 75.0,
  589. // height: 35.0,
  590. // alignment:
  591. // Alignment.center,
  592. // child: Text(
  593. // "发送",
  594. // style: TextStyle(
  595. // color:
  596. // Colors.white),
  597. // ),
  598. // decoration:
  599. // BoxDecoration(
  600. // borderRadius:
  601. // BorderRadius.all(
  602. // Radius
  603. // .circular(
  604. // 20.0)),
  605. // border: new Border
  606. // .all(
  607. // width: 1,
  608. // color: Theme.of(
  609. // context)
  610. // .accentColor),
  611. // gradient: LinearGradient(
  612. // begin: Alignment
  613. // .topCenter,
  614. // end: Alignment
  615. // .bottomCenter,
  616. // colors: [
  617. // Color.fromRGBO(
  618. // 255,
  619. // 196,
  620. // 0,
  621. // 1),
  622. // Color.fromRGBO(
  623. // 255,
  624. // 170,
  625. // 0,
  626. // 1),
  627. // ]),
  628. // ),
  629. // ),
  630. // onTap: () async {
  631. // RespData<CommentPost>
  632. // commentData =
  633. // await api.postForumComment(
  634. // "${widget.comment.subjectId}",
  635. // _textFieldValue,
  636. // toCommentId:
  637. // widget
  638. // .comment
  639. // .id,
  640. // parentCommentId:
  641. // widget
  642. // .comment
  643. // .id);
  644. // print(commentData);
  645. // print(commentData.code);
  646. // if (commentData.code !=
  647. // 0) {
  648. // Fluttertoast.showToast(
  649. // msg:
  650. // "${commentData.msg}",
  651. // toastLength: Toast
  652. // .LENGTH_SHORT,
  653. // gravity:
  654. // ToastGravity
  655. // .CENTER,
  656. // backgroundColor:
  657. // Colors.white,
  658. // textColor:
  659. // Colors.black,
  660. // fontSize: 13.0);
  661. // } else {
  662. // widget.initData();
  663. // Navigator.pop(
  664. // context);
  665. // }
  666. // },
  667. // )
  668. // ],
  669. // ),
  670. // ),
  671. // )),
  672. // );
  673. // },
  674. // );
  675. showCommentList(context, widget.comment);
  676. },
  677. ),
  678. ],
  679. ),
  680. ],
  681. ),
  682. ],
  683. ),
  684. )
  685. : Padding(
  686. padding: EdgeInsets.only(),
  687. ),
  688. Padding(
  689. padding: EdgeInsets.only(top: 17.0),
  690. child: Divider(),
  691. )
  692. ],
  693. ),
  694. );
  695. }
  696. }