From 4c938ed8aac0348128cbd88a379162f3da4cb5b9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 6 Sep 2023 22:47:54 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=8A=A8=E6=80=81=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=B8=B2=E6=9F=93=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/widgets/content_panel.dart | 2 + lib/pages/dynamics/widgets/dynamic_panel.dart | 3 +- lib/pages/dynamics/widgets/forward_panel.dart | 30 +- lib/pages/dynamics/widgets/pic_panel.dart | 4 +- .../dynamics/widgets/rich_node_panel.dart | 438 ++++++++++++------ lib/pages/dynamics/widgets/video_panel.dart | 29 +- 6 files changed, 324 insertions(+), 182 deletions(-) diff --git a/lib/pages/dynamics/widgets/content_panel.dart b/lib/pages/dynamics/widgets/content_panel.dart index 34324d2e..d6174975 100644 --- a/lib/pages/dynamics/widgets/content_panel.dart +++ b/lib/pages/dynamics/widgets/content_panel.dart @@ -28,6 +28,8 @@ Widget content(item, context, source) { focusNode: FocusNode(), selectionControls: MaterialTextSelectionControls(), child: Text.rich( + /// fix 默认20px高度 + style: const TextStyle(height: 0), richNode(item, context), maxLines: source == 'detail' ? 999 : 3, overflow: TextOverflow.ellipsis, diff --git a/lib/pages/dynamics/widgets/dynamic_panel.dart b/lib/pages/dynamics/widgets/dynamic_panel.dart index ef0bc8cc..42670ac1 100644 --- a/lib/pages/dynamics/widgets/dynamic_panel.dart +++ b/lib/pages/dynamics/widgets/dynamic_panel.dart @@ -41,7 +41,8 @@ class DynamicPanel extends StatelessWidget { padding: const EdgeInsets.fromLTRB(12, 12, 12, 8), child: author(item, context), ), - if (item!.modules!.moduleDynamic!.desc != null) + if (item!.modules!.moduleDynamic!.desc != null || + item!.modules!.moduleDynamic!.major != null) content(item, context, source), forWard(item, context, _dynamicsController, source), const SizedBox(height: 2), diff --git a/lib/pages/dynamics/widgets/forward_panel.dart b/lib/pages/dynamics/widgets/forward_panel.dart index 55972e37..77734779 100644 --- a/lib/pages/dynamics/widgets/forward_panel.dart +++ b/lib/pages/dynamics/widgets/forward_panel.dart @@ -44,19 +44,21 @@ Widget forWard(item, context, ctr, source, {floor = 1}) { ], ), const SizedBox(height: 2), - if (item.modules.moduleDynamic.topic != null) ...[ - Padding( - padding: floor == 2 - ? EdgeInsets.zero - : const EdgeInsets.only(left: 12, right: 12), - child: GestureDetector( - child: Text( - '#${item.modules.moduleDynamic.topic.name}', - style: authorStyle, - ), - ), - ), - ], + + /// fix #话题跟content重复 + // if (item.modules.moduleDynamic.topic != null) ...[ + // Padding( + // padding: floor == 2 + // ? EdgeInsets.zero + // : const EdgeInsets.only(left: 12, right: 12), + // child: GestureDetector( + // child: Text( + // '#${item.modules.moduleDynamic.topic.name}', + // style: authorStyle, + // ), + // ), + // ), + // ], Text.rich( richNode(item, context), // 被转发状态(floor=2) 隐藏 @@ -71,6 +73,8 @@ Widget forWard(item, context, ctr, source, {floor = 1}) { : const EdgeInsets.only(left: 12, right: 12), child: picWidget(item, context), ), + + /// 附加内容 商品信息、直播预约等等 if (item.modules.moduleDynamic.additional != null) addWidget( item, diff --git a/lib/pages/dynamics/widgets/pic_panel.dart b/lib/pages/dynamics/widgets/pic_panel.dart index 9ee8be53..6fb21b16 100644 --- a/lib/pages/dynamics/widgets/pic_panel.dart +++ b/lib/pages/dynamics/widgets/pic_panel.dart @@ -8,7 +8,9 @@ Widget picWidget(item, context) { String type = item.modules.moduleDynamic.major.type; List pictures = []; if (type == 'MAJOR_TYPE_OPUS') { - pictures = item.modules.moduleDynamic.major.opus.pics; + /// fix 图片跟rich_node_panel重复 + // pictures = item.modules.moduleDynamic.major.opus.pics; + return const SizedBox(); } if (type == 'MAJOR_TYPE_DRAW') { pictures = item.modules.moduleDynamic.major.draw.items; diff --git a/lib/pages/dynamics/widgets/rich_node_panel.dart b/lib/pages/dynamics/widgets/rich_node_panel.dart index 78d5aaba..66d1a9a5 100644 --- a/lib/pages/dynamics/widgets/rich_node_panel.dart +++ b/lib/pages/dynamics/widgets/rich_node_panel.dart @@ -1,175 +1,307 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/models/dynamics/result.dart'; // 富文本 InlineSpan richNode(item, context) { - TextStyle authorStyle = - TextStyle(color: Theme.of(context).colorScheme.primary); - 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, style: const TextStyle(height: 1.65))); + final spacer = _VerticalSpaceSpan(0.0); + try { + TextStyle authorStyle = + TextStyle(color: Theme.of(context).colorScheme.primary); + List spanChilds = []; + String contentType = 'desc'; + + dynamic richTextNodes; + if (item.modules.moduleDynamic.desc != null) { + richTextNodes = item.modules.moduleDynamic.desc.richTextNodes; + } else if (item.modules.moduleDynamic.major != null) { + contentType = 'major'; + // 动态页面 richTextNodes 层级可能与主页动态层级不同 + richTextNodes = + item.modules.moduleDynamic.major.opus.summary.richTextNodes; } - // @用户 - if (i.type == 'RICH_TEXT_NODE_TYPE_AT') { - spanChilds.add( - WidgetSpan( - alignment: PlaceholderAlignment.middle, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - GestureDetector( - onTap: () => Get.toNamed('/member?mid=${i.rid}', - arguments: {'face': null}), + if (richTextNodes == null || richTextNodes.isEmpty) { + return spacer; + } else { + for (var i in richTextNodes) { + if (i.type == 'RICH_TEXT_NODE_TYPE_TEXT') { + spanChilds.add( + TextSpan(text: i.origText, style: const TextStyle(height: 1.65))); + } + // @用户 + if (i.type == 'RICH_TEXT_NODE_TYPE_AT') { + spanChilds.add( + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + GestureDetector( + onTap: () => Get.toNamed('/member?mid=${i.rid}', + arguments: {'face': null}), + child: Text( + ' ${i.text}', + style: authorStyle, + ), + ), + ], + ), + ), + ); + } + // 话题 + if (i.type == 'RICH_TEXT_NODE_TYPE_TOPIC') { + spanChilds.add( + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: GestureDetector( + onTap: () {}, child: Text( - ' ${i.text}', + '${i.origText}', style: authorStyle, ), ), - ], - ), - ), - ); - } - // 话题 - if (i.type == 'RICH_TEXT_NODE_TYPE_TOPIC') { - spanChilds.add( - WidgetSpan( - alignment: PlaceholderAlignment.middle, - child: GestureDetector( - onTap: () {}, - child: Text( - '${i.origText}', - style: authorStyle, ), - ), - ), - ); - } - // 网页链接 - if (i.type == 'RICH_TEXT_NODE_TYPE_WEB') { - spanChilds.add( - WidgetSpan( - alignment: PlaceholderAlignment.middle, - child: Icon( - Icons.link, - size: 20, - color: Theme.of(context).colorScheme.primary, - ), - ), - ); - spanChilds.add( - WidgetSpan( - alignment: PlaceholderAlignment.middle, - child: GestureDetector( - onTap: () { - Get.toNamed( - '/webview', - parameters: {'url': i.origText, 'type': 'url', 'pageTitle': ''}, - ); - }, - child: Text( - i.text, - style: authorStyle, + ); + } + // 网页链接 + if (i.type == 'RICH_TEXT_NODE_TYPE_WEB') { + spanChilds.add( + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Icon( + Icons.link, + size: 20, + color: Theme.of(context).colorScheme.primary, + ), ), - ), - ), - ); - } - // 投票 - if (i.type == 'RICH_TEXT_NODE_TYPE_VOTE') { - spanChilds.add( - WidgetSpan( - alignment: PlaceholderAlignment.middle, - child: GestureDetector( - onTap: () { - String dynamicId = item.basic['comment_id_str']; - Get.toNamed( - '/webview', - parameters: { - 'url': - 'https://t.bilibili.com/vote/h5/index/#/result?vote_id=${i.rid}&dynamic_id=$dynamicId&isWeb=1', - 'type': 'vote', - 'pageTitle': '投票' + ); + spanChilds.add( + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: GestureDetector( + onTap: () { + Get.toNamed( + '/webview', + parameters: { + 'url': i.origText, + 'type': 'url', + 'pageTitle': '' + }, + ); }, - ); - }, - child: Text( - '投票:${i.text}', - style: authorStyle, + 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, - ), - ), - ); - } - // 抽奖 - if (i.type == 'RICH_TEXT_NODE_TYPE_LOTTERY') { - spanChilds.add( - WidgetSpan( - alignment: PlaceholderAlignment.middle, - child: Icon( - Icons.redeem_rounded, - size: 16, - color: Theme.of(context).colorScheme.primary, - ), - ), - ); - spanChilds.add( - WidgetSpan( - alignment: PlaceholderAlignment.middle, - child: GestureDetector( - onTap: () {}, - child: Text( - '${i.origText} ', - style: authorStyle, + ); + } + // 投票 + if (i.type == 'RICH_TEXT_NODE_TYPE_VOTE') { + spanChilds.add( + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: GestureDetector( + onTap: () { + String dynamicId = item.basic['comment_id_str']; + Get.toNamed( + '/webview', + parameters: { + 'url': + 'https://t.bilibili.com/vote/h5/index/#/result?vote_id=${i.rid}&dynamic_id=$dynamicId&isWeb=1', + 'type': 'vote', + 'pageTitle': '投票' + }, + ); + }, + 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, + ), + ), + ); + } + // 抽奖 + if (i.type == 'RICH_TEXT_NODE_TYPE_LOTTERY') { + spanChilds.add( + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Icon( + Icons.redeem_rounded, + size: 16, + color: Theme.of(context).colorScheme.primary, + ), + ), + ); + spanChilds.add( + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: GestureDetector( + onTap: () {}, + child: Text( + '${i.origText} ', + style: authorStyle, + ), + ), + ), + ); + } - /// TODO 商品 - if (i.type == 'RICH_TEXT_NODE_TYPE_GOODS') { - spanChilds.add( - WidgetSpan( - alignment: PlaceholderAlignment.middle, - child: Icon( - Icons.shopping_bag_outlined, - size: 16, - color: Theme.of(context).colorScheme.primary, - ), - ), - ); - spanChilds.add( - WidgetSpan( - alignment: PlaceholderAlignment.middle, - child: GestureDetector( - onTap: () {}, - child: Text( - '${i.text} ', - style: authorStyle, + /// TODO 商品 + if (i.type == 'RICH_TEXT_NODE_TYPE_GOODS') { + spanChilds.add( + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Icon( + Icons.shopping_bag_outlined, + size: 16, + color: Theme.of(context).colorScheme.primary, + ), ), - ), - ), + ); + spanChilds.add( + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: GestureDetector( + onTap: () {}, + child: Text( + '${i.text} ', + style: authorStyle, + ), + ), + ), + ); + } + } + if (contentType == 'major' && + item.modules.moduleDynamic.major.opus.pics.isNotEmpty) { + // 图片可能跟其他widget重复渲染 + print('有图片'); + List pics = item.modules.moduleDynamic.major.opus.pics; + int len = pics.length; + List picList = []; + + if (len == 1) { + OpusPicsModel pictureItem = pics.first; + picList.add(pictureItem.url); + spanChilds.add(const TextSpan(text: '\n')); + spanChilds.add( + WidgetSpan( + child: LayoutBuilder( + builder: (context, BoxConstraints box) { + return GestureDetector( + onTap: () { + Get.toNamed('/preview', + arguments: {'initialPage': 0, 'imgList': picList}); + }, + child: Padding( + padding: const EdgeInsets.only(top: 4), + child: NetworkImgLayer( + src: pictureItem.url, + width: box.maxWidth / 2, + height: box.maxWidth * + 0.5 * + pictureItem.height! / + pictureItem.width!, + ), + ), + ); + }, + ), + ), + ); + } + if (len > 1) { + List list = []; + for (var i = 0; i < len; i++) { + picList.add(pics[i].url); + list.add( + LayoutBuilder( + builder: (context, BoxConstraints box) { + return GestureDetector( + onTap: () { + Get.toNamed('/preview', + arguments: {'initialPage': i, 'imgList': picList}); + }, + child: NetworkImgLayer( + src: pics[i].url, + width: box.maxWidth, + height: box.maxWidth, + ), + ); + }, + ), + ); + } + spanChilds.add( + WidgetSpan( + child: LayoutBuilder( + builder: (context, BoxConstraints box) { + double maxWidth = box.maxWidth; + double crossCount = len < 3 ? 2 : 3; + double height = maxWidth / + crossCount * + (len % crossCount == 0 + ? len ~/ crossCount + : len ~/ crossCount + 1) + + 6; + return Container( + padding: const EdgeInsets.only(top: 6), + height: height, + child: GridView.count( + padding: EdgeInsets.zero, + physics: const NeverScrollableScrollPhysics(), + crossAxisCount: crossCount.toInt(), + mainAxisSpacing: 4.0, + crossAxisSpacing: 4.0, + childAspectRatio: 1, + children: list, + ), + ); + }, + ), + ), + ); + } + // spanChilds.add( + // WidgetSpan( + // child: NetworkImgLayer( + // src: pics.first.url, + // type: 'emote', + // width: 100, + // height: 200, + // ), + // ), + // ); + } + return TextSpan( + children: spanChilds, ); } + } catch (err) { + print('❌rich_node_panel err: $err'); + return spacer; } - return TextSpan( - children: spanChilds, - ); +} + +class _VerticalSpaceSpan extends WidgetSpan { + _VerticalSpaceSpan(double height) + : super(child: SizedBox(height: height, width: double.infinity)); } diff --git a/lib/pages/dynamics/widgets/video_panel.dart b/lib/pages/dynamics/widgets/video_panel.dart index 04c1ae19..32a6e21c 100644 --- a/lib/pages/dynamics/widgets/video_panel.dart +++ b/lib/pages/dynamics/widgets/video_panel.dart @@ -57,20 +57,21 @@ Widget videoSeasonWidget(item, context, type, {floor = 1}) { const SizedBox(height: 6), ], // const SizedBox(height: 4), - if (item.modules.moduleDynamic.topic != null) ...[ - 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), - ], + /// fix #话题跟content重复 + // if (item.modules.moduleDynamic.topic != null) ...[ + // 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), + // ], if (floor == 2 && item.modules.moduleDynamic.desc != null) ...[ Text.rich(richNode(item, context)), const SizedBox(height: 6),