import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:huixiang/community/community_view/class_details_video.dart'; import 'package:huixiang/generated/l10n.dart'; import 'package:huixiang/data/base_data.dart'; import 'package:huixiang/data/chapter.dart'; import 'package:huixiang/data/course_details.dart'; import 'package:huixiang/retrofit/retrofit_api.dart'; import 'package:huixiang/utils/font_weight.dart'; import 'package:huixiang/view_widget/tips_dialog.dart'; import 'package:huixiang/web/web_view/comment_list.dart'; import 'package:huixiang/web/web_view/input_comment.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:sharesdk_plugin/sharesdk_defines.dart'; import 'package:sharesdk_plugin/sharesdk_interface.dart'; import 'package:sharesdk_plugin/sharesdk_map.dart'; import '../../view_widget/share_dialog.dart'; class ClassDetails extends StatefulWidget { final Map arguments; ClassDetails({required this.arguments}); @override State createState() { return _ClassDetails(); } } class _ClassDetails extends State with WidgetsBindingObserver { ApiService? apiService; final GlobalKey commentKey = GlobalKey(); final GlobalKey videoKey = GlobalKey(); final ScrollController scrollController = ScrollController(); bool isKeyBoardShow = false; var commentFocus = FocusNode(); String parenId = "0"; String hintText = S.current.liuxianinjingcaidepinglunba; final GlobalKey inputKey = GlobalKey(); final TextEditingController commentTextController = TextEditingController(); int commentTotal = 0; CourseDetails? course; List chapterList = []; bool isShowImg = true; int chapterIndex = 0; double height = 0; var isShowMore = false; bool emojiShowing = false; double keyboard = -1; bool needShowSmiley = false; bool needHideSmiley = false; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); commentFocus.addListener(_focusNodeListener); courseDetails(widget.arguments["id"]); queryChapterList(widget.arguments["id"]); } ///课程章节列表 queryChapterList(courseId) async { BaseData>? baseData = await apiService?.catalogList(courseId).catchError((onError) {}); if (baseData?.isSuccess ?? false) { setState(() { chapterList.clear(); chapterList.addAll(baseData!.data ?? []); chapterIndex = 0; ClassDetailsVideoState? state = videoKey.currentState as ClassDetailsVideoState?; state?.initVideo(chapterList[chapterIndex].content?.fileUrl); // initVideo(chapterList[chapterIndex].content.fileUrl); }); } EasyLoading.dismiss(); } ///课程详情 courseDetails(id) async { BaseData? baseData = await apiService?.course(id).catchError((error) {}); if (baseData?.isSuccess ?? false) { setState(() { course = baseData!.data; }); } } @override void didChangeMetrics() { WidgetsBinding.instance.addPostFrameCallback((_) { isKeyBoardShow = MediaQuery.of(context).viewInsets.bottom > 0; if (!mounted) return; if (MediaQuery.of(context).viewInsets.bottom == 0) { if (isKeyBoardShow) { FocusScope.of(context).requestFocus(FocusNode()); if (mounted) if(!emojiShowing) setState(() { hintText = S.current.liuxianinjingcaidepinglunba; isKeyBoardShow = false; }); } } else { if (mounted) setState(() { isKeyBoardShow = true; }); } }); if (needShowSmiley && window.viewInsets.bottom <= 0.1) { needShowSmiley = false; setState(() { emojiShowing = true; }); } if(needHideSmiley && window.viewInsets.bottom > 0.1) { needHideSmiley = false; setState(() { emojiShowing = false; }); } } void _focusNodeListener() { /*if (_focusNode.hasFocus || _focusNode.consumeKeyboardToken()){ setState(() { smileyPadGone = true; }); }*/ } _onTextFieldTap() { if(emojiShowing) { needHideSmiley = true; } } _onSmileyTap() { if(!emojiShowing && commentFocus.hasFocus && isKeyBoardShow) { needShowSmiley = true; commentFocus.unfocus(); } else { setState(() { emojiShowing = !emojiShowing; isKeyBoardShow = emojiShowing; }); } } @override void dispose(){ WidgetsBinding.instance.removeObserver(this); commentFocus.removeListener(_focusNodeListener); super.dispose(); } @override Widget build(BuildContext context) { double h = MediaQuery.of(context).viewInsets.bottom; if(h > 0 && keyboard < h) { setState(() { keyboard = h; }); } return AnnotatedRegion( value: SystemUiOverlayStyle.light, child: Material( child: Scaffold( body: GestureDetector( behavior: HitTestBehavior.translucent, onTap: () { setState(() { emojiShowing = false; isKeyBoardShow = emojiShowing; FocusScope.of(context).requestFocus(FocusNode()); }); }, child: Container( // margin: EdgeInsets.only(top:25), child: Column( children: [ Expanded( child: SingleChildScrollView( physics: BouncingScrollPhysics(), child: Column( children: [ Container( height: MediaQuery.of(context).size.width / 1.5, child: Stack( children: [ ClassDetailsVideo( key: videoKey, exitFull: () { setState(() {}); }, coverImg: chapterList.length > chapterIndex ? chapterList[chapterIndex] .content?.coverImg : "", isShowImg: this.isShowImg, changeShowImg: (isShowImg) { setState(() { this.isShowImg = isShowImg; }); }, heightFun: (height) { this.height = height + MediaQuery.of(context).padding.top + kToolbarHeight + 24; if (mounted) setState(() {}); }, ), Container( margin: EdgeInsets.only( top: 40.h, left: 16.w, right: 16.w, ), decoration: BoxDecoration( color: Colors.transparent, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ GestureDetector( child: Image.asset( "assets/image/integral_return.webp", width: 24, height: 24, ), onTap: () { Navigator.of(context).pop( course != null ? (course!.viewers ?? 0) + 1 : 0); }, ), ], ), ), ], ), ), Container( margin: EdgeInsets.only(bottom: 16.h), padding: EdgeInsets.only( left: 16, top: 16, right: 10, ), decoration: BoxDecoration( color: Colors.white, boxShadow: [ BoxShadow( color: Colors.black.withAlpha(12), offset: Offset(0, 2), blurRadius: 14, spreadRadius: 0, ), ], ), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( // height: 22.h, padding: EdgeInsets.only( left: 2, right: 2, ), alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(2), border: Border.all( width: 1, color: Color(0xFFFF7A1A), style: BorderStyle.solid, ), ), child: Text( "${course?.tags?[0]}", overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 14.sp, fontWeight: MyFontWeight.medium, color: Color(0xFFFF7A1A), ), ), ), SizedBox( width: 6.w, ), Expanded( child: Text( course?.subject ?? "", overflow: TextOverflow.ellipsis, maxLines: 2, style: TextStyle( fontSize: 16.sp, fontWeight: MyFontWeight.semi_bold, color: Color(0xFF1A1A1A), ), ), flex: 1, ), // GestureDetector( // onTap: () { // share(); // }, // child: Icon( // Icons.share, // size: 24, // color: Colors.black, // ), // ), // SizedBox(width: 15.w,), ], ), SizedBox( height: 10.h, ), Padding( padding: EdgeInsets.only(right: 16), child: Row( children: [ Expanded( child: Text( "${S.of(context).jiangshi}:${course?.author?.name}", overflow: TextOverflow.ellipsis, maxLines: 2, style: TextStyle( fontSize: 12.sp, fontWeight: MyFontWeight.regular, color: Colors.black, ), ), ), Text( "${S.of(context).bofangcishu}:", style: TextStyle( fontSize: 12.sp, fontWeight: MyFontWeight.regular, color: Color(0xFF808080), ), ), SizedBox( width: 4, ), Text( "${course?.viewers}", overflow: TextOverflow.ellipsis, maxLines: 2, style: TextStyle( fontSize: 12.sp, fontWeight: MyFontWeight.regular, color: Color(0xFF808080), ), ), ], ), ), SizedBox( height: 10.h, ), Text( course?.introduce ?? "", overflow: isShowMore ? TextOverflow.visible : TextOverflow.ellipsis, maxLines: isShowMore ? 10 : 2, style: TextStyle( fontSize: 12.sp, height: 1.2.h, fontWeight: MyFontWeight.regular, color: Colors.black, ), ), SizedBox(height: 5.h), if((course?.introduce ?? "").length > 50) GestureDetector( onTap: () { setState(() { isShowMore = !isShowMore; }); }, child: Align( alignment: Alignment.center, child: Icon( (!isShowMore) ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_up, color: Colors.black, size: 24, ), ), ), SizedBox(height: 3.h), ], ), ), anthology(), CommentList( commentKey, course?.likes ?? 0, widget.arguments["id"], 3, isKeyBoardShow, _reply, _delCommentTips, 12.sp, requestApiFinish: (total) { setState(() { commentTotal = total; }); }, ), if (commentTotal == 0) Container( width: double.infinity, alignment: Alignment.topCenter, margin: EdgeInsets.only(top: 40), padding: EdgeInsets.all(22.h), child: Text( S.of(context).zanwupinglun, style: TextStyle( fontSize: 12, fontWeight: FontWeight.bold, color: Color(0xFFA0A0A0), ), ), ), ], ), ), flex: 1, ), /// 富文本评论的输入框 InputComment( inputKey, hintText, isKeyBoardShow, keyboard, emojiShowing, commentFocus, commentTextController, _toComment, _onSmileyTap, _onTextFieldTap, _queryMemberComment, _queryCourseLikes, isLike: course?.selfLiked, ), ], ), )), ), )); } Widget anthology() { return Container( color: Colors.white, margin: EdgeInsets.only(bottom: 16), padding: EdgeInsets.all(16), child: Column( children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( S.of(context).xuanji, style: TextStyle( fontSize: 15.sp, fontWeight: MyFontWeight.medium, color: Color(0xFF1A1A1A), ), ), Text( "共${chapterList.length}集", style: TextStyle( fontSize: 16.sp, fontWeight: MyFontWeight.regular, color: Color(0xFF1A1A1A), ), ), ], ), Container( height: 70.h, margin: EdgeInsets.only(top: 10), child: ListView.builder( scrollDirection: Axis.horizontal, physics: BouncingScrollPhysics(), itemCount: chapterList.length, itemBuilder: (context, position) { return GestureDetector( onTap: () { setState(() { chapterIndex = position; isShowImg = true; ClassDetailsVideoState? state = videoKey.currentState as ClassDetailsVideoState?; state?.initVideo(chapterList[position].content?.fileUrl); // initVideo(chapterList[position].content.fileUrl); }); }, child: classSelectItem(chapterList[position], position), ); }, ), ), ], ), ); } Widget classSelectItem(Chapter chapterList, position) { return Container( width: 106.w, alignment: Alignment.center, margin: EdgeInsets.symmetric( horizontal: 6.w, vertical: 3, ), padding: EdgeInsets.all(8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), color: position == chapterIndex ? Color(0xFF32A060) : Color(0xFFE5E5E5), ), child: Text( chapterList.name ?? chapterList.name ?? "", overflow: TextOverflow.ellipsis, maxLines: 2, style: TextStyle( fontSize: 12.sp, fontWeight: MyFontWeight.regular, color: position == chapterIndex ? Colors.white : Color(0xFF7C7C7C), ), ), ); } ///课程点赞 _queryCourseLikes() async { BaseData? baseData = await apiService?.courseLikes(widget.arguments["id"]) .catchError((onError) {}); if (baseData?.isSuccess ?? false) { setState(() { int likes = course?.likes ?? 0; if (course?.selfLiked ?? false) { course?.likes = likes - 1; } else { course?.likes = likes + 1; } course?.selfLiked = !(course?.selfLiked ?? false); }); commentKey.currentState?.setState(() {}); } else { // SmartDialog.showToast(baseData.msg, alignment: Alignment.center); } } ///发布评论 _queryMemberComment(String content) async { BaseData? baseData = await apiService?.memberComment({ "content": content, "parentId": parenId, "relationalId": widget.arguments["id"], "relationalType": 3 }).catchError((error) {}); if (baseData?.isSuccess ?? false) { CommentListState? state = commentKey.currentState as CommentListState?; state?.queryMemberCommentList(); commentTextController.text = ""; FocusScope.of(context).unfocus(); // _toComment(); } } ///滑动到评论列表 _toComment() { if (commentKey.currentContext == null) return; RenderBox? firstRenderBox = commentKey.currentContext!.findRenderObject() as RenderBox?; Offset? first = firstRenderBox?.localToGlobal(Offset.zero); scrollController.animateTo( (first?.dy ?? 0) + scrollController.offset - (kToolbarHeight + MediaQuery.of(context).padding.top), duration: Duration(milliseconds: 300), curve: Curves.easeIn, ); } ///评论 回复 _reply(memberComment) { FocusScope.of(context).requestFocus(commentFocus); parenId = memberComment.id; hintText = S.of(context).huifu_("${memberComment.username}"); } ///删除评论 delComment(memberComment) async { BaseData? baseData = await apiService?.delComment(memberComment.id); if (baseData?.isSuccess ?? false) { CommentListState? state = commentKey.currentState as CommentListState?; state?.queryMemberCommentList(); } } ///删除评论的提示 _delCommentTips(memberComment) { SmartDialog.show(widget: Tips(() { delComment(memberComment); })); } share() async { SSDKMap params = SSDKMap() ..setGeneral( course?.subject ?? "", course?.introduce ?? "", [ chapterList[chapterIndex].content?.coverImg ?? "", ], chapterList[chapterIndex].content?.fileUrl ?? "", "", buildShareUrl(), "", "", "", "", SSDKContentTypes.webpage, ); showModalBottomSheet( context: context, backgroundColor: Colors.transparent, builder: (context) { return ShareDialog((platform) { if (platform == ShareSDKPlatforms.line) { params.map["type"] = SSDKContentTypes.text.value; params.map["text"] = "${course?.subject} ${buildShareUrl()}"; } SharesdkPlugin.share(platform, params, (state, userData, contentEntity, error) { print("share!$state"); }); }); }); } String buildShareUrl() { return "https://hx.lotus-wallet.com/index.html?id=${widget.arguments["id"]}&type=${"article"}"; } }