From 838a141bc7d63e8025674047e8e3687e568b107e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 25 Jun 2023 22:02:48 +0800 Subject: [PATCH] =?UTF-8?q?mod:=20=E6=95=B0=E6=8D=AE=E8=AF=B7=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 2 + lib/http/dynamics.dart | 3 +- lib/models/common/dynamics_type.dart | 11 + lib/models/dynamics/result.dart | 90 ++++- lib/pages/dynamics/controller.dart | 16 +- lib/pages/dynamics/view.dart | 123 ++++--- lib/pages/dynamics/widgets/dynamic_panel.dart | 322 ++++++++++++++---- 7 files changed, 466 insertions(+), 101 deletions(-) create mode 100644 lib/models/common/dynamics_type.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index d54eb0f9..77741dd8 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -136,6 +136,8 @@ class Api { // 关注的up动态 // https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all + // https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all?timezone_offset=-480&type=video&page=1&features=itemOpusStyle + // https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all?host_mid=548196587&offset=&page=1&features=itemOpusStyle static const String followDynamic = '/x/polymer/web-dynamic/v1/feed/all'; // 获取稍后再看 diff --git a/lib/http/dynamics.dart b/lib/http/dynamics.dart index 56bc1174..0353f1a8 100644 --- a/lib/http/dynamics.dart +++ b/lib/http/dynamics.dart @@ -10,7 +10,8 @@ class DynamicsHttp { var res = await Request().get(Api.followDynamic, data: { 'type': type ?? 'all', 'page': page ?? 1, - 'offest': page == 1 ? '' : offset, + 'timezone_offset': '-480', + 'offset': page == 1 ? '' : offset, 'features': 'itemOpusStyle' }); if (res.data['code'] == 0) { diff --git a/lib/models/common/dynamics_type.dart b/lib/models/common/dynamics_type.dart new file mode 100644 index 00000000..85a5270a --- /dev/null +++ b/lib/models/common/dynamics_type.dart @@ -0,0 +1,11 @@ +enum DynamicsType { + all, + video, + pgc, + article, +} + +extension BusinessTypeExtension on DynamicsType { + String get values => ['all', 'video', 'pgc', 'article'][index]; + String get labels => ['全部', '视频投稿', '追番追剧', '专栏'][index]; +} diff --git a/lib/models/dynamics/result.dart b/lib/models/dynamics/result.dart index 9b909360..6c7b5ccd 100644 --- a/lib/models/dynamics/result.dart +++ b/lib/models/dynamics/result.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + class DynamicsDataModel { DynamicsDataModel({ this.hasMore, @@ -186,13 +188,17 @@ class DynamicAddModel { DynamicAddModel({ this.type, this.vote, + this.ugc, }); String? type; Vote? vote; + Ugc? ugc; + DynamicAddModel.fromJson(Map json) { type = json['type']; vote = json['vote'] != null ? Vote.fromJson(json['vote']) : null; + ugc = json['ugc'] != null ? Ugc.fromJson(json['ugc']) : null; } } @@ -214,7 +220,7 @@ class Vote { int? defaultShare; int? endTime; int? joinNum; - String? status; + int? status; String? type; int? uid; int? voteId; @@ -232,6 +238,39 @@ class Vote { } } +class Ugc { + Ugc({ + this.cover, + this.descSecond, + this.duration, + this.headText, + this.idStr, + this.jumpUrl, + this.multiLine, + this.title, + }); + + String? cover; + String? descSecond; + String? duration; + String? headText; + String? idStr; + String? jumpUrl; + String? multiLine; + String? title; + + Ugc.fromJson(Map json) { + cover = json['cover']; + descSecond = json['desc_second']; + duration = json['duration']; + headText = json['head_text']; + idStr = json['id_str']; + jumpUrl = json['jump_url']; + multiLine = json['multi_line']; + title = json['title']; + } +} + class DynamicDescModel { DynamicDescModel({ this.richTextNodes, @@ -258,6 +297,8 @@ class DynamicMajorModel { this.draw, this.ugcSeason, this.opus, + this.pgc, + this.liveRcmd, this.none, this.type, }); @@ -266,6 +307,8 @@ class DynamicMajorModel { DynamicDrawModel? draw; DynamicArchiveModel? ugcSeason; DynamicOpusModel? opus; + DynamicArchiveModel? pgc; + DynamicLiveModel? liveRcmd; DynamicNoneModel? none; // MAJOR_TYPE_DRAW 图片 // MAJOR_TYPE_ARCHIVE 视频 @@ -283,6 +326,11 @@ class DynamicMajorModel { : null; opus = json['opus'] != null ? DynamicOpusModel.fromJson(json['opus']) : null; + pgc = + json['pgc'] != null ? DynamicArchiveModel.fromJson(json['pgc']) : null; + liveRcmd = json['live_rcmd'] != null + ? DynamicLiveModel.fromJson(json['live_rcmd']) + : null; none = json['none'] != null ? DynamicNoneModel.fromJson(json['none']) : null; type = json['type']; @@ -506,6 +554,46 @@ class DynamicDrawItemModel { } } +class DynamicLiveModel { + DynamicLiveModel({ + this.content, + }); + + String? content; + int? type; + Map? livePlayInfo; + int? uid; + String? parentAreaName; + int? roomId; + String? liveId; + int? liveStatus; + String? cover; + int? online; + String? areaName; + String? title; + int? liveStartTime; + + DynamicLiveModel.fromJson(Map json) { + content = json['content']; + if (json['content'] != null) { + Map data = jsonDecode(json['content']); + + type = data['type']; + Map livePlayInfo = data['live_play_info']; + uid = livePlayInfo['uid']; + parentAreaName = livePlayInfo['parent_area_name']; + roomId = livePlayInfo['room_id']; + liveId = livePlayInfo['live_id']; + liveStatus = livePlayInfo['live_status']; + cover = livePlayInfo['cover']; + online = livePlayInfo['online']; + areaName = livePlayInfo['area_name']; + title = livePlayInfo['title']; + liveStartTime = livePlayInfo['live_start_time']; + } + } +} + // 动态状态 转发、评论、点赞 class ModuleStatModel { ModuleStatModel({ diff --git a/lib/pages/dynamics/controller.dart b/lib/pages/dynamics/controller.dart index 438198b0..4bc23b60 100644 --- a/lib/pages/dynamics/controller.dart +++ b/lib/pages/dynamics/controller.dart @@ -4,14 +4,15 @@ import 'package:pilipala/models/dynamics/result.dart'; class DynamicsController extends GetxController { int page = 1; - String reqType = 'all'; - String? offset; + String? offset = ''; RxList? dynamicsList = [DynamicItemModel()].obs; + RxString dynamicsType = 'all'.obs; + RxString dynamicsTypeLabel = '全部'.obs; Future queryFollowDynamic({type = 'init'}) async { var res = await DynamicsHttp.followDynamic( - page: page, - type: reqType, + page: type == 'init' ? 1 : page, + type: dynamicsType.value, offset: offset, ); if (res['status']) { @@ -21,7 +22,14 @@ class DynamicsController extends GetxController { dynamicsList!.addAll(res['data'].items); } offset = res['data'].offset; + page++; } return res; } + + onSelectType(value, label) { + dynamicsType.value = value; + dynamicsTypeLabel.value = label; + queryFollowDynamic(); + } } diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index fcf6d1f2..e7a0896e 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/models/common/dynamics_type.dart'; import 'package:pilipala/models/dynamics/result.dart'; import 'controller.dart'; @@ -17,7 +18,8 @@ class _DynamicsPageState extends State with AutomaticKeepAliveClientMixin { DynamicsController _dynamicsController = Get.put(DynamicsController()); Future? _futureBuilderFuture; - + final ScrollController scrollController = ScrollController(); + bool _isLoadingMore = false; @override bool get wantKeepAlive => true; @@ -25,6 +27,19 @@ class _DynamicsPageState extends State void initState() { super.initState(); _futureBuilderFuture = _dynamicsController.queryFollowDynamic(); + + scrollController.addListener( + () async { + if (scrollController.position.pixels >= + scrollController.position.maxScrollExtent - 200) { + if (!_isLoadingMore) { + _isLoadingMore = true; + await _dynamicsController.queryFollowDynamic(type: 'onLoad'); + _isLoadingMore = false; + } + } + }, + ); } @override @@ -34,46 +49,78 @@ class _DynamicsPageState extends State centerTitle: false, title: const Text('动态'), actions: [ - IconButton(onPressed: () {}, icon: const Icon(Icons.more_horiz)), - const SizedBox(width: 10) - ], - ), - body: FutureBuilder( - future: _dynamicsController.queryFollowDynamic(), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data; - if (data['status']) { - List list = _dynamicsController.dynamicsList!; - return Obx( - () => ListView.builder( - shrinkWrap: true, - itemCount: list.length, - itemBuilder: (BuildContext context, index) { - return DynamicPanel(item: list[index]); - }, - ), - ); - } else { - return CustomScrollView( - slivers: [ - HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), + Obx( + () => PopupMenuButton( + initialValue: _dynamicsController.dynamicsType.value, + position: PopupMenuPosition.under, + itemBuilder: (context) => [ + for (var i in DynamicsType.values) ...[ + PopupMenuItem( + value: i.values, + onTap: () => + _dynamicsController.onSelectType(i.values, i.labels), + child: Text(i.labels), ) ], - ); - } - } else { - // 骨架屏 - // return SliverList( - // delegate: SliverChildBuilderDelegate((context, index) { - // return const VideoCardHSkeleton(); - // }, childCount: 10), - // ); - return Text('加载中'); - } + ], + child: Row( + children: [ + Text( + _dynamicsController.dynamicsTypeLabel.value, + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + ), + const SizedBox(width: 10) + ], + ), + ), + ), + const SizedBox(width: 4) + ], + ), + body: RefreshIndicator( + onRefresh: () async { + await _dynamicsController.queryFollowDynamic(); }, + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map data = snapshot.data; + if (data['status']) { + List list = _dynamicsController.dynamicsList!; + return Obx( + () => ListView.builder( + controller: scrollController, + shrinkWrap: true, + itemCount: list.length, + itemBuilder: (BuildContext context, index) { + return DynamicPanel(item: list[index]); + }, + ), + ); + } else { + return CustomScrollView( + slivers: [ + HttpError( + errMsg: data['msg'], + fn: () => setState(() {}), + ) + ], + ); + } + } else { + // 骨架屏 + // return SliverList( + // delegate: SliverChildBuilderDelegate((context, index) { + // return const VideoCardHSkeleton(); + // }, childCount: 10), + // ); + return Text('加载中'); + } + }, + ), ), ); } diff --git a/lib/pages/dynamics/widgets/dynamic_panel.dart b/lib/pages/dynamics/widgets/dynamic_panel.dart index c03c1060..4c2fb233 100644 --- a/lib/pages/dynamics/widgets/dynamic_panel.dart +++ b/lib/pages/dynamics/widgets/dynamic_panel.dart @@ -99,7 +99,11 @@ class DynamicPanel extends StatelessWidget { ), ), ], - Text.rich(richNode(item, context)), + Text.rich( + richNode(item, context), + maxLines: 4, + overflow: TextOverflow.ellipsis, + ), ], )); } @@ -135,7 +139,25 @@ class DynamicPanel extends StatelessWidget { ], ), const SizedBox(height: 2), - Text(item.modules.moduleDynamic.desc.text), + 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), + maxLines: 4, + overflow: TextOverflow.ellipsis, + ), + // Text(item.modules.moduleDynamic.desc.text), const SizedBox(height: 4), ], Padding( @@ -188,60 +210,203 @@ class DynamicPanel extends StatelessWidget { .copyWith(fontWeight: FontWeight.bold), ), const SizedBox(height: 2), - Text(item.modules.moduleDynamic.major.opus.summary.richTextNodes - .first.text), + if (item.modules.moduleDynamic.major.opus.summary.text != + 'undefined') + Text( + item.modules.moduleDynamic.major.opus.summary.richTextNodes + .first.text, + maxLines: 4, + overflow: TextOverflow.ellipsis, + ), picWidget(item, context) ], ), ); // 转发 case 'DYNAMIC_TYPE_FORWARD': - switch (item.orig.type) { - // 递归 - case 'DYNAMIC_TYPE_AV': - 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_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('渲染出错了1'); - } + 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), + ); + // switch (item.orig.type) { + // // 递归 + // case 'DYNAMIC_TYPE_AV': + // 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_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), + // ); + // case 'DYNAMIC_TYPE_PGC': + // 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_LIVE_RCMD': + // 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('渲染出错了1'); + // } // 直播 case 'DYNAMIC_TYPE_LIVE_RCMD': - return const Text('DYNAMIC_TYPE_LIVE_RCMD'); + 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), + ), + ], + ), + ], + 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), + ], + if (floor == 2 && item.modules.moduleDynamic.desc != null) ...[ + Text.rich(richNode(item, context)), + 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: item.modules.moduleDynamic.major.liveRcmd.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(item.modules.moduleDynamic.major + .liveRcmd.areaName ?? + ''), + ], + ), + ), + ], + ), + )), + ], + ); + }), + ), + const SizedBox(height: 6), + Padding( + padding: floor == 1 + ? const EdgeInsets.only(left: 12, right: 12) + : EdgeInsets.zero, + child: Text( + item.modules.moduleDynamic.major.liveRcmd.title, + maxLines: 1, + style: const TextStyle(fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox(height: 2), + ], + ); // 合集 case 'DYNAMIC_TYPE_UGC_SEASON': return videoSeasonWidget(item, context, 'ugcSeason'); @@ -276,7 +441,8 @@ class DynamicPanel extends StatelessWidget { ], ) : const SizedBox(height: 0); - + case 'DYNAMIC_TYPE_PGC': + return videoSeasonWidget(item, context, 'pgc', floor: floor); case 'DYNAMIC_TYPE_NONE': return Row( children: [ @@ -371,7 +537,7 @@ class DynamicPanel extends StatelessWidget { : len ~/ crossCount + 1) + 6; return Container( - padding: const EdgeInsets.only(top: 6), + padding: const EdgeInsets.only(top: 4), height: height, child: GridView.count( padding: EdgeInsets.zero, @@ -401,6 +567,7 @@ class DynamicPanel extends StatelessWidget { Map dynamicProperty = { 'ugcSeason': item.modules.moduleDynamic.major.ugcSeason, 'archive': item.modules.moduleDynamic.major.archive, + 'pgc': item.modules.moduleDynamic.major.pgc }; dynamic content = dynamicProperty[type]; @@ -427,8 +594,8 @@ class DynamicPanel extends StatelessWidget { ], ), ], + const SizedBox(height: 4), if (item.modules.moduleDynamic.topic != null) ...[ - const SizedBox(height: 4), Padding( padding: floor == 2 ? EdgeInsets.zero @@ -440,8 +607,12 @@ class DynamicPanel extends StatelessWidget { ), ), ), + const SizedBox(height: 6), + ], + if (floor == 2 && item.modules.moduleDynamic.desc != null) ...[ + Text.rich(richNode(item, context)), + const SizedBox(height: 6), ], - const SizedBox(height: 6), GestureDetector( onTap: () {}, child: LayoutBuilder(builder: (context, box) { @@ -487,8 +658,9 @@ class DynamicPanel extends StatelessWidget { color: Colors.white), child: Row( children: [ - Text(content.durationText), - const SizedBox(width: 10), + Text(content.durationText ?? ''), + if (content.durationText != null) + const SizedBox(width: 10), Text(content.stat.play + '次围观'), const SizedBox(width: 10), Text(content.stat.danmaku + '条弹幕') @@ -526,12 +698,13 @@ class DynamicPanel extends StatelessWidget { InlineSpan richNode(item, context) { TextStyle authorStyle = - TextStyle(color: Theme.of(context).colorScheme.primary, fontSize: 13); + 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)); } + // @用户 if (i.type == 'RICH_TEXT_NODE_TYPE_AT') { spanChilds.add( WidgetSpan( @@ -542,7 +715,7 @@ class DynamicPanel extends StatelessWidget { GestureDetector( onTap: () {}, child: Text( - '@${item.modules.moduleAuthor.name}', + '${i.text}', style: authorStyle, ), ), @@ -551,6 +724,7 @@ class DynamicPanel extends StatelessWidget { ), ); } + // 话题 if (i.type == 'RICH_TEXT_NODE_TYPE_TOPIC') { spanChilds.add( WidgetSpan( @@ -564,6 +738,7 @@ class DynamicPanel extends StatelessWidget { ), ); } + // 网页链接 if (i.type == 'RICH_TEXT_NODE_TYPE_WEB') { spanChilds.add( WidgetSpan( @@ -577,6 +752,7 @@ class DynamicPanel extends StatelessWidget { ), ); } + // 投票 if (i.type == 'RICH_TEXT_NODE_TYPE_VOTE') { spanChilds.add( WidgetSpan( @@ -590,6 +766,7 @@ class DynamicPanel extends StatelessWidget { ), ); } + // 表情 if (i.type == 'RICH_TEXT_NODE_TYPE_EMOJI') { spanChilds.add( WidgetSpan( @@ -602,7 +779,38 @@ class DynamicPanel extends StatelessWidget { ), ); } + // 抽奖 + if (i.type == 'RICH_TEXT_NODE_TYPE_LOTTERY') { + spanChilds.add( + WidgetSpan( + child: GestureDetector( + onTap: () {}, + child: Text( + ' ${i.origText} ', + style: authorStyle, + ), + ), + ), + ); + } + + /// TODO 商品 + if (i.type == 'RICH_TEXT_NODE_TYPE_GOODS') { + spanChilds.add( + WidgetSpan( + child: GestureDetector( + onTap: () {}, + child: Text( + ' ${i.text} ', + style: authorStyle, + ), + ), + ), + ); + } } - return TextSpan(children: spanChilds); + return TextSpan( + children: spanChilds, + ); } }