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_easyrefresh/easy_refresh.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:photo_view/photo_view.dart'; import 'package:photo_view/photo_view_gallery.dart'; import 'package:provider/provider.dart'; import 'package:sport/bean/comment.dart'; import 'package:sport/bean/comment_post.dart'; import 'package:sport/pages/social/post_comment.dart'; import 'package:sport/provider/game_info_model.dart'; import 'package:sport/provider/lib/provider_widget.dart'; import 'package:sport/provider/user_model.dart'; import 'package:sport/services/api/inject_api.dart'; import 'package:sport/services/api/resp.dart'; import 'package:sport/widgets/box.dart'; import 'package:sport/widgets/decoration.dart'; import 'package:sport/widgets/dialog/bindphone_dialog.dart'; import 'package:sport/widgets/dialog/comment_dialog.dart'; import 'package:sport/widgets/error.dart'; import 'package:sport/widgets/loading.dart'; import 'package:sport/widgets/misc.dart'; import 'package:sport/widgets/persistent_header.dart'; import 'package:sport/widgets/small_label.dart'; import 'package:sport/widgets/text_input.dart'; class DetailBottom extends StatelessWidget { TabController _controller; List _images; List _labels; dynamic _fileSize; dynamic _publishDate; dynamic _developCompany; int _subjectId; DetailBottom(this._controller, this._images, this._labels, this._fileSize, this._publishDate, this._developCompany, this._subjectId); @override Widget build(BuildContext context) { return TabBarView( controller: _controller, children: [ TabDetail(_images, _labels, _fileSize, _publishDate, _developCompany), TabComment(_subjectId), ], ); } } // Tab 详情 左边的tab页 class TabDetail extends StatelessWidget { List _images; List _labels; dynamic _fileSize; dynamic _publishDate; dynamic _developCompany; TabDetail(this._images, this._labels, this._fileSize, this._publishDate, this._developCompany); @override Widget build(BuildContext context) { return SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(top: 12.0), child: SizedBox( height: 260, child: ListView.builder( scrollDirection: Axis.horizontal, shrinkWrap: true, itemBuilder: (context, index) { return GestureDetector( onTap: () => open(context, index), child: Padding( padding: EdgeInsets.only(left: (index == 0 ? 12.0 : 10.0), right: (index == _images.length - 1 ? 12.0 : 0.0)), child: ClipRRect( child: CachedNetworkImage( imageUrl: _images[index], width: 170, height: 226, fit: BoxFit.cover, ), borderRadius: BorderRadius.circular(10), ), ), ); }, itemCount: _images.length), ), ), SizedBox(height: 10,), Padding( padding: EdgeInsets.all(12.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.only(bottom: 8.0), child: Row( children: [ Padding( padding: EdgeInsets.only(right: 2.0), child: Text( "运动标签:", style: TextStyle(color: Color.fromRGBO(102, 102, 102, 1)), ), ), if (_labels != null) Wrap( spacing: 4, runSpacing: 4, children: _labels.map((e) => gameTag(context, e)).toList(), ), ], ), ), Padding( padding: EdgeInsets.only(bottom: 8.0), child: Text("文件大小:${_fileSize}M", style: TextStyle(color: Color.fromRGBO(102, 102, 102, 1))), ), Padding( padding: EdgeInsets.only(bottom: 8.0), child: Text("发行时间:$_publishDate", style: TextStyle(color: Color.fromRGBO(102, 102, 102, 1))), ), Padding( padding: EdgeInsets.only(bottom: 8.0), child: Text("开发产商:$_developCompany", style: TextStyle(color: Color.fromRGBO(102, 102, 102, 1))), ), ], ), ) // 第一个 ], ), ); } void open(BuildContext context, final int index) { Navigator.push( context, FadeRoute( page: GalleryPhotoViewWrapper( galleryItems: _images, backgroundDecoration: const BoxDecoration( color: Colors.black, ), initialIndex: index, scrollDirection: Axis.horizontal, ), ), ); } } // 右边的tab class TabComment extends StatefulWidget { final int _subjectId; TabComment(this._subjectId); @override createState() => _TabCommentState(); } class _TabCommentState extends State with InjectApi { FocusNode _comment = FocusNode(); String _textFieldValue = ""; CommentListModel _commentListModel; @override initState() { _commentListModel = new CommentListModel('${widget._subjectId}', by: 'created_at'); super.initState(); } Widget build(BuildContext context) { return ProviderWidget( model: _commentListModel, onModelReady: (model) => model.initData(), builder: (_, model, __) { return EasyRefresh.custom( controller: model.refreshController, enableControlFinishRefresh: true, enableControlFinishLoad: true, onRefresh: () => model.refresh(), onLoad: model.isIdle ? () => model.loadMore() : null, header: buildClassicalHeader(), footer: buildClassicalFooter(), slivers: [ SliverPersistentHeader( delegate: PersistentHeader( min: 100, max: 100, child: Container( height: 100, margin: EdgeInsets.fromLTRB(12,12,12,24), padding: EdgeInsets.symmetric(horizontal: 12, vertical: 12.0), decoration: card(), child: InkWell( child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ Row( children: [ Consumer( builder: (_, model, __) { return model.user == null ? Container() : Row( children: [ CircleAvatar(radius: 15, backgroundImage: CachedNetworkImageProvider(model.user.avatar)), SizedBox( width: 12, ), Text(model.user.name, style: Theme.of(context).textTheme.subtitle1.copyWith(fontWeight: FontWeight.w600)) ], ); }, ), ], ), Row( children: [ Padding( padding: EdgeInsets.only(right: 0.0), child: Text( "发表评论", style: TextStyle(color: Color.fromRGBO(51, 51, 51, 1)), ), ), Icon(Icons.keyboard_arrow_right) ], ) ], ), onTap: ()async { if (await showBindPhoneDialog(context) != true) { return; } // 从下面弹出个框来 输入 评论 showModalBottomSheet( context: context, isScrollControlled: true, // !important builder: (BuildContext context) { return SingleChildScrollView( // !important child: Container( padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), // !important child: TextInput( '${widget._subjectId}', focusNode: _comment, autoFocus: true, callback: () { model.refresh(); Navigator.pop(context); }, )), ); }, ); }, ), )), pinned: true, ), if (model.isBusy) SliverToBoxAdapter( child: RequestLoadingWidget(), ), if (model.isIdle && model.list.isNotEmpty) SliverList( delegate: SliverChildBuilderDelegate((context, index) { return PostCommentWidget(model.list[index]); // getComment( // 1, // model.list[index], // // initData: model.initData(), // ); }, childCount: model.list.length)), if (model.isEmpty) SliverFillRemaining( child: Center( child: RequestErrorWidget( null, msg: "暂无评论~", assets: RequestErrorWidget.ASSETS_NO_COMMENT, ), ), ), ]); }, ); } } // type 1 bottom // type 2 head class getComment extends StatefulWidget { int type; String textType = "big"; Function initData; Comment comment; getComment(this.type, this.comment, {this.initData}); @override State createState() { // TODO: implement createState return _getCommentState(); } } class _getCommentState extends State with InjectApi { FocusNode _comment = new FocusNode(); String _textFieldValue = ""; @override void initState() { // TODO: implement initState super.initState(); } void handleLike({int type}) async { if (type == 1) { RespData _data = await api.postForumLike(widget.comment.id, "comment_id"); } else if (type == 2) { RespData _data = await api.postForumUnLike(widget.comment.id, "comment_id"); } } @override Widget build(BuildContext context) { // TODO: implement build return Container( padding: EdgeInsets.only(left: 12.0, right: 12.0, top: 16.0), child: Column( children: [ // avatar Padding( padding: EdgeInsets.only(bottom: 9.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ ClipRRect( child: CachedNetworkImage( imageUrl: widget.comment.socialInfo.avatar == "" ? "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2661558467,1288211245&fm=26&gp=0.jpg" : widget.comment.socialInfo.avatar, width: 40.0, height: 40.0, fit: BoxFit.cover, ), borderRadius: BorderRadius.all(Radius.circular(50.0)), ), Expanded( child: Container( padding: EdgeInsets.only(left: 12.0), alignment: Alignment.centerLeft, child: Column( children: [ Container( width: double.infinity, alignment: Alignment.centerLeft, child: Text( "${widget.comment.socialInfo.name}", style: TextStyle(color: Color.fromRGBO(51, 51, 51, 1), fontWeight: FontWeight.bold), ), ), Container( padding: EdgeInsets.only(top: 5.0), width: double.infinity, alignment: Alignment.centerLeft, child: Text("${widget.comment.createdAt}", style: TextStyle(color: Color.fromRGBO(153, 153, 153, 1), fontSize: 12.0)), ), ], ), ), ), widget.type == 2 ? Padding( padding: EdgeInsets.only(left: 12.0), child: Row( children: [ widget.comment.isLiked ? InkWell( child: Image.asset( "lib/assets/img/bbslist_icon_liked.png", width: 16.0, height: 16.0, ), onTap: () { handleLike(type: 2); widget.initData(); }, ) : InkWell( child: Image.asset( "lib/assets/img/bbslist_icon_like.png", width: 16.0, height: 16.0, ), onTap: () { handleLike(type: 1); widget.initData(); }, ), Padding( padding: EdgeInsets.only(left: 5.0), child: Text( "${widget.comment.likeCount}", style: TextStyle(color: Color.fromRGBO(153, 153, 153, 1), fontSize: 12.0), ), ) ], ), ) : Container() ], )), // comment-content Container( width: double.infinity, alignment: Alignment.centerLeft, child: Text( "${widget.comment.content}", textAlign: TextAlign.left, style: TextStyle(fontSize: widget.textType == "big" ? 16.0 : 14.0, fontWeight: FontWeight.w400, height: 1.5, color: Colors.black), ), ), // comment-bottom widget.type == 1 ? Padding( padding: EdgeInsets.only(top: 12.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ widget.comment.subList.length != 0 ? Row( children: [ InkWell( child: Text("查看${widget.comment.subList.length}条回复", style: TextStyle(fontSize: 12.0, fontWeight: FontWeight.w400, color: Color.fromRGBO(153, 153, 153, 1))), onTap: () async { _showCommentList(widget.comment, subjectId: widget.comment.id); // if (widget.comment.subList.length != 0) { // var data = await showModalBottomSheet( // context: context, // isScrollControlled: true, // shape: RoundedRectangleBorder( // borderRadius: BorderRadius.vertical( // top: Radius.circular(30))), // builder: (BuildContext context) { // return SingleChildScrollView( // child: Container( // height: MediaQuery.of(context) // .size // .height / // 3 * // 2, // padding: EdgeInsets.only( // bottom: MediaQuery.of( // context) // .viewInsets // .bottom), // !important // child: MaskComment( // widget.comment.id, // widget.comment.subjectId, // widget.comment.id, // widget.initData, // widget.comment, // )), // ); // }, // ); // if (data == null) { // widget.initData(); // } // } // else { // Fluttertoast.showToast( // msg: "没有可查看的回复", // toastLength: Toast.LENGTH_SHORT, // gravity: ToastGravity.CENTER, // timeInSecForIosWeb: 1, // backgroundColor: Colors.white, // textColor: Colors.black, // fontSize: 16.0); // } }, ), Icon( Icons.chevron_right, color: Color.fromRGBO(153, 153, 153, 1), size: 16.0, ) ], ) : Container(), Row( children: [ Padding( padding: EdgeInsets.only(right: 25.0), child: Row( children: [ widget.comment.isLiked ? InkWell( child: Image.asset( "lib/assets/img/bbslist_icon_liked.png", width: 16.0, height: 16.0, ), onTap: () { handleLike(type: 2); widget.initData(); }, ) : InkWell( child: Image.asset( "lib/assets/img/bbslist_icon_like.png", width: 16.0, height: 16.0, ), onTap: () { handleLike(type: 1); widget.initData(); }, ), Padding( padding: EdgeInsets.only(left: 5.0), child: Text( "${widget.comment.likeCount}", style: TextStyle(color: Color.fromRGBO(153, 153, 153, 1), fontSize: 12.0), ), ) ], ), ), Row( children: [ Image.asset( "lib/assets/img/bbslist_icon_reply.png", width: 16.0, height: 16.0, ), InkWell( child: Padding( padding: EdgeInsets.only(left: 5.0), child: Text( "回复", style: TextStyle(color: Color.fromRGBO(153, 153, 153, 1), fontSize: 12.0), ), ), onTap: () { // showModalBottomSheet( // context: context, // isScrollControlled: true, // !important // builder: (BuildContext context) { // return SingleChildScrollView( // // !important // child: Container( // padding: EdgeInsets.only( // bottom: MediaQuery.of(context) // .viewInsets // .bottom), // !important // child: Container( // width: 351.0, // height: 44.0, // child: Padding( // padding: EdgeInsets.symmetric( // horizontal: 15.0), // child: Row( // mainAxisAlignment: // MainAxisAlignment // .spaceBetween, // crossAxisAlignment: // CrossAxisAlignment.center, // children: [ // Image.asset( // "lib/assets/img/bbs_icon_report.png", // width: 20.0, // height: 20.0, // ), // Expanded( // child: Container( // padding: // EdgeInsets.only( // left: 15.0), // alignment: Alignment // .centerLeft, // child: TextField( // focusNode: _comment, // autofocus: true, // style: TextStyle( // color: Color // .fromRGBO( // 153, // 153, // 153, // 1), // fontSize: 14.0), // onChanged: (value) { // setState(() { // _textFieldValue = // value; // }); // }, // decoration: // InputDecoration( // hintText: // '发表你的看法', // border: // InputBorder // .none), // )), // ), // InkWell( // child: Container( // width: 75.0, // height: 35.0, // alignment: // Alignment.center, // child: Text( // "发送", // style: TextStyle( // color: // Colors.white), // ), // decoration: // BoxDecoration( // borderRadius: // BorderRadius.all( // Radius // .circular( // 20.0)), // border: new Border // .all( // width: 1, // color: Theme.of( // context) // .accentColor), // gradient: LinearGradient( // begin: Alignment // .topCenter, // end: Alignment // .bottomCenter, // colors: [ // Color.fromRGBO( // 255, // 196, // 0, // 1), // Color.fromRGBO( // 255, // 170, // 0, // 1), // ]), // ), // ), // onTap: () async { // RespData // commentData = // await api.postForumComment( // "${widget.comment.subjectId}", // _textFieldValue, // toCommentId: // widget // .comment // .id, // parentCommentId: // widget // .comment // .id); // print(commentData); // print(commentData.code); // if (commentData.code != // 0) { // Fluttertoast.showToast( // msg: // "${commentData.msg}", // toastLength: Toast // .LENGTH_SHORT, // gravity: // ToastGravity // .CENTER, // backgroundColor: // Colors.white, // textColor: // Colors.black, // fontSize: 13.0); // } else { // widget.initData(); // Navigator.pop( // context); // } // }, // ) // ], // ), // ), // )), // ); // }, // ); _showCommentList(widget.comment, subjectId: widget.comment.id); }, ), ], ), ], ), ], ), ) : Padding( padding: EdgeInsets.only(), ), Padding( padding: EdgeInsets.only(top: 17.0), child: Divider(), ) ], ), ); } void _showCommentList(Comment currentItem, {bool comment = false, String subjectId}) { showModalBottomSheet( context: context, isScrollControlled: true, shape: RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(30))), builder: (BuildContext context) { return SingleChildScrollView( child: Container( height: max(520, MediaQuery.of(context).size.height / 3 * 2), padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), // !important child: CommentDialog(subjectId, currentItem, showInput: comment), ), ); }, ).then((val) { print(val); }); } } class MaskComment extends StatefulWidget { String parentCommentId; String id; String subjectId; Function initData; Comment comment; MaskComment(this.parentCommentId, this.subjectId, this.id, this.initData, this.comment); @override State createState() { // TODO: implement createState return _MaskCommentState(); } } class _MaskCommentState extends State with InjectApi { FocusNode _comment; RespPage _data; var _textFieldValue = ''; bool _isPopupText = false; @override void initState() { // TODO: implement initState super.initState(); // _comment = FocusNode(); // print(widget.parentCommentId); initData(); } @override void dispose() { // TODO: implement dispose super.dispose(); // _comment.dispose(); } initData() async { RespPage comment = await api.getPostCommentSubs(widget.parentCommentId, sortBy: "created_at"); setState(() { _data = comment; print(_data); }); } void handleLike({int type}) { print(type); } @override // void didChangeMetrics() { //// super.didChangeMetrics(); // WidgetsBinding.instance.addPostFrameCallback((_) { // // 当前是安卓系统并且在焦点聚焦的情况下 // if (Platform.isAndroid && _comment.hasFocus) { // if (_isPopupText) { // _isPopupText = false; // // 使输入框失去焦点 // _comment.unfocus(); // return; // } // _isPopupText = true; // } // }); // } @override Widget build(BuildContext context) { // TODO: implement build return Container( width: MediaQuery.of(context).size.width, child: _data != null ? Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // content Expanded( child: Column( children: [ // header Container( width: double.infinity, height: 50, child: Stack( alignment: Alignment.center, children: [ Text( "回复详情", style: TextStyle(color: Color.fromRGBO(153, 153, 153, 1), fontSize: 14.0), ), Positioned( left: 12.0, child: IconButton( icon: Icon(Icons.chevron_left), iconSize: 28.0, onPressed: () { Navigator.pop(context); }, )) ], ), ), // Content getComment(1, widget.comment), Expanded( child: Padding( padding: EdgeInsets.only(left: 24.0), child: ListView.builder( itemBuilder: (context, index) { print(_data.pageResult.results[index].isLiked); return getComment( 2, _data.pageResult.results[index], initData: initData, ); }, itemCount: _data.pageResult.results.length, ), ), ), // listView ], ), ), // bottom Container( height: 50.0, decoration: new BoxDecoration(color: Colors.white, // 底色 boxShadow: [BoxShadow(offset: Offset(0.0, 0), blurRadius: 5, spreadRadius: 0, color: Color.fromRGBO(0, 0, 0, 0.1))]), child: Padding( padding: EdgeInsets.symmetric(vertical: 7.0, horizontal: 22.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ Image.asset( "lib/assets/img/bbs_icon_report.png", width: 20.0, height: 20.0, ), Expanded( child: Container( padding: EdgeInsets.only(left: 15.0), alignment: Alignment.centerLeft, child: TextField( style: TextStyle(color: Color.fromRGBO(153, 153, 153, 1), fontSize: 14.0), onChanged: (value) { setState(() { _textFieldValue = value; }); }, decoration: InputDecoration(hintText: '发表你的看法', border: InputBorder.none), )), ), InkWell( child: Container( width: 75.0, height: 35.0, alignment: Alignment.center, child: Text( "发送", style: TextStyle(color: Colors.white), ), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(20.0)), border: new Border.all(width: 1, color: Theme.of(context).accentColor), gradient: LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Color.fromRGBO(255, 196, 0, 1), Color.fromRGBO(255, 170, 0, 1), ]), ), ), onTap: () async { RespData commentData; if (widget.parentCommentId != 0) { commentData = await api.postForumComment("${widget.subjectId}", _textFieldValue, toCommentId: widget.id, parentCommentId: widget.parentCommentId); } else { commentData = await api.postForumComment( "${widget.subjectId}", _textFieldValue, toCommentId: widget.id, ); } if (commentData.code != 0) { Fluttertoast.showToast( msg: "${commentData.msg}", toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.CENTER, backgroundColor: Colors.white, textColor: Colors.black, fontSize: 13.0); } else { setState(() { _data = null; }); initData(); // Navigator.pop(context); } }, ) ], ), ), ), ], ) : FadeingCircleLoading(context), ); } } // 自定义PopRoute class PopRoute extends PopupRoute { final Duration _duration = Duration(milliseconds: 300); Widget child; PopRoute({@required this.child}); @override Color get barrierColor => null; @override bool get barrierDismissible => true; @override String get barrierLabel => null; @override Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) { return child; } @override Duration get transitionDuration => _duration; } class FadeRoute extends PageRouteBuilder { final Widget page; FadeRoute({this.page}) : super( pageBuilder: ( BuildContext context, Animation animation, Animation secondaryAnimation, ) => page, transitionsBuilder: ( BuildContext context, Animation animation, Animation secondaryAnimation, Widget child, ) => FadeTransition( opacity: animation, child: child, ), ); } class GalleryPhotoViewWrapper extends StatefulWidget { GalleryPhotoViewWrapper({ this.loadingBuilder, this.backgroundDecoration, this.minScale, this.maxScale, this.initialIndex, @required this.galleryItems, this.scrollDirection = Axis.horizontal, }) : pageController = PageController(initialPage: initialIndex); final LoadingBuilder loadingBuilder; final Decoration backgroundDecoration; final dynamic minScale; final dynamic maxScale; final int initialIndex; final PageController pageController; final List galleryItems; final Axis scrollDirection; @override State createState() { return _GalleryPhotoViewWrapperState(); } } class _GalleryPhotoViewWrapperState extends State { int currentIndex; @override void initState() { currentIndex = widget.initialIndex; super.initState(); } void onPageChanged(int index) { setState(() { currentIndex = index; }); } @override Widget build(BuildContext context) { return Scaffold( body: Container( decoration: widget.backgroundDecoration, constraints: BoxConstraints.expand( height: MediaQuery.of(context).size.height, ), child: Stack( alignment: Alignment.bottomRight, children: [ PhotoViewGallery.builder( scrollPhysics: const BouncingScrollPhysics(), builder: _buildItem, itemCount: widget.galleryItems.length, loadingBuilder: widget.loadingBuilder, backgroundDecoration: widget.backgroundDecoration, pageController: widget.pageController, onPageChanged: onPageChanged, scrollDirection: widget.scrollDirection, ), Container( padding: const EdgeInsets.all(20.0), child: Text( "", style: const TextStyle( color: Colors.white, fontSize: 17.0, decoration: null, ), ), ) ], ), ), ); } PhotoViewGalleryPageOptions _buildItem(BuildContext context, int index) { final String item = widget.galleryItems[index]; return PhotoViewGalleryPageOptions( imageProvider: CachedNetworkImageProvider(item), initialScale: PhotoViewComputedScale.contained, minScale: PhotoViewComputedScale.contained * (0.5 + index / 10), maxScale: PhotoViewComputedScale.covered * 1.1, heroAttributes: PhotoViewHeroAttributes(tag: item), onTapUp: ( _, __, ___, ) { Navigator.of(context).pop(); }); } }