diff --git a/lib/common/widgets/badge.dart b/lib/common/widgets/badge.dart new file mode 100644 index 00000000..035be598 --- /dev/null +++ b/lib/common/widgets/badge.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; + +Widget pBadge( + text, + context, + double? top, + double? right, + double? bottom, + double? left, { + type = 'primary', +}) { + Color bgColor = Theme.of(context).colorScheme.primary; + Color color = Theme.of(context).colorScheme.onPrimary; + if (type == 'gray') { + bgColor = Colors.black54.withOpacity(0.4); + color = Colors.white; + } + return Positioned( + top: top, + left: left, + right: right, + bottom: bottom, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 1, horizontal: 6), + decoration: + BoxDecoration(borderRadius: BorderRadius.circular(4), color: bgColor), + child: Text( + text, + style: TextStyle(fontSize: 11, color: color), + ), + ), + ); +} diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index 9da58416..435b4826 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -2,6 +2,7 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:pilipala/common/constants.dart'; +import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/stat/view.dart'; import 'package:pilipala/http/search.dart'; import 'package:pilipala/utils/utils.dart'; @@ -84,24 +85,13 @@ class VideoCardH extends StatelessWidget { ), ), // Image.network( videoItem['pic'], width: double.infinity, height: double.infinity,), - Positioned( - right: 4, - bottom: 4, - child: Container( - padding: const EdgeInsets.symmetric( - vertical: 1, horizontal: 6), - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(4), - color: - Colors.black54.withOpacity(0.4)), - child: Text( - Utils.timeFormat(videoItem.duration!), - style: const TextStyle( - fontSize: 11, color: Colors.white), - ), - ), - ) + pBadge(Utils.timeFormat(videoItem.duration!), + context, null, 6.0, 6.0, null, + type: 'gray'), + if (videoItem.rcmdReason != null && + videoItem.rcmdReason.content != '') + pBadge(videoItem.rcmdReason.content, + context, 6.0, 6.0, null, null), ], ); }, @@ -171,22 +161,22 @@ class VideoContent extends StatelessWidget { ), ], const Spacer(), - if (videoItem.rcmdReason != null && - videoItem.rcmdReason.content != '') - Container( - padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - border: Border.all( - color: Theme.of(context).colorScheme.surfaceTint), - ), - child: Text( - videoItem.rcmdReason.content, - style: TextStyle( - fontSize: 9, - color: Theme.of(context).colorScheme.surfaceTint), - ), - ), + // if (videoItem.rcmdReason != null && + // videoItem.rcmdReason.content != '') + // Container( + // padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5), + // decoration: BoxDecoration( + // borderRadius: BorderRadius.circular(4), + // border: Border.all( + // color: Theme.of(context).colorScheme.surfaceTint), + // ), + // child: Text( + // videoItem.rcmdReason.content, + // style: TextStyle( + // fontSize: 9, + // color: Theme.of(context).colorScheme.surfaceTint), + // ), + // ), const SizedBox(height: 4), Row( children: [ diff --git a/lib/http/api.dart b/lib/http/api.dart index 77741dd8..3c24a618 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -98,6 +98,7 @@ class Api { static const String relationMod = '/x/relation/modify'; // 评论列表 + // https://api.bilibili.com/x/v2/reply/main?csrf=6e22efc1a47225ea25f901f922b5cfdd&mode=3&oid=254175381&pagination_str=%7B%22offset%22:%22%22%7D&plat=1&seek_rpid=0&type=11 static const String replyList = '/x/v2/reply'; // 楼中楼 diff --git a/lib/models/dynamics/result.dart b/lib/models/dynamics/result.dart index f886f784..0584fb21 100644 --- a/lib/models/dynamics/result.dart +++ b/lib/models/dynamics/result.dart @@ -22,7 +22,7 @@ class DynamicsDataModel { // 单个动态 class DynamicItemModel { DynamicItemModel({ - // this.basic, + this.basic, this.idStr, this.modules, this.orig, @@ -30,7 +30,7 @@ class DynamicItemModel { this.visible, }); - // Map? basic; + Map? basic; String? idStr; ItemModulesModel? modules; ItemOrigModel? orig; @@ -38,7 +38,7 @@ class DynamicItemModel { bool? visible; DynamicItemModel.fromJson(Map json) { - // basic = json['basic']; + basic = json['basic']; idStr = json['id_str']; modules = ItemModulesModel.fromJson(json['modules']); orig = json['orig'] != null ? ItemOrigModel.fromJson(json['orig']) : null; @@ -140,7 +140,7 @@ class ModuleAuthorModel { name = json['name']; pubAction = json['pub_action']; pubTime = json['pub_time']; - pubTs = json['pub_ts']; + pubTs = json['pub_ts'] == 0 ? null : json['pub_ts']; type = json['type']; } } diff --git a/lib/models/user/history.dart b/lib/models/user/history.dart index ead6bded..19698aa9 100644 --- a/lib/models/user/history.dart +++ b/lib/models/user/history.dart @@ -158,7 +158,7 @@ class History { History.fromJson(Map json) { oid = json['oid']; epid = json['epid']; - bvid = json['bvid']; + bvid = json['bvid'] == '' ? null : json['bvid']; page = json['page']; cid = json['cid'] == 0 ? null : json['cid']; part = json['part']; diff --git a/lib/pages/dynamics/controller.dart b/lib/pages/dynamics/controller.dart index 4bc23b60..e4399e5b 100644 --- a/lib/pages/dynamics/controller.dart +++ b/lib/pages/dynamics/controller.dart @@ -32,4 +32,32 @@ class DynamicsController extends GetxController { dynamicsTypeLabel.value = label; queryFollowDynamic(); } + + pushDetail(item, floor) { + switch (item!.type) { + case 'DYNAMIC_TYPE_FORWARD': + print('转发的动态'); + Get.toNamed('/dynamicDetail', + arguments: {'item': item, 'floor': floor}); + break; + case 'DYNAMIC_TYPE_DRAW': + Get.toNamed('/dynamicDetail', + arguments: {'item': item, 'floor': floor}); + break; + case 'DYNAMIC_TYPE_AV': + print('视频'); + break; + case 'DYNAMIC_TYPE_ARTICLE': + print('文章/专栏'); + break; + case 'DYNAMIC_TYPE_PGC': + print('番剧'); + break; + case 'DYNAMIC_TYPE_WORD': + print('纯文本'); + Get.toNamed('/dynamicDetail', + arguments: {'item': item, 'floor': floor}); + break; + } + } } diff --git a/lib/pages/dynamics/deatil/controller.dart b/lib/pages/dynamics/deatil/controller.dart new file mode 100644 index 00000000..41f7ef74 --- /dev/null +++ b/lib/pages/dynamics/deatil/controller.dart @@ -0,0 +1,80 @@ +import 'package:get/get.dart'; +import 'package:pilipala/http/reply.dart'; +import 'package:pilipala/models/dynamics/result.dart'; +import 'package:pilipala/models/video/reply/data.dart'; +import 'package:pilipala/models/video/reply/item.dart'; + +class DynamicDetailController extends GetxController { + DynamicDetailController(this.oid, this.type); + int? oid; + int? type; + var item; + int? floor; + int currentPage = 0; + bool isLoadingMore = false; + RxString noMore = ''.obs; + RxList replyList = [ReplyItemModel()].obs; + RxInt acount = 0.obs; + + @override + void onInit() { + super.onInit(); + item = Get.arguments['item']; + floor = Get.arguments['floor']; + if (floor == 1) { + acount.value = + int.parse(item!.modules!.moduleStat!.comment!.count ?? '0'); + } + } + + Future queryReplyList({reqType = 'init'}) async { + if (reqType == 'init') { + currentPage = 0; + } + var res = await ReplyHttp.replyList( + oid: oid!, + pageNum: currentPage + 1, + type: type!, + ); + if (res['status']) { + res['data'] = ReplyData.fromJson(res['data']); + acount.value = res['data'].page.acount; + if (res['data'].replies.isNotEmpty) { + currentPage = currentPage + 1; + noMore.value = '加载中...'; + if (replyList.isEmpty) { + noMore.value = '没有更多了'; + return; + } + } else { + if (currentPage == 0) { + noMore.value = '还没有评论'; + } else { + noMore.value = '没有更多了'; + return; + } + } + List replies = res['data'].replies; + if (reqType == 'init') { + // 添加置顶回复 + if (res['data'].upper.top != null) { + bool flag = false; + for (var i = 0; i < res['data'].topReplies.length; i++) { + if (res['data'].topReplies[i].rpid == res['data'].upper.top.rpid) { + flag = true; + } + } + if (!flag) { + replies.insert(0, res['data'].upper.top); + } + } + replies.insertAll(0, res['data'].topReplies); + replyList.value = replies; + } else { + replyList.addAll(replies); + } + } + isLoadingMore = false; + return res; + } +} diff --git a/lib/pages/dynamics/deatil/index.dart b/lib/pages/dynamics/deatil/index.dart new file mode 100644 index 00000000..8746d8ae --- /dev/null +++ b/lib/pages/dynamics/deatil/index.dart @@ -0,0 +1,4 @@ +library dynamic_detail; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/dynamics/deatil/view.dart b/lib/pages/dynamics/deatil/view.dart new file mode 100644 index 00000000..213c145d --- /dev/null +++ b/lib/pages/dynamics/deatil/view.dart @@ -0,0 +1,248 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/skeleton/video_reply.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/pages/dynamics/deatil/index.dart'; +import 'package:pilipala/pages/dynamics/widgets/author_panel.dart'; +import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart'; + +import '../widgets/dynamic_panel.dart'; + +class DynamicDetailPage extends StatefulWidget { + // const DynamicDetailPage({super.key}); + const DynamicDetailPage({Key? key}) : super(key: key); + + @override + State createState() => _DynamicDetailPageState(); +} + +class _DynamicDetailPageState extends State { + late DynamicDetailController? _dynamicDetailController; + Future? _futureBuilderFuture; + late StreamController titleStreamC; // appBar title + final ScrollController scrollController = ScrollController(); + bool _visibleTitle = false; + + @override + void initState() { + super.initState(); + int oid = 0; + int type = 0; + if (Get.arguments['floor'] == 1) { + oid = int.parse(Get.arguments['item'].basic!['comment_id_str']); + type = Get.arguments['item'].basic!['comment_type']; + } else { + oid = Get.arguments['item'].modules.moduleDynamic.major.draw.id; + type = 11; + } + + _dynamicDetailController = Get.put(DynamicDetailController(oid, type)); + _futureBuilderFuture = _dynamicDetailController!.queryReplyList(); + titleStreamC = StreamController(); + scrollController.addListener(_listen); + } + + void _listen() async { + if (scrollController.position.pixels >= + scrollController.position.maxScrollExtent - 300) { + if (!_dynamicDetailController!.isLoadingMore) { + _dynamicDetailController!.isLoadingMore = true; + await _dynamicDetailController!.queryReplyList(reqType: 'onLoad'); + } + } + + if (scrollController.offset > 55 && !_visibleTitle) { + _visibleTitle = true; + titleStreamC.add(true); + } else if (scrollController.offset <= 55 && _visibleTitle) { + _visibleTitle = false; + titleStreamC.add(false); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + elevation: 0, + scrolledUnderElevation: 0, + centerTitle: false, + titleSpacing: 0, + title: StreamBuilder( + stream: titleStreamC.stream, + initialData: false, + builder: (context, AsyncSnapshot snapshot) { + return AnimatedOpacity( + opacity: snapshot.data ? 1 : 0, + duration: const Duration(milliseconds: 300), + child: author(_dynamicDetailController!.item, context), + ); + }, + ), + // actions: _detailModel != null ? appBarAction() : [], + ), + body: RefreshIndicator( + onRefresh: () async { + await _dynamicDetailController!.queryReplyList(); + }, + child: CustomScrollView( + controller: scrollController, + slivers: [ + SliverToBoxAdapter( + child: DynamicPanel( + item: _dynamicDetailController!.item, + source: 'detail', + ), + ), + SliverPersistentHeader( + delegate: _MySliverPersistentHeaderDelegate( + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + border: Border( + top: BorderSide( + width: 0.6, + color: Theme.of(context).dividerColor.withOpacity(0.05), + ), + ), + ), + height: 45, + padding: const EdgeInsets.only(left: 15, right: 12), + child: Row( + children: [ + Obx( + () => AnimatedSwitcher( + duration: const Duration(milliseconds: 400), + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition( + scale: animation, child: child); + }, + child: Text( + '${_dynamicDetailController!.acount.value}', + key: ValueKey( + _dynamicDetailController!.acount.value), + ), + ), + ), + const Text('条回复'), + const Spacer(), + // TextButton.icon( + // onPressed: () {}, + // icon: const Icon( + // Icons.subject_rounded, + // size: 15, + // ), + // style: TextButton.styleFrom( + // padding: const EdgeInsets.fromLTRB(12, 0, 12, 0), + // foregroundColor: + // Theme.of(context).colorScheme.outline, + // ), + // label: Text( + // '按时间', + // style: TextStyle( + // fontSize: Theme.of(context) + // .textTheme + // .titleSmall! + // .fontSize, + // ), + // ), + // ), + ], + ), + ), + ), + pinned: true, + ), + FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map data = snapshot.data as Map; + if (snapshot.data['status']) { + // 请求成功 + return Obx( + () => SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + if (index == + _dynamicDetailController!.replyList.length) { + return Container( + padding: EdgeInsets.only( + bottom: + MediaQuery.of(context).padding.bottom), + height: + MediaQuery.of(context).padding.bottom + 100, + child: Center( + child: Obx(() => Text( + _dynamicDetailController!.noMore.value)), + ), + ); + } else { + return ReplyItem( + replyItem: _dynamicDetailController! + .replyList[index], + showReplyRow: true, + replyLevel: '1'); + } + }, + childCount: + _dynamicDetailController!.replyList.length + 1, + ), + ), + ); + } else { + // 请求错误 + return HttpError( + errMsg: data['msg'], + fn: () => setState(() {}), + ); + } + } else { + // 骨架屏 + return SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + return const VideoReplySkeleton(); + }, childCount: 5), + ); + } + }, + ) + ], + ), + ), + ); + } +} + +class _MySliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate { + final double _minExtent = 45; + final double _maxExtent = 45; + final Widget child; + + _MySliverPersistentHeaderDelegate({required this.child}); + + @override + Widget build( + BuildContext context, double shrinkOffset, bool overlapsContent) { + //创建child子组件 + //shrinkOffset:child偏移值minExtent~maxExtent + //overlapsContent:SliverPersistentHeader覆盖其他子组件返回true,否则返回false + return child; + } + + //SliverPersistentHeader最大高度 + @override + double get maxExtent => _maxExtent; + + //SliverPersistentHeader最小高度 + @override + double get minExtent => _minExtent; + + @override + bool shouldRebuild(covariant _MySliverPersistentHeaderDelegate oldDelegate) { + return true; + } +} diff --git a/lib/pages/dynamics/widgets/action_panel.dart b/lib/pages/dynamics/widgets/action_panel.dart index b5ae78c4..673d04a4 100644 --- a/lib/pages/dynamics/widgets/action_panel.dart +++ b/lib/pages/dynamics/widgets/action_panel.dart @@ -1,10 +1,15 @@ // 操作栏 import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:get/get.dart'; import 'package:pilipala/models/dynamics/result.dart'; +import 'package:pilipala/pages/dynamics/index.dart'; + +final DynamicsController _dynamicsController = Get.put(DynamicsController()); Widget action(item, context) { ModuleStatModel stat = item.modules.moduleStat; + return Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ @@ -14,14 +19,22 @@ Widget action(item, context) { FontAwesomeIcons.shareFromSquare, size: 16, ), + style: TextButton.styleFrom( + padding: const EdgeInsets.fromLTRB(15, 0, 15, 0), + foregroundColor: Theme.of(context).colorScheme.outline, + ), label: Text(stat.forward!.count ?? '转发'), ), TextButton.icon( - onPressed: () {}, + onPressed: () => _dynamicsController.pushDetail(item, 1), icon: const Icon( FontAwesomeIcons.comment, size: 16, ), + style: TextButton.styleFrom( + padding: const EdgeInsets.fromLTRB(15, 0, 15, 0), + foregroundColor: Theme.of(context).colorScheme.outline, + ), label: Text(stat.comment!.count ?? '评论'), ), TextButton.icon( @@ -30,6 +43,10 @@ Widget action(item, context) { FontAwesomeIcons.thumbsUp, size: 16, ), + style: TextButton.styleFrom( + padding: const EdgeInsets.fromLTRB(15, 0, 15, 0), + foregroundColor: Theme.of(context).colorScheme.outline, + ), label: Text(stat.like!.count ?? '点赞'), ) ], diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart new file mode 100644 index 00000000..f1d86067 --- /dev/null +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; + +Widget author(item, context) { + return Container( + padding: const EdgeInsets.fromLTRB(12, 12, 12, 8), + child: Row( + children: [ + GestureDetector( + onTap: () { + print('个人主页'); + }, + child: NetworkImgLayer( + width: 40, + height: 40, + type: 'avatar', + src: item.modules.moduleAuthor.face, + ), + ), + const SizedBox(width: 10), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.modules.moduleAuthor.name, + style: TextStyle( + fontSize: Theme.of(context).textTheme.titleSmall!.fontSize, + ), + ), + DefaultTextStyle.merge( + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, + ), + child: Row( + children: [ + Text(item.modules.moduleAuthor.pubTime), + if (item.modules.moduleAuthor.pubTime != '' && + item.modules.moduleAuthor.pubAction != '') + const Text(' '), + Text(item.modules.moduleAuthor.pubAction), + ], + ), + ) + ], + ), + ], + ), + ); +} diff --git a/lib/pages/dynamics/widgets/content_panel.dart b/lib/pages/dynamics/widgets/content_panel.dart index 702b03f6..872be803 100644 --- a/lib/pages/dynamics/widgets/content_panel.dart +++ b/lib/pages/dynamics/widgets/content_panel.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'rich_node_panel.dart'; -Widget content(item, context) { +Widget content(item, context, source) { TextStyle authorStyle = TextStyle(color: Theme.of(context).colorScheme.primary); return Container( @@ -22,7 +22,7 @@ Widget content(item, context) { ], Text.rich( richNode(item, context), - maxLines: 3, + 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 f356a740..5fa50afd 100644 --- a/lib/pages/dynamics/widgets/dynamic_panel.dart +++ b/lib/pages/dynamics/widgets/dynamic_panel.dart @@ -1,28 +1,26 @@ 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/models/dynamics/result.dart'; -import 'package:pilipala/utils/utils.dart'; - +import 'package:pilipala/pages/dynamics/index.dart'; import 'action_panel.dart'; -import 'article_panel.dart'; +import 'author_panel.dart'; import 'content_panel.dart'; -import 'live_rcmd_panel.dart'; -import 'pic_panel.dart'; -import 'rich_node_panel.dart'; +import 'forward_panel.dart'; class DynamicPanel extends StatelessWidget { - DynamicItemModel? item; - DynamicPanel({this.item, Key? key}); + var item; + String? source; + DynamicPanel({this.item, this.source, Key? key}) : super(key: key); + final DynamicsController _dynamicsController = Get.put(DynamicsController()); @override Widget build(BuildContext context) { return Container( + padding: source == 'detail' + ? const EdgeInsets.only(bottom: 12) + : EdgeInsets.zero, decoration: BoxDecoration( border: Border( - top: BorderSide( + bottom: BorderSide( width: 8, color: Theme.of(context).dividerColor.withOpacity(0.05), ), @@ -35,334 +33,20 @@ class DynamicPanel extends StatelessWidget { borderRadius: BorderRadius.circular(0), ), child: InkWell( - onTap: () {}, + onTap: () => _dynamicsController.pushDetail(item, 1), child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ author(item, context), if (item!.modules!.moduleDynamic!.desc != null) - content(item, context), - Padding( - padding: EdgeInsets.zero, - // padding: const EdgeInsets.only(left: 15, right: 15), - child: forWard(item, context), - ), - action(item, context), - ], - ), - ), - ), - ); - } - - Widget author(item, context) { - return Container( - padding: const EdgeInsets.fromLTRB(12, 12, 12, 8), - child: Row( - children: [ - NetworkImgLayer( - width: 40, - height: 40, - type: 'avatar', - src: item.modules.moduleAuthor.face, - ), - const SizedBox(width: 10), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(item.modules.moduleAuthor.name), - DefaultTextStyle.merge( - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, - ), - child: Row( - children: [ - Text(item.modules.moduleAuthor.pubTime), - if (item.modules.moduleAuthor.pubTime != '' && - item.modules.moduleAuthor.pubAction != '') - const Text(' '), - Text(item.modules.moduleAuthor.pubAction), - ], - ), - ) - ], - ), - ], - ), - ); - } - - // 转发 - Widget forWard(item, context, {floor = 1}) { - TextStyle authorStyle = - TextStyle(color: Theme.of(context).colorScheme.primary); - switch (item.type) { - // 图文 - case 'DYNAMIC_TYPE_DRAW': - 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), - ), - ], - ), + content(item, context, source), + forWard(item, context, _dynamicsController, source), 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, - ), - ), - ), - ], - Text.rich( - richNode(item, context), - maxLines: 4, - overflow: TextOverflow.ellipsis, - ), - const SizedBox(height: 4), - ], - Padding( - padding: floor == 2 - ? EdgeInsets.zero - : const EdgeInsets.only(left: 12, right: 12), - child: picWidget(item, context), - ), - ], - ); - // 视频 - case 'DYNAMIC_TYPE_AV': - return videoSeasonWidget(item, context, 'archive', floor: floor); - // 文章 - case 'DYNAMIC_TYPE_ARTICLE': - return articlePanel(item, context, floor: floor); - // 转发 - case 'DYNAMIC_TYPE_FORWARD': - return Container( - padding: - const EdgeInsets.only(left: 15, top: 10, right: 15, bottom: 8), - color: Theme.of(context).dividerColor.withOpacity(0.08), - child: forWard(item.orig, context, floor: 2), - ); - // 直播 - case 'DYNAMIC_TYPE_LIVE_RCMD': - return liveRcmdPanel(item, context, floor: floor); - // 合集 - case 'DYNAMIC_TYPE_UGC_SEASON': - 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_PGC': - return videoSeasonWidget(item, context, 'pgc', floor: floor); - case 'DYNAMIC_TYPE_NONE': - return Row( - children: [ - const Icon( - FontAwesomeIcons.ghost, - size: 14, - ), - const SizedBox(width: 4), - Text(item.modules.moduleDynamic.major.none.tips) - ], - ); - default: - return const SizedBox(height: 0); - } - } - - // 视频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, - 'pgc': item.modules.moduleDynamic.major.pgc - }; - dynamic content = dynamicProperty[type]; - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.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 (source == null) action(item, context), ], ), - 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), - ], - 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: 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.black54, - ], - ), - 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 ?? ''), - if (content.durationText != null) - const SizedBox(width: 10), - Text(content.stat.play + '次围观'), - const SizedBox(width: 10), - Text(content.stat.danmaku + '条弹幕') - ], - ), - ), - Image.asset( - 'assets/images/play.png', - width: 60, - height: 60, - ), - ], - ), - ), - ), - ], - ); - }), ), - 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, - ), - ), - ], + ), ); } } diff --git a/lib/pages/dynamics/widgets/forward_panel.dart b/lib/pages/dynamics/widgets/forward_panel.dart new file mode 100644 index 00000000..e2baa86c --- /dev/null +++ b/lib/pages/dynamics/widgets/forward_panel.dart @@ -0,0 +1,139 @@ +// 转发 +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:pilipala/utils/utils.dart'; + +import 'article_panel.dart'; +import 'live_rcmd_panel.dart'; +import 'pic_panel.dart'; +import 'rich_node_panel.dart'; +import 'video_panel.dart'; + +Widget forWard(item, context, ctr, source, {floor = 1}) { + TextStyle authorStyle = + TextStyle(color: Theme.of(context).colorScheme.primary); + switch (item.type) { + // 图文 + case 'DYNAMIC_TYPE_DRAW': + 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: 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, + ), + ), + ), + ], + Text.rich( + richNode(item, context), + // 被转发状态(floor=2) 隐藏 + maxLines: source == 'detail' && floor != 2 ? 999 : 4, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 4), + ], + Padding( + padding: floor == 2 + ? EdgeInsets.zero + : const EdgeInsets.only(left: 12, right: 12), + child: picWidget(item, context), + ), + ], + ); + // 视频 + case 'DYNAMIC_TYPE_AV': + return videoSeasonWidget(item, context, 'archive', floor: floor); + // 文章 + case 'DYNAMIC_TYPE_ARTICLE': + return articlePanel(item, context, floor: floor); + // 转发 + case 'DYNAMIC_TYPE_FORWARD': + return InkWell( + onTap: () => ctr.pushDetail(item.orig, floor + 1), + child: Container( + padding: + const EdgeInsets.only(left: 15, top: 10, right: 15, bottom: 8), + color: Theme.of(context).dividerColor.withOpacity(0.08), + child: forWard(item.orig, context, ctr, source, floor: floor + 1), + ), + ); + // 直播 + case 'DYNAMIC_TYPE_LIVE_RCMD': + return liveRcmdPanel(item, context, floor: floor); + // 合集 + case 'DYNAMIC_TYPE_UGC_SEASON': + 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_PGC': + return videoSeasonWidget(item, context, 'pgc', floor: floor); + case 'DYNAMIC_TYPE_NONE': + return Row( + children: [ + const Icon( + FontAwesomeIcons.ghost, + size: 14, + ), + const SizedBox(width: 4), + Text(item.modules.moduleDynamic.major.none.tips) + ], + ); + default: + return const SizedBox(height: 0); + } +} diff --git a/lib/pages/dynamics/widgets/pic_panel.dart b/lib/pages/dynamics/widgets/pic_panel.dart index 05f976ad..0bc2fd87 100644 --- a/lib/pages/dynamics/widgets/pic_panel.dart +++ b/lib/pages/dynamics/widgets/pic_panel.dart @@ -26,14 +26,14 @@ Widget picWidget(item, context) { Get.toNamed('/preview', arguments: {'initialPage': i, 'imgList': picList}); }, - child: Hero( - tag: pictures[i].src, - child: NetworkImgLayer( - src: pictures[i].src ?? pictures[i].url, - width: box.maxWidth, - height: box.maxWidth, - ), + // child: Hero( + // tag: pictures[i].src ?? pictures[i].url, + child: NetworkImgLayer( + src: pictures[i].src ?? pictures[i].url, + width: box.maxWidth, + height: box.maxWidth, ), + // ), ); }, ), @@ -53,6 +53,10 @@ Widget picWidget(item, context) { if (len == 1) { aspectRatio = pictures.first.width / pictures.first.height; height = pictures.first.height * maxWidth / pictures.first.width; + if (pictures.first.width != 1920) { + crossCount = 2; + height = maxWidth / 2 / aspectRatio; + } } else { aspectRatio = 1; height = maxWidth / @@ -62,7 +66,6 @@ Widget picWidget(item, context) { : len ~/ crossCount + 1) + 6; } - return Container( padding: const EdgeInsets.only(top: 4), decoration: BoxDecoration( diff --git a/lib/pages/dynamics/widgets/rich_node_panel.dart b/lib/pages/dynamics/widgets/rich_node_panel.dart index 76560b53..ad8d58d4 100644 --- a/lib/pages/dynamics/widgets/rich_node_panel.dart +++ b/lib/pages/dynamics/widgets/rich_node_panel.dart @@ -8,7 +8,8 @@ InlineSpan richNode(item, context) { 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)); + spanChilds.add( + TextSpan(text: i.origText, style: const TextStyle(height: 1.65))); } // @用户 if (i.type == 'RICH_TEXT_NODE_TYPE_AT') { @@ -100,6 +101,16 @@ InlineSpan richNode(item, context) { } // 抽奖 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, @@ -116,6 +127,16 @@ InlineSpan richNode(item, context) { /// 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, diff --git a/lib/pages/dynamics/widgets/video_panel.dart b/lib/pages/dynamics/widgets/video_panel.dart new file mode 100644 index 00000000..63acd375 --- /dev/null +++ b/lib/pages/dynamics/widgets/video_panel.dart @@ -0,0 +1,153 @@ +// 视频or合集 +import 'package:flutter/material.dart'; +import 'package:pilipala/common/constants.dart'; +import 'package:pilipala/common/widgets/badge.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/utils/utils.dart'; + +import 'rich_node_panel.dart'; + +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, + 'pgc': item.modules.moduleDynamic.major.pgc + }; + dynamic content = dynamicProperty[type]; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + if (floor == 2) ...[ + Row( + children: [ + GestureDetector( + onTap: () {}, + child: Text( + item.modules.moduleAuthor.type == null + ? '@${item.modules.moduleAuthor.name}' + : item.modules.moduleAuthor.name, + style: authorStyle, + ), + ), + const SizedBox(width: 6), + Text( + item.modules.moduleAuthor.pubTs != null + ? Utils.dateFormat(item.modules.moduleAuthor.pubTs) + : item.modules.moduleAuthor.pubTime, + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + fontSize: Theme.of(context).textTheme.labelSmall!.fontSize), + ), + ], + ), + 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), + ], + if (floor == 2 && item.modules.moduleDynamic.desc != null) ...[ + Text.rich(richNode(item, context)), + const SizedBox(height: 6), + ], + 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, + ), + if (content.badge != null && type == 'pgc') + pBadge(content.badge['text'], context, 8.0, 10.0, null, null), + 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.black54, + ], + ), + 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 ?? ''), + if (content.durationText != null) + const SizedBox(width: 10), + Text(content.stat.play + '次围观'), + const SizedBox(width: 10), + Text(content.stat.danmaku + '条弹幕') + ], + ), + ), + Image.asset( + 'assets/images/play.png', + width: 60, + height: 60, + ), + ], + ), + ), + ), + ], + ); + }), + 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, + ), + ), + ], + ); +} diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index 6123ee29..f0bbd1a8 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; +import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/http/search.dart'; import 'package:pilipala/models/common/business_type.dart'; @@ -14,13 +15,15 @@ class HistoryItem extends StatelessWidget { @override Widget build(BuildContext context) { int aid = videoItem.history.oid; - String bvid = videoItem.history.bvid; + String bvid = videoItem.history.bvid ?? IdUtils.av2bv(aid); String heroTag = Utils.makeHeroTag(aid); return InkWell( onTap: () async { int cid = videoItem.history.cid ?? + videoItem.history.oid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid); await Future.delayed(const Duration(milliseconds: 200)); + print(videoItem.history.business); if (videoItem.history.business.contains('article')) { Get.toNamed( '/webview', @@ -75,71 +78,23 @@ class HistoryItem extends StatelessWidget { if (!BusinessType .hiddenDurationType.hiddenDurationType .contains(videoItem.history.business)) - Positioned( - right: 4, - bottom: 4, - child: Container( - padding: const EdgeInsets.symmetric( - vertical: 1, horizontal: 6), - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(4), - color: - Colors.black54.withOpacity(0.4)), - child: Text( - videoItem.progress == -1 - ? '已看完' - : '${Utils.timeFormat(videoItem.progress!)}/${Utils.timeFormat(videoItem.duration!)}', - style: const TextStyle( - fontSize: 11, color: Colors.white), - ), - ), - ), + pBadge( + videoItem.progress == -1 + ? '已看完' + : '${Utils.timeFormat(videoItem.progress!)}/${Utils.timeFormat(videoItem.duration!)}', + context, + null, + 6.0, + 6.0, + null, + type: 'gray'), // 右上角 if (BusinessType.showBadge.showBadge - .contains(videoItem.history.business)) - Positioned( - right: 4, - top: 4, - child: Container( - padding: const EdgeInsets.symmetric( - vertical: 1, horizontal: 6), - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(4), - color: Theme.of(context) - .colorScheme - .primaryContainer), - child: Text( - videoItem.badge, - style: TextStyle( - fontSize: 11, - color: Theme.of(context) - .colorScheme - .primary), - ), - ), - ), - if (videoItem.history.business == - BusinessType.live.type) - Positioned( - right: 4, - top: 4, - child: Container( - padding: const EdgeInsets.symmetric( - vertical: 1, horizontal: 6), - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(4), - color: - Colors.black54.withOpacity(0.4)), - child: Text( - videoItem.badge, - style: const TextStyle( - fontSize: 11, color: Colors.white), - ), - ), - ) + .contains(videoItem.history.business) || + videoItem.history.business == + BusinessType.live.type) + pBadge(videoItem.badge, context, 6.0, 6.0, + null, null), ], ); }, diff --git a/lib/pages/searchPanel/widgets/media_bangumi_panel.dart b/lib/pages/searchPanel/widgets/media_bangumi_panel.dart index 6ded02df..4a0cb8c4 100644 --- a/lib/pages/searchPanel/widgets/media_bangumi_panel.dart +++ b/lib/pages/searchPanel/widgets/media_bangumi_panel.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/http/search.dart'; import 'package:pilipala/models/bangumi/info.dart'; @@ -33,7 +34,8 @@ Widget searchMbangumiPanel(BuildContext context, ctr, list) { height: 148, src: i.cover, ), - Positioned(top: 6, right: 4, child: UpTag(type: i.mediaType)) + pBadge(i.mediaType == 1 ? '番剧' : '国创', context, 6.0, 4.0, + null, null) ], ), const SizedBox(width: 10), @@ -126,28 +128,3 @@ Widget searchMbangumiPanel(BuildContext context, ctr, list) { }, ); } - -class UpTag extends StatelessWidget { - int? type; - UpTag({super.key, this.type = 4}); - @override - Widget build(BuildContext context) { - Color primary = Theme.of(context).colorScheme.primary; - return Container( - width: 24, - height: 16, - decoration: - BoxDecoration(borderRadius: BorderRadius.circular(3), color: primary), - margin: const EdgeInsets.only(right: 4), - child: Center( - child: Text( - type == 1 ? '番剧' : '国创', - style: TextStyle( - fontSize: 9, - color: Theme.of(context).colorScheme.onPrimary, - ), - ), - ), - ); - } -} diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 14fae6b1..11775c2b 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -1,5 +1,6 @@ import 'package:get/get.dart'; -import 'package:pilipala/pages/dynamics/view.dart'; +import 'package:pilipala/pages/dynamics/deatil/index.dart'; +import 'package:pilipala/pages/dynamics/index.dart'; import 'package:pilipala/pages/fav/index.dart'; import 'package:pilipala/pages/favDetail/index.dart'; import 'package:pilipala/pages/history/index.dart'; @@ -8,13 +9,12 @@ import 'package:pilipala/pages/hot/index.dart'; import 'package:pilipala/pages/later/index.dart'; import 'package:pilipala/pages/preview/index.dart'; import 'package:pilipala/pages/search/index.dart'; +import 'package:pilipala/pages/searchResult/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/webview/index.dart'; import 'package:pilipala/pages/setting/index.dart'; import 'package:pilipala/pages/media/index.dart'; -import '../pages/searchResult/index.dart'; - class Routes { static final List getPages = [ // 首页(推荐) @@ -49,7 +49,9 @@ class Routes { GetPage(name: '/search', page: () => const SearchPage()), // 搜索结果 GetPage(name: '/searchResult', page: () => const SearchResultPage()), - // - GetPage(name: '/dynamics', page: () => const DynamicsPage()) + // 动态 + GetPage(name: '/dynamics', page: () => const DynamicsPage()), + // 动态详情 + GetPage(name: '/dynamicDetail', page: () => const DynamicDetailPage()) ]; }