diff --git a/lib/models/dynamics/result.dart b/lib/models/dynamics/result.dart index 3e567957..9b909360 100644 --- a/lib/models/dynamics/result.dart +++ b/lib/models/dynamics/result.dart @@ -146,24 +146,29 @@ class ModuleAuthorModel { // 单个动态详情 - 动态信息 class ModuleDynamicModel { ModuleDynamicModel({ - // this.additional, + this.additional, this.desc, this.major, this.topic, }); - String? additional; + DynamicAddModel? additional; DynamicDescModel? desc; DynamicMajorModel? major; - Map? topic; + DynamicTopicModel? topic; ModuleDynamicModel.fromJson(Map json) { + additional = json['additional'] != null + ? DynamicAddModel.fromJson(json['additional']) + : null; desc = json['desc'] != null ? DynamicDescModel.fromJson(json['desc']) : null; if (json['major'] != null) { major = DynamicMajorModel.fromJson(json['major']); } - topic = json['topic']; + topic = json['topic'] != null + ? DynamicTopicModel.fromJson(json['topic']) + : null; } } @@ -177,18 +182,71 @@ class ModuleDynamicModel { // } // } +class DynamicAddModel { + DynamicAddModel({ + this.type, + this.vote, + }); + + String? type; + Vote? vote; + DynamicAddModel.fromJson(Map json) { + type = json['type']; + vote = json['vote'] != null ? Vote.fromJson(json['vote']) : null; + } +} + +class Vote { + Vote({ + this.choiceCnt, + this.defaultShare, + this.share, + this.endTime, + this.joinNum, + this.status, + this.type, + this.uid, + this.voteId, + }); + + int? choiceCnt; + String? share; + int? defaultShare; + int? endTime; + int? joinNum; + String? status; + String? type; + int? uid; + int? voteId; + + Vote.fromJson(Map json) { + choiceCnt = json['choice_cnt']; + share = json['share']; + defaultShare = json['default_share']; + endTime = json['end_time']; + joinNum = json['join_num']; + status = json['status']; + type = json['type']; + uid = json['uid']; + voteId = json['vote_id']; + } +} class DynamicDescModel { DynamicDescModel({ - this.richTextNode, + this.richTextNodes, this.text, }); - List? richTextNode; + List? richTextNodes; String? text; DynamicDescModel.fromJson(Map json) { - richTextNode = json['rich_text_nodes']; + richTextNodes = json['rich_text_nodes'] != null + ? json['rich_text_nodes'] + .map((e) => RichTextNodeItem.fromJson(e)) + .toList() + : []; text = json['text']; } } @@ -199,6 +257,8 @@ class DynamicMajorModel { this.archive, this.draw, this.ugcSeason, + this.opus, + this.none, this.type, }); @@ -206,6 +266,7 @@ class DynamicMajorModel { DynamicDrawModel? draw; DynamicArchiveModel? ugcSeason; DynamicOpusModel? opus; + DynamicNoneModel? none; // MAJOR_TYPE_DRAW 图片 // MAJOR_TYPE_ARCHIVE 视频 // MAJOR_TYPE_OPUS 图文/文章 @@ -222,10 +283,30 @@ class DynamicMajorModel { : null; opus = json['opus'] != null ? DynamicOpusModel.fromJson(json['opus']) : null; + none = + json['none'] != null ? DynamicNoneModel.fromJson(json['none']) : null; type = json['type']; } } +class DynamicTopicModel { + DynamicTopicModel({ + this.id, + this.jumpUrl, + this.name, + }); + + int? id; + String? jumpUrl; + String? name; + + DynamicTopicModel.fromJson(Map json) { + id = json['id']; + jumpUrl = json['jump_url']; + name = json['name']; + } +} + class DynamicArchiveModel { DynamicArchiveModel({ this.aid, @@ -296,35 +377,110 @@ class DynamicOpusModel { }); String? jumpUrl; - List? pics; - Map? summary; + List? pics; + SummaryModel? summary; String? title; DynamicOpusModel.fromJson(Map json) { jumpUrl = json['jump_url']; - pics = json['pics']; - summary = json['summary']; + pics = json['pics'] != null + ? json['pics'] + .map((e) => OpusPicsModel.fromJson(e)) + .toList() + : []; + summary = + json['summary'] != null ? SummaryModel.fromJson(json['summary']) : null; title = json['title']; } } +class SummaryModel { + SummaryModel({ + this.richTextNodes, + this.text, + }); + + List? richTextNodes; + String? text; + + SummaryModel.fromJson(Map json) { + richTextNodes = json['rich_text_nodes'] + .map((e) => RichTextNodeItem.fromJson(e)) + .toList(); + text = json['text']; + } +} + +class RichTextNodeItem { + RichTextNodeItem({ + this.emoji, + this.origText, + this.text, + this.type, + }); + Emoji? emoji; + String? origText; + String? text; + String? type; + + RichTextNodeItem.fromJson(Map json) { + emoji = json['emoji'] != null ? Emoji.fromJson(json['emoji']) : null; + origText = json['orig_text']; + text = json['text']; + type = json['type']; + } +} + +class Emoji { + Emoji({ + this.iconUrl, + this.size, + this.text, + this.type, + }); + + String? iconUrl; + double? size; + String? text; + int? type; + Emoji.fromJson(Map json) { + iconUrl = json['icon_url']; + size = json['size'].toDouble(); + text = json['text']; + type = json['type']; + } +} + +class DynamicNoneModel { + DynamicNoneModel({ + this.tips, + }); + String? tips; + DynamicNoneModel.fromJson(Map json) { + tips = json['tips']; + } +} + class OpusPicsModel { OpusPicsModel({ this.width, this.height, this.size, this.src, + this.url, }); int? width; int? height; int? size; String? src; + String? url; OpusPicsModel.fromJson(Map json) { width = json['width']; height = json['height']; - size = json['size']; + size = json['size'].toInt(); src = json['src']; + url = json['url']; } } @@ -337,13 +493,13 @@ class DynamicDrawItemModel { this.width, }); int? height; - double? size; + int? size; String? src; List? tags; int? width; DynamicDrawItemModel.fromJson(Map json) { height = json['height']; - size = json['size']; + size = json['size'].toInt(); src = json['src']; tags = json['tags']; width = json['width']; @@ -359,12 +515,12 @@ class ModuleStatModel { }); Comment? comment; - Map? forward; + ForWard? forward; Like? like; ModuleStatModel.fromJson(Map json) { comment = Comment.fromJson(json['comment']); - forward = json['forward']; + forward = ForWard.fromJson(json['forward']); like = Like.fromJson(json['like']); } } @@ -380,7 +536,18 @@ class Comment { bool? forbidden; Comment.fromJson(Map json) { - count = json['count'].toString(); + count = json['count'] == 0 ? null : json['count'].toString(); + forbidden = json['forbidden']; + } +} + +class ForWard { + ForWard({this.count, this.forbidden}); + String? count; + bool? forbidden; + + ForWard.fromJson(Map json) { + count = json['count'] == 0 ? null : json['count'].toString(); forbidden = json['forbidden']; } } @@ -398,7 +565,7 @@ class Like { bool? status; Like.fromJson(Map json) { - count = json['count'].toString(); + count = json['count'] == 0 ? null : json['count'].toString(); forbidden = json['forbidden']; status = json['status']; } diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 80a9d3cf..fcf6d1f2 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; -import 'package:pilipala/common/skeleton/video_card_h.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/models/dynamics/result.dart'; @@ -15,10 +13,14 @@ class DynamicsPage extends StatefulWidget { State createState() => _DynamicsPageState(); } -class _DynamicsPageState extends State { +class _DynamicsPageState extends State + with AutomaticKeepAliveClientMixin { DynamicsController _dynamicsController = Get.put(DynamicsController()); Future? _futureBuilderFuture; + @override + bool get wantKeepAlive => true; + @override void initState() { super.initState(); diff --git a/lib/pages/dynamics/widgets/dynamic_panel.dart b/lib/pages/dynamics/widgets/dynamic_panel.dart index a9aabcf5..c03c1060 100644 --- a/lib/pages/dynamics/widgets/dynamic_panel.dart +++ b/lib/pages/dynamics/widgets/dynamic_panel.dart @@ -1,12 +1,10 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; -import 'package:pilipala/common/widgets/stat/danmu.dart'; -import 'package:pilipala/common/widgets/stat/view.dart'; import 'package:pilipala/models/dynamics/result.dart'; +import 'package:pilipala/utils/utils.dart'; class DynamicPanel extends StatelessWidget { DynamicItemModel? item; @@ -69,14 +67,15 @@ class DynamicPanel extends StatelessWidget { Text( item.modules.moduleAuthor.pubTime + (item.modules.moduleAuthor.pubAction != '' - ? ' - ${item.modules.moduleAuthor.pubAction}' + ? ' ${item.modules.moduleAuthor.pubAction}' : ''), style: TextStyle( - color: Theme.of(context).colorScheme.outline, - fontSize: Theme.of(context).textTheme.labelSmall!.fontSize), + color: Theme.of(context).colorScheme.outline, + fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, + ), ) ], - ) + ), ], ), ); @@ -84,140 +83,119 @@ class DynamicPanel extends StatelessWidget { // 内容 Widget content(item, context) { + TextStyle authorStyle = + TextStyle(color: Theme.of(context).colorScheme.primary); return Container( - width: double.infinity, - padding: const EdgeInsets.fromLTRB(15, 0, 15, 10), - child: Text( - item!.modules!.moduleDynamic!.desc!.text!, - maxLines: 5, - overflow: TextOverflow.ellipsis, - ), - ); + width: double.infinity, + padding: const EdgeInsets.fromLTRB(12, 0, 12, 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (item.modules.moduleDynamic.topic != null) ...[ + GestureDetector( + child: Text( + '#${item.modules.moduleDynamic.topic.name}', + style: authorStyle, + ), + ), + ], + Text.rich(richNode(item, context)), + ], + )); } // 转发 Widget forWard(item, context, {floor = 1}) { + TextStyle authorStyle = + TextStyle(color: Theme.of(context).colorScheme.primary); switch (item.type) { // 图文 case 'DYNAMIC_TYPE_DRAW': - return Padding( - padding: const EdgeInsets.only(left: 6, right: 6), - child: picWidget(item, context), - ); - // 视频 - case 'DYNAMIC_TYPE_AV': return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (floor == 2) ...[ - Text('@' + item.modules.moduleAuthor.name), - const SizedBox(height: 8), - ], - GestureDetector( - onTap: () {}, - child: LayoutBuilder(builder: (context, box) { - double width = box.maxWidth; - return Stack( - children: [ - NetworkImgLayer( - type: floor == 1 ? 'emote' : null, - width: width, - height: width / StyleString.aspectRatio, - src: item.modules.moduleDynamic.major.archive.cover, + Row( + children: [ + GestureDetector( + onTap: () {}, + child: Text( + '@${item.modules.moduleAuthor.name}', + style: authorStyle, ), - Positioned( - left: 0, - right: 0, - bottom: 0, - child: Container( - padding: const EdgeInsets.fromLTRB(12, 22, 10, 15), - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - gradient: const LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Colors.transparent, - Colors.black87, - ], - tileMode: TileMode.mirror, - ), - borderRadius: floor == 1 - ? null - : const BorderRadius.all(Radius.circular(6))), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Row( - children: [ - Text( - item.modules.moduleDynamic.major.archive - .durationText, - style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .labelMedium! - .fontSize, - color: Colors.white), - ), - const SizedBox(width: 10), - Text( - item.modules.moduleDynamic.major.archive - .stat.play, - style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .labelMedium! - .fontSize, - color: Colors.white), - ), - const SizedBox(width: 10), - Text( - item.modules.moduleDynamic.major.archive - .stat.danmaku, - style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .labelMedium! - .fontSize, - color: Colors.white), - ) - ], - ), - Image.asset( - 'assets/images/play.png', - width: 70, - height: 70, - ), - ], - ), - )), - ], - ); - }), - ), - const SizedBox(height: 6), - Padding( - padding: floor == 1 - ? const EdgeInsets.only(left: 12, right: 12) - : EdgeInsets.zero, - child: Text( - item.modules.moduleDynamic.major.archive.title, - maxLines: 1, - style: const TextStyle(fontWeight: FontWeight.bold), - overflow: TextOverflow.ellipsis, + ), + const SizedBox(width: 6), + Text( + Utils.dateFormat(item.modules.moduleAuthor.pubTs), + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + fontSize: + Theme.of(context).textTheme.labelSmall!.fontSize), + ), + ], ), + const SizedBox(height: 2), + Text(item.modules.moduleDynamic.desc.text), + const SizedBox(height: 4), + ], + Padding( + padding: floor == 2 + ? EdgeInsets.zero + : const EdgeInsets.only(left: 12, right: 12), + child: picWidget(item, context), ), - const SizedBox(height: 4), ], ); + // 视频 + case 'DYNAMIC_TYPE_AV': + return videoSeasonWidget(item, context, 'archive', floor: floor); // 文章 case 'DYNAMIC_TYPE_ARTICLE': - return const Text('DYNAMIC_TYPE_ARTICLE'); + return Padding( + padding: floor == 2 + ? EdgeInsets.zero + : const EdgeInsets.only(left: 12, right: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (floor == 2) ...[ + Row( + children: [ + GestureDetector( + onTap: () {}, + child: Text( + '@${item.modules.moduleAuthor.name}', + style: authorStyle, + ), + ), + const SizedBox(width: 6), + Text( + Utils.dateFormat(item.modules.moduleAuthor.pubTs), + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + fontSize: + Theme.of(context).textTheme.labelSmall!.fontSize), + ), + ], + ), + const SizedBox(height: 8), + ], + Text( + item.modules.moduleDynamic.major.opus.title, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontWeight: FontWeight.bold), + ), + const SizedBox(height: 2), + Text(item.modules.moduleDynamic.major.opus.summary.richTextNodes + .first.text), + picWidget(item, context) + ], + ), + ); // 转发 case 'DYNAMIC_TYPE_FORWARD': - // return const Text('DYNAMIC_TYPE_FORWARD'); switch (item.orig.type) { // 递归 case 'DYNAMIC_TYPE_AV': @@ -227,127 +205,97 @@ class DynamicPanel extends StatelessWidget { color: Theme.of(context).dividerColor.withOpacity(0.08), child: forWard(item.orig, context, floor: 2), ); + case 'DYNAMIC_TYPE_DRAW': + return Container( + padding: const EdgeInsets.only( + left: 15, top: 10, right: 15, bottom: 10), + color: Theme.of(context).dividerColor.withOpacity(0.08), + child: forWard(item.orig, context, floor: 2), + ); + case 'DYNAMIC_TYPE_WORD': + return Container( + width: double.infinity, + padding: const EdgeInsets.only( + left: 15, top: 10, right: 15, bottom: 10), + color: Theme.of(context).dividerColor.withOpacity(0.08), + child: forWard(item.orig, context, floor: 2), + ); + case 'DYNAMIC_TYPE_NONE': + return Container( + width: double.infinity, + padding: const EdgeInsets.only( + left: 15, top: 10, right: 15, bottom: 10), + color: Theme.of(context).dividerColor.withOpacity(0.08), + child: forWard(item.orig, context, floor: 2), + ); + case 'DYNAMIC_TYPE_ARTICLE': + return Container( + width: double.infinity, + padding: const EdgeInsets.only( + left: 15, top: 10, right: 15, bottom: 10), + color: Theme.of(context).dividerColor.withOpacity(0.08), + child: forWard(item.orig, context, floor: 2), + ); default: - return const Text('渲染出错了'); + return const Text('渲染出错了1'); } // 直播 case 'DYNAMIC_TYPE_LIVE_RCMD': return const Text('DYNAMIC_TYPE_LIVE_RCMD'); // 合集 case 'DYNAMIC_TYPE_UGC_SEASON': - return Column( - crossAxisAlignment: CrossAxisAlignment.start, + return videoSeasonWidget(item, context, 'ugcSeason'); + case 'DYNAMIC_TYPE_WORD': + return floor == 2 + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + GestureDetector( + onTap: () {}, + child: Text( + '@${item.modules.moduleAuthor.name}', + style: authorStyle, + ), + ), + const SizedBox(width: 6), + Text( + Utils.dateFormat(item.modules.moduleAuthor.pubTs), + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + fontSize: Theme.of(context) + .textTheme + .labelSmall! + .fontSize), + ), + ], + ), + const SizedBox(height: 8), + Text(item.modules.moduleDynamic.desc.text) + ], + ) + : const SizedBox(height: 0); + + case 'DYNAMIC_TYPE_NONE': + return Row( children: [ - // if (floor == 2) ...[ - // Text('@' + item.modules.moduleAuthor.name), - // const SizedBox(height: 8), - // ], - GestureDetector( - onTap: () {}, - child: LayoutBuilder(builder: (context, box) { - double width = box.maxWidth; - return Stack( - children: [ - NetworkImgLayer( - type: floor == 1 ? 'emote' : null, - width: width, - height: width / StyleString.aspectRatio, - src: item.modules.moduleDynamic.major.ugcSeason.cover, - ), - Positioned( - left: 0, - right: 0, - bottom: 0, - child: Container( - padding: const EdgeInsets.fromLTRB(12, 22, 10, 15), - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - gradient: const LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Colors.transparent, - Colors.black87, - ], - tileMode: TileMode.mirror, - ), - borderRadius: floor == 1 - ? null - : const BorderRadius.all(Radius.circular(6))), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Row( - children: [ - Text( - item.modules.moduleDynamic.major.ugcSeason - .durationText, - style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .labelMedium! - .fontSize, - color: Colors.white), - ), - const SizedBox(width: 10), - Text( - item.modules.moduleDynamic.major.ugcSeason - .stat.play, - style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .labelMedium! - .fontSize, - color: Colors.white), - ), - const SizedBox(width: 10), - Text( - item.modules.moduleDynamic.major.ugcSeason - .stat.danmaku, - style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .labelMedium! - .fontSize, - color: Colors.white), - ) - ], - ), - Image.asset( - 'assets/images/play.png', - width: 70, - height: 70, - ), - ], - ), - )), - ], - ); - }), + const Icon( + FontAwesomeIcons.ghost, + size: 14, ), - const SizedBox(height: 6), - Padding( - padding: floor == 1 - ? const EdgeInsets.only(left: 12, right: 12) - : EdgeInsets.zero, - child: Text( - item.modules.moduleDynamic.major.ugcSeason.title, - maxLines: 1, - style: const TextStyle(fontWeight: FontWeight.bold), - overflow: TextOverflow.ellipsis, - ), - ), - const SizedBox(height: 4), + const SizedBox(width: 4), + Text(item.modules.moduleDynamic.major.none.tips) ], ); default: - return const Text('渲染出错了'); + return const SizedBox(height: 0); } } // 操作栏 Widget action(item, context) { + ModuleStatModel stat = item.modules.moduleStat; return Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ @@ -357,7 +305,7 @@ class DynamicPanel extends StatelessWidget { FontAwesomeIcons.shareFromSquare, size: 16, ), - label: const Text('转发'), + label: Text(stat.forward!.count ?? '转发'), ), TextButton.icon( onPressed: () {}, @@ -365,7 +313,7 @@ class DynamicPanel extends StatelessWidget { FontAwesomeIcons.comment, size: 16, ), - label: Text(item.modules.moduleStat.comment.count), + label: Text(stat.comment!.count ?? '评论'), ), TextButton.icon( onPressed: () {}, @@ -373,20 +321,27 @@ class DynamicPanel extends StatelessWidget { FontAwesomeIcons.thumbsUp, size: 16, ), - label: Text(item.modules.moduleStat.like.count), + label: Text(stat.like!.count ?? '点赞'), ) ], ); } Widget picWidget(item, context) { - List pictures = item.modules.moduleDynamic.major.draw.items; + String type = item.modules.moduleDynamic.major.type; + List pictures = []; + if (type == 'MAJOR_TYPE_OPUS') { + pictures = item.modules.moduleDynamic.major.opus.pics; + } + if (type == 'MAJOR_TYPE_DRAW') { + pictures = item.modules.moduleDynamic.major.draw.items; + } int len = pictures.length; List picList = []; List list = []; for (var i = 0; i < len; i++) { - picList.add(pictures[i].src); + picList.add(pictures[i].src ?? pictures[i].url); list.add( LayoutBuilder( builder: (context, BoxConstraints box) { @@ -396,7 +351,7 @@ class DynamicPanel extends StatelessWidget { arguments: {'initialPage': i, 'imgList': picList}); }, child: NetworkImgLayer( - src: pictures[i].src, + src: pictures[i].src ?? pictures[i].url, width: box.maxWidth, height: box.maxWidth, ), @@ -431,4 +386,223 @@ class DynamicPanel extends StatelessWidget { }, ); } + + // 视频or合集 + Widget videoSeasonWidget(item, context, type, {floor = 1}) { + TextStyle authorStyle = + TextStyle(color: Theme.of(context).colorScheme.primary); + // type archive ugcSeason + // archive 视频/显示发布人 + // ugcSeason 合集/不显示发布人 + + // floor 1 2 + // 1 投稿视频 铺满 borderRadius 0 + // 2 转发视频 铺满 borderRadius 6 + Map dynamicProperty = { + 'ugcSeason': item.modules.moduleDynamic.major.ugcSeason, + 'archive': item.modules.moduleDynamic.major.archive, + }; + dynamic content = dynamicProperty[type]; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (floor == 2) ...[ + Row( + children: [ + GestureDetector( + onTap: () {}, + child: Text( + '@${item.modules.moduleAuthor.name}', + style: authorStyle, + ), + ), + const SizedBox(width: 6), + Text( + Utils.dateFormat(item.modules.moduleAuthor.pubTs), + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + fontSize: Theme.of(context).textTheme.labelSmall!.fontSize), + ), + ], + ), + ], + if (item.modules.moduleDynamic.topic != null) ...[ + const SizedBox(height: 4), + Padding( + padding: floor == 2 + ? EdgeInsets.zero + : const EdgeInsets.only(left: 12, right: 12), + child: GestureDetector( + child: Text( + '#${item.modules.moduleDynamic.topic.name}', + style: authorStyle, + ), + ), + ), + ], + const SizedBox(height: 6), + GestureDetector( + onTap: () {}, + child: LayoutBuilder(builder: (context, box) { + double width = box.maxWidth; + return Stack( + children: [ + NetworkImgLayer( + type: floor == 1 ? 'emote' : null, + width: width, + height: width / StyleString.aspectRatio, + src: content.cover, + ), + Positioned( + left: 0, + right: 0, + bottom: 0, + child: Container( + height: 80, + padding: const EdgeInsets.fromLTRB(12, 0, 10, 10), + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + gradient: const LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black87, + ], + ), + borderRadius: floor == 1 + ? null + : const BorderRadius.all(Radius.circular(6))), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + DefaultTextStyle.merge( + style: TextStyle( + fontSize: Theme.of(context) + .textTheme + .labelMedium! + .fontSize, + color: Colors.white), + child: Row( + children: [ + Text(content.durationText), + const SizedBox(width: 10), + Text(content.stat.play + '次围观'), + const SizedBox(width: 10), + Text(content.stat.danmaku + '条弹幕') + ], + ), + ), + Image.asset( + 'assets/images/play.png', + width: 70, + height: 70, + ), + ], + ), + )), + ], + ); + }), + ), + const SizedBox(height: 6), + Padding( + padding: floor == 1 + ? const EdgeInsets.only(left: 12, right: 12) + : EdgeInsets.zero, + child: Text( + content.title, + maxLines: 1, + style: const TextStyle(fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox(height: 2), + ], + ); + } + + InlineSpan richNode(item, context) { + TextStyle authorStyle = + TextStyle(color: Theme.of(context).colorScheme.primary, fontSize: 13); + List spanChilds = []; + for (var i in item.modules.moduleDynamic.desc.richTextNodes) { + if (i.type == 'RICH_TEXT_NODE_TYPE_TEXT') { + spanChilds.add(TextSpan(text: i.origText)); + } + if (i.type == 'RICH_TEXT_NODE_TYPE_AT') { + spanChilds.add( + WidgetSpan( + baseline: TextBaseline.ideographic, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + GestureDetector( + onTap: () {}, + child: Text( + '@${item.modules.moduleAuthor.name}', + style: authorStyle, + ), + ), + ], + ), + ), + ); + } + if (i.type == 'RICH_TEXT_NODE_TYPE_TOPIC') { + spanChilds.add( + WidgetSpan( + child: GestureDetector( + onTap: () {}, + child: Text( + ' ${i.origText} ', + style: authorStyle, + ), + ), + ), + ); + } + if (i.type == 'RICH_TEXT_NODE_TYPE_WEB') { + spanChilds.add( + WidgetSpan( + child: GestureDetector( + onTap: () {}, + child: Text( + i.text, + style: authorStyle, + ), + ), + ), + ); + } + if (i.type == 'RICH_TEXT_NODE_TYPE_VOTE') { + spanChilds.add( + WidgetSpan( + child: GestureDetector( + onTap: () {}, + child: Text( + i.text, + style: authorStyle, + ), + ), + ), + ); + } + if (i.type == 'RICH_TEXT_NODE_TYPE_EMOJI') { + spanChilds.add( + WidgetSpan( + child: NetworkImgLayer( + src: i.emoji.iconUrl, + type: 'emote', + width: i.emoji.size * 20, + height: i.emoji.size * 20, + ), + ), + ); + } + } + return TextSpan(children: spanChilds); + } }