import 'dart:convert'; import 'dart:math'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:sport/bean/achievement_info.dart'; import 'package:sport/bean/post.dart'; import 'package:sport/bean/post_user.dart'; import 'package:sport/pages/social/gallery_photo_view.dart'; import 'package:sport/pages/social/post_detail_page.dart'; import 'package:sport/pages/social/share_webview.dart'; import 'package:sport/provider/social_detail_model.dart'; import 'package:sport/router/navigator_util.dart'; import 'package:sport/services/api/inject_api.dart'; import 'package:sport/utils/DateFormat.dart'; import 'package:sport/utils/toast.dart'; import 'package:sport/widgets/dialog/alert_dialog.dart'; import 'package:sport/widgets/dialog/bindphone_dialog.dart'; import 'package:sport/widgets/dialog/modal_bottom_action.dart'; import 'package:sport/widgets/dialog/request_dialog.dart'; import 'package:sport/widgets/image.dart'; import 'package:sport/widgets/menu_share_bottom.dart'; import 'package:sport/widgets/misc.dart'; import 'package:sport/widgets/space.dart'; import 'package:sport/widgets/user_widget.dart'; class PostWidget extends StatefulWidget { final SocialDetailModel model; final Post post; final bool isSelf; final bool jump; // 头像是否可点击 final String? keyword; // 头像是否可点击 final bool highlight; // 头像是否可点击 final bool showForum; // 头像是否可点击 final bool showTopButton; // 头像是否可点击 final Function? callback; // 头像是否可点击 PostWidget(this.post, this.model, this.isSelf, {this.jump = true, this.highlight = false, this.keyword, this.showForum = false, this.showTopButton = false, this.callback}); @override State createState() => _PostWidgetState(); } class _PostWidgetState extends State with InjectApi { @override Widget build(BuildContext context) { return widget.post.isDelete == true ? Container() : _buildPostDetailWidget(context, widget.post, widget.isSelf); } void go(Post post, bool comment) async { // print("${post.toJson()}----------------------------------------"); var result = await Navigator.push(context, MaterialPageRoute(builder: (context) { return PostDetailPage(post, comment, widget.model.list); })); if (result == true) { _delete(widget.model, post); } } bool isExpansion(String text) { TextPainter _textPainter = TextPainter(maxLines: 3, text: TextSpan(text: text, style: TextStyle(fontSize: 16.0)), textDirection: TextDirection.ltr) ..layout(maxWidth: MediaQuery.of(context).size.width - 24); if (_textPainter.didExceedMaxLines) { return true; } else { return false; } } Widget _highlightName(String content) { String keyword = widget.keyword ?? ""; var list = content.split(keyword); List texts = []; for (int i = 0; i < list.length; i++) { texts.add(TextSpan(text: list[i])); if (i < list.length - 1) texts.add(TextSpan(text: keyword, style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Theme.of(context).accentColor))); } return RichText( text: TextSpan(style: Theme.of(context).textTheme.subtitle1!, children: texts), ); } Widget _highlightContent(String content, {int maxLines = 1}) { String keyword = widget.keyword ?? ""; var list = content.split(keyword); List texts = []; for (int i = 0; i < list.length; i++) { texts.add(TextSpan(text: list[i])); if (i < list.length - 1) texts.add(TextSpan(text: keyword, style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16, color: Theme.of(context).accentColor))); } return RichText( maxLines: maxLines, text: TextSpan(style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16), children: texts), ); } Widget _postWidget(Post post) { double width = MediaQuery.of(context).size.width - 24 - 22; // if(post.quoteData != null) print("[post:]${post.toJson()}-------------------------"); // print("[post:]${post.toJson()}----------------------------------------"); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ RichText( maxLines: 3, overflow: TextOverflow.ellipsis, text: TextSpan(style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16), children: [ TextSpan(text: '${post.nickname}:', style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Theme.of(context).accentColor)), TextSpan(text: '${post.content}', style: Theme.of(context).textTheme.subtitle1!), ]), ), if (post.images?.isNotEmpty == true) GridView.count( physics: new NeverScrollableScrollPhysics(), shrinkWrap: true, padding: EdgeInsets.only(top: 15), childAspectRatio: post.images!.length == 1 ? max(16 / 10, post.images![0].getImageAspectRatio()) : 1, crossAxisSpacing: 10.0, crossAxisCount: min(3, post.images!.length), children: post.images! .asMap() .keys .take(min(3, post.images!.length)) .map((i) => GestureDetector( onTap: () => open(context, i, post.images!), child: i < 2 ? post.images!.length == 1 ? Row( mainAxisSize: MainAxisSize.min, children: [ ClipRRect( borderRadius: BorderRadius.circular(6), child: Stack( children: [ CachedNetworkImage( imageUrl: post.images![i].thumbnail ?? "", fit: BoxFit.cover, width: post.images![i].getWidth(width), ), if (post.images![i].isLongImage()) Positioned( bottom: 4, right: 4, child: Container( padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration(color: Colors.black.withOpacity(.8), borderRadius: BorderRadius.all(Radius.circular(20))), child: Text( "长图", style: Theme.of(context).textTheme.bodyText1!.copyWith(color: Colors.white), ), ), ) ], )) ], ) : ClipRRect( borderRadius: BorderRadius.circular(6), child: CachedNetworkImage(imageUrl: post.images![i].thumbnail ?? "", fit: BoxFit.cover)) : ClipRRect( borderRadius: BorderRadius.circular(6), child: Stack( fit: StackFit.expand, children: [ CachedNetworkImage( imageUrl: post.images![i].thumbnail ?? "", fit: BoxFit.cover, ), if (post.images!.length - 3 > 0) Container( color: Color(0x80000000), child: Center( child: Text( "+${post.images!.length - 3}", style: TextStyle(color: Colors.white, fontSize: 16), ), ), ) ], )))) .toList()), if (post.quoteData != null) _postLink(post.quoteData!, color: Colors.white), ], ); } Widget _postLink(String quote, {Color? color}) { var data = json.decode(quote); return Container( padding: EdgeInsets.all(8.0), color: color != null ? color : Color(0xfff5f5f5), child: Row(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [ Icon( Icons.link, size: 60.0, ), Space( width: 5.0, ), Expanded( child: RichText( maxLines: 3, overflow: TextOverflow.ellipsis, text: TextSpan(style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16), children: [ TextSpan(text: '${data["username"]["value"]}:', style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Theme.of(context).accentColor)), TextSpan(text: '分享了他的运动记录,快来围观吧~', style: Theme.of(context).textTheme.subtitle1!), ]), ), ), ]), ); } _delete(SocialDetailModel model, Post post) { model.list.removeWhere((element) => element.id == post.id); setState(() { post.isDelete = true; }); } _deleteF(SocialDetailModel model, Post post) async { if (await showDialog( context: context, builder: (context) => CustomAlertDialog(title: '确定删除帖子?', ok: () => Navigator.of(context).pop(true)), ) == true) { await request(context, () async { await model.api.postDelSubject(post.id!); ToastUtil.show("删除成功"); _delete(model, post); }); } } Widget _buildPostDetailWidget(BuildContext context, Post post, bool isSelf) { SocialDetailModel model = widget.model; double width = MediaQuery.of(context).size.width - 24; return Column( children: [ GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { go(post, false); // NavigatorUtil.goSocialPostDetail(context, post) }, onLongPress: () async { await showActionDialog(context, { if (isSelf == false) "举报帖子": () async { await request(context, () async { await model.api.postForumReport(subjectId: post.id).catchError((onError) {}); ToastUtil.show("举报成功"); }); }, if (isSelf == false) "屏蔽此条": () async { await request(context, () async { await model.api.postForumBlockObject(post.id!, "subject").catchError((onError) {}); ToastUtil.show("屏蔽成功"); _delete(model, post); }); }, "复制文字": () { Clipboard.setData(ClipboardData(text: post.content)); ToastUtil.show("已复制到粘贴板"); } }); }, child: Padding( padding: const EdgeInsets.symmetric(vertical: 12.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ GestureDetector( onTap: widget.jump ? () => NavigatorUtil.goSocialUserDetail(context, PostUser(id: post.userId, name: post.nickname, avatar: post.avatar)) : null, child: Row( children: [ Space( width: 12, ), CircleAvatar(backgroundColor: Colors.black26,backgroundImage: userAvatarProvider(post.avatar), radius: 18), Space( width: 8, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ widget.highlight ? _highlightName(post.nickname!) : Text( post.nickname!, style: Theme.of(context).textTheme.subtitle1!.copyWith(fontWeight: FontWeight.w600), ), UserLevelWidget(level :Level(level: post.userLevel, logo: post.userLevelLogo)), if (post.followStatus == "friends") Container( margin: EdgeInsets.only(left: 2.0), width: 27.0, height: 14.0, decoration: BoxDecoration( // color: Colors.white, // borderRadius: BorderRadius.all(Radius.circular(2)), // border: Border.all( // color: Theme.of(context).accentColor, // width: .5, // ) image: DecorationImage(image: AssetImage("lib/assets/img/bbs_icon_friend.png")), ), // padding: EdgeInsets.fromLTRB(4, 0, 4, 2), // margin: EdgeInsets.fromLTRB(6.0, 0, 6, 0), // child: Text( // "好友", // style: Theme.of(context).textTheme.subtitle2.copyWith(color: Theme.of(context).accentColor, fontSize: 11), // ) ), if (post.isOfficial == "1") Container( margin: EdgeInsets.only(left: 2.0), width: 27.0, height: 14.0, decoration: BoxDecoration( image: DecorationImage(image: AssetImage("lib/assets/img/bbs_icon_official.png")), ), ), ], ), Text(DateFormat.formatTime(post.createTime), style: Theme.of(context).textTheme.bodyText1!) ], ), ], )), widget.showForum && isSelf ? PopupMenuButton( icon: Image.asset( "lib/assets/img/bbs_btn_more.png", height: 12, fit: BoxFit.fitHeight, ), onSelected: (action) async { switch (action) { case "delete": if (await showDialog( context: context, builder: (context) => CustomAlertDialog(title: '确定删除此条?', ok: () => Navigator.of(context).pop(true)), ) == true) { await request(context, () async { await model.api.postDelSubject(post.id!); ToastUtil.show("删除成功"); _delete(model, post); }); } break; case "top": await request(context, () async { if (post.isUserTop == "1") { var resp = await model.api.unsetUserTopSubjects(post.id!).catchError((onError) {}); if (resp.code == 0) { ToastUtil.show("取消置顶成功"); post.isUserTop = "0"; } } else { var resp = await model.api.setUserTopSubjects(post.id!).catchError((onError) {}); if (resp.code == 0) { ToastUtil.show("置顶成功"); post.isUserTop = "1"; } } widget.callback?.call(); // setState(() {}); }); break; } }, itemBuilder: (_) => divideMenus([ menuItemCenter("delete", "删除此条"), if (widget.showTopButton == true) menuItemCenter("top", post.isUserTop == "1" ? "取消置顶" : "置顶帖子"), ]), ) : IconButton( icon: Image.asset("lib/assets/img/btn_close_invitation.png"), onPressed: () async { if (isSelf == false) { if (await showDialog( context: context, builder: (context) => CustomAlertDialog(title: '是否屏蔽此条帖子', ok: () => Navigator.of(context).pop(true)), ) == true) { await request(context, () async { await model.api.postForumBlockObject(post.id!, "subject").catchError((onError) {}); ToastUtil.show("屏蔽成功"); _delete(model, post); }); } } else { _deleteF(model, post); } }, ) ], ), Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(top: 6.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ post.isExpansion ?? false == false ? widget.highlight ? _highlightContent(post.content!, maxLines: 3) : Text(post.content!, maxLines: 3, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16, height: 1.5)) : widget.highlight ? _highlightContent(post.content!) : Text(post.content!, style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 16, height: 1.5)), if (isExpansion(post.content!)) GestureDetector( onTap: () { post.isExpansion = !(post.isExpansion ?? true); setState(() {}); }, child: Padding( padding: const EdgeInsets.only(top: 5.0), child: Row( children: [ Text( post.isExpansion ?? false == false ? "全部" : "收起", style: Theme.of(context).textTheme.bodyText1, strutStyle: fixedLine, ), SizedBox( width: 4, ), post.isExpansion ?? false == false ? arrowBottom() : arrowTop() ], ), )), ], ), ), if (post.images?.isNotEmpty == true) GridView.count( physics: new NeverScrollableScrollPhysics(), shrinkWrap: true, padding: const EdgeInsets.only(top: 12.0), childAspectRatio: post.images!.length == 1 ? max(16 / 10, post.images![0].getImageAspectRatio()) : 1, crossAxisSpacing: 10.0, crossAxisCount: min(3, post.images!.length), children: post.images! .asMap() .keys .take(min(3, post.images!.length)) .map((i) => GestureDetector( onTap: () => i < 2 ? open(context, i, post.images ?? []) : go(post, false), child: i < 2 ? post.images!.length == 1 ? Row( mainAxisSize: MainAxisSize.min, children: [ ClipRRect( borderRadius: BorderRadius.circular(6), child: Stack( children: [ CachedNetworkImage( imageUrl: post.images![i].thumbnail ?? "", fit: BoxFit.cover, width: post.images![i].getWidth(width), ), if (post.images![i].isLongImage()) Positioned( bottom: 4, right: 4, child: Container( padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: Colors.black.withOpacity(.8), borderRadius: BorderRadius.all(Radius.circular(20))), child: Text( "长图", style: Theme.of(context).textTheme.bodyText1!.copyWith(color: Colors.white), ), ), ) ], )) ], ) : ClipRRect( borderRadius: BorderRadius.circular(6), child: CachedNetworkImage(imageUrl: post.images![i].thumbnail ?? "", fit: BoxFit.cover)) : ClipRRect( borderRadius: BorderRadius.circular(6), child: Stack( fit: StackFit.expand, children: [ CachedNetworkImage( imageUrl: post.images![i].thumbnail ?? "", fit: BoxFit.cover, ), if (post.images!.length - 3 > 0) Container( color: Color(0x80000000), child: Center( child: Text( "+${post.images!.length - 3}", style: TextStyle(color: Colors.white, fontSize: 16), ), ), ) ], )))) .toList()), ], ), ), if (post.quoteSubject != null) GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { go(post.quoteSubject!, false); }, child: Container( padding: EdgeInsets.all(11.0), margin: EdgeInsets.symmetric(horizontal: 12.0, vertical: 5.0), decoration: BoxDecoration(shape: BoxShape.rectangle, borderRadius: BorderRadius.all(Radius.circular(10)), color: Color(0xfff5f5f5)), child: _postWidget(post.quoteSubject!), ), ), Row( children: [ // Expanded(child:Text('${post.toJson()}')), if (post.gameName != null && post.gameName != "") Container( padding: EdgeInsets.fromLTRB(12.0, 12.0, 6.0, 0.0), alignment: Alignment.centerLeft, child: Container( padding: EdgeInsets.only(left: 6.0, right: 6.0, bottom: 2.0, top: 1.0), decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(4.0)), color: Color(0xffF1F1F1)), child: Text( post.gameName!, style: TextStyle(color: Color(0xff999999), fontSize: 12.0), strutStyle: StrutStyle(forceStrutHeight: true), ), ), ), if (widget.post.isUserTop == '1') Padding( padding: post.gameName != null && post.gameName != "" ? EdgeInsets.fromLTRB(0.0, 12.0, 12.0, 0.0) : EdgeInsets.fromLTRB(12.0, 12.0, 12.0, 0.0), child: Container( child: Text( "已置顶", style: TextStyle(fontSize: 12, color: Color(0xff5498FF)), strutStyle: fixedLine, ), padding: EdgeInsets.only(left: 6.0, right: 6.0, bottom: 2.0, top: 1.0), decoration: BoxDecoration( borderRadius: BorderRadius.circular(2), border: Border.all( color: Color(0xff5498FF), width: .5, ), ), ), ) ], ), if (post.quoteData != null) GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { var data = json.decode(post.quoteData!); print("$data------------------------------"); NavigatorUtil.goPage( context, (context) => WebViewSharePage( data['url']["value"], hash: data['hash']["value"], )); }, child: Container( padding: EdgeInsets.all(11.0), margin: EdgeInsets.symmetric(horizontal: 12.0, vertical: 5.0), decoration: BoxDecoration(shape: BoxShape.rectangle, borderRadius: BorderRadius.all(Radius.circular(10)), color: Color(0xfff5f5f5)), child: _postLink(post.quoteData!), ), ), Space( height: 15, ), Row( children: [ Expanded( child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { // NavigatorUtil.goPage(context, (context) => PostSharePage(post)); menuShareBottom(context, "social", post: post); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 5.0), child: Center( child: Row( mainAxisSize: MainAxisSize.min, children: [ Image.asset("lib/assets/img/bbslist_icon_share.png"), SizedBox( width: 5, ), Text( "转发", strutStyle: fixedLine, style: Theme.of(context).textTheme.bodyText1!, ), ], ), ), ), ), ), Expanded( child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () async { if (await showBindPhoneDialog(context) == true) { go(post, true); } }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 5.0), child: Center( child: Row( mainAxisSize: MainAxisSize.min, children: [ Image.asset("lib/assets/img/bbslist_icon_reply.png"), SizedBox( width: 5, ), Text( post.commentCount == 0 ? "回复" : "${post.commentCount}", strutStyle: fixedLine, style: Theme.of(context).textTheme.bodyText1!, ), ], ), ), ), ), ), Expanded( child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () async { await (post.isLiked == true ? model.api.postForumUnLike(post.id!, "subject_id") : model.api.postForumLike(post.id!, "subject_id")); setState(() { post.toggleLike(); }); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 5.0), child: Center( child: SizedBox( height: 19, child: Row( crossAxisAlignment: CrossAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: [ Image.asset( "lib/assets/img/bbslist_icon_like${post.isLiked == true ? 'd' : ''}.png", ), const SizedBox( width: 5, ), Text( post.likeCount == 0 ? "点赞" : "${post.likeCount}", strutStyle: fixedLine, style: Theme.of(context).textTheme.bodyText1!, ), ], ), ), ), ), ), ), ], ), ], ), ), ), // if (widget.showForum) // Padding( // padding: const EdgeInsets.only(bottom: 12.0, left: 12.0), // child: Row( // children: [ //// Container( //// child: Text("来自${widget.post.forumName}", style: Theme.of(context).textTheme.bodyText1!), //// padding: EdgeInsets.fromLTRB(8, 0, 8, 1), //// decoration: BoxDecoration(shape: BoxShape.rectangle, borderRadius: BorderRadius.all(Radius.circular(20)), color: Color(0xfff1f1f1)), //// ), //// Space( //// width: 10, //// ), // ], // ), // ), Divider( height: 1, ), ], ); } }