diff --git a/lib/common/widgets/app_expansion_panel_list.dart b/lib/common/widgets/app_expansion_panel_list.dart index 4698310a..b0fc16f0 100644 --- a/lib/common/widgets/app_expansion_panel_list.dart +++ b/lib/common/widgets/app_expansion_panel_list.dart @@ -168,7 +168,7 @@ class _AppExpansionPanelListState extends State { if (widget.expansionCallback != null && childIndex != index && child.value == _currentOpenPanel?.value) { - widget.expansionCallback!(childIndex, false); + widget.expansionCallback?.call(childIndex, false); } } diff --git a/lib/common/widgets/article_content.dart b/lib/common/widgets/article_content.dart index d98fb2b0..5d507aa9 100644 --- a/lib/common/widgets/article_content.dart +++ b/lib/common/widgets/article_content.dart @@ -6,90 +6,83 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; -class ArticleContent extends StatelessWidget { - const ArticleContent({ - super.key, - required this.list, - }); - - final List list; - - @override - Widget build(BuildContext context) { - List? imgList = list - .where((item) => item.pic != null) - .toList() - .map((item) => item.pic?.pics?.first.url ?? '') - .toList(); - return SliverList.separated( - itemCount: list.length, - itemBuilder: (_, index) { - ArticleContentModel item = list[index]; - if (item.text != null) { - List spanList = []; - item.text?.nodes?.forEach((item) { - spanList.add(TextSpan( - text: item.word?.words, - style: TextStyle( - letterSpacing: 0.3, - fontSize: FontSize.large.value, - height: LineHeight.percent(125).size, - fontStyle: - item.word?.style?.italic == true ? FontStyle.italic : null, - color: item.word?.color != null - ? Color(int.parse( - item.word!.color!.replaceFirst('#', 'FF'), - radix: 16, - )) - : null, - decoration: item.word?.style?.strikethrough == true - ? TextDecoration.lineThrough - : null, - fontWeight: - item.word?.style?.bold == true ? FontWeight.bold : null, - ), - )); - }); - return SelectableText.rich(TextSpan(children: spanList)); - } else if (item.line != null) { - return Container( - alignment: Alignment.center, - padding: const EdgeInsets.symmetric(vertical: 10), - child: CachedNetworkImage( - imageUrl: item.line?.pic?.url?.http2https ?? '', - height: item.line?.pic?.height?.toDouble(), +Widget articleContent({ + required BuildContext context, + required List list, +}) { + List? imgList = list + .where((item) => item.pic != null) + .toList() + .map((item) => item.pic?.pics?.first.url ?? '') + .toList(); + return SliverList.separated( + itemCount: list.length, + itemBuilder: (_, index) { + ArticleContentModel item = list[index]; + if (item.text != null) { + List spanList = []; + item.text?.nodes?.forEach((item) { + spanList.add(TextSpan( + text: item.word?.words, + style: TextStyle( + letterSpacing: 0.3, + fontSize: FontSize.large.value, + height: LineHeight.percent(125).size, + fontStyle: + item.word?.style?.italic == true ? FontStyle.italic : null, + color: item.word?.color != null + ? Color(int.parse( + item.word!.color!.replaceFirst('#', 'FF'), + radix: 16, + )) + : null, + decoration: item.word?.style?.strikethrough == true + ? TextDecoration.lineThrough + : null, + fontWeight: + item.word?.style?.bold == true ? FontWeight.bold : null, ), - ); - } else if (item.pic != null) { - return LayoutBuilder( - builder: (_, constraints) => GestureDetector( - onTap: () { - showDialog( - useSafeArea: false, - context: context, - builder: (context) { - return ImagePreview( - initialPage: imgList.indexOf(item.pic!.pics!.first.url!), - imgList: imgList, - ); - }, - ); - }, - child: NetworkImgLayer( - width: constraints.maxWidth, - height: constraints.maxWidth * - item.pic!.pics!.first.height! / - item.pic!.pics!.first.width!, - src: item.pic!.pics!.first.url, - ), + )); + }); + return SelectableText.rich(TextSpan(children: spanList)); + } else if (item.line != null) { + return Container( + alignment: Alignment.center, + padding: const EdgeInsets.symmetric(vertical: 10), + child: CachedNetworkImage( + imageUrl: item.line?.pic?.url?.http2https ?? '', + height: item.line?.pic?.height?.toDouble(), + ), + ); + } else if (item.pic != null) { + return LayoutBuilder( + builder: (_, constraints) => GestureDetector( + onTap: () { + showDialog( + useSafeArea: false, + context: context, + builder: (context) { + return ImagePreview( + initialPage: imgList.indexOf(item.pic!.pics!.first.url!), + imgList: imgList, + ); + }, + ); + }, + child: NetworkImgLayer( + width: constraints.maxWidth, + height: constraints.maxWidth * + item.pic!.pics!.first.height! / + item.pic!.pics!.first.width!, + src: item.pic!.pics!.first.url, ), - ); - } else { - return const SizedBox.shrink(); - // return Text('unsupported content'); - } - }, - separatorBuilder: (context, index) => const SizedBox(height: 10), - ); - } + ), + ); + } else { + return const SizedBox.shrink(); + // return Text('unsupported content'); + } + }, + separatorBuilder: (context, index) => const SizedBox(height: 10), + ); } diff --git a/lib/common/widgets/html_render.dart b/lib/common/widgets/html_render.dart index 0dce51ef..5c3a7a24 100644 --- a/lib/common/widgets/html_render.dart +++ b/lib/common/widgets/html_render.dart @@ -3,139 +3,125 @@ import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; import 'network_img_layer.dart'; -// ignore: must_be_immutable -class HtmlRender extends StatelessWidget { - const HtmlRender({ - this.htmlContent, - this.imgCount, - this.imgList, - required this.constrainedWidth, - super.key, - }); - - final String? htmlContent; - final int? imgCount; - final List? imgList; - final double constrainedWidth; - - @override - Widget build(BuildContext context) { - // double textScale = - // setting.get(SettingBoxKey.defaultTextScale, defaultValue: 1.0); - return SelectionArea( - child: Html( - data: htmlContent, - onLinkTap: (String? url, Map buildContext, attributes) {}, - extensions: [ - TagExtension( - tagsToExtend: {'img'}, - builder: (ExtensionContext extensionContext) { - try { - final Map attributes = - extensionContext.attributes; - final List key = attributes.keys.toList(); - String imgUrl = key.contains('src') - ? attributes['src'] as String - : attributes['data-src'] as String; - if (imgUrl.startsWith('//')) { - imgUrl = 'https:$imgUrl'; - } - if (imgUrl.startsWith('http://')) { - imgUrl = imgUrl.replaceAll('http://', 'https://'); - } - imgUrl = imgUrl.contains('@') ? imgUrl.split('@').first : imgUrl; - final bool isEmote = imgUrl.contains('/emote/'); - final bool isMall = imgUrl.contains('/mall/'); - if (isMall) { - return const SizedBox(); - } - // bool inTable = - // extensionContext.element!.previousElementSibling == null || - // extensionContext.element!.nextElementSibling == null; - // imgUrl = Utils().imageUrl(imgUrl!); - // return Image.network( - // imgUrl, - // width: isEmote ? 22 : null, - // height: isEmote ? 22 : null, - // ); - return GestureDetector( - onTap: () { - showDialog( - useSafeArea: false, - context: context, - builder: (context) { - return ImagePreview( - initialPage: 0, - imgList: [imgUrl], - ); - }, - ); - }, - child: NetworkImgLayer( - width: isEmote ? 22 : constrainedWidth, - height: isEmote ? 22 : 200, - src: imgUrl, - ignoreHeight: !isEmote, - ), - ); - } catch (err) { +Widget htmlRender({ + required BuildContext context, + String? htmlContent, + int? imgCount, + List? imgList, + required double constrainedWidth, +}) { + return SelectionArea( + child: Html( + data: htmlContent, + onLinkTap: (String? url, Map buildContext, attributes) {}, + extensions: [ + TagExtension( + tagsToExtend: {'img'}, + builder: (ExtensionContext extensionContext) { + try { + final Map attributes = extensionContext.attributes; + final List key = attributes.keys.toList(); + String imgUrl = key.contains('src') + ? attributes['src'] as String + : attributes['data-src'] as String; + if (imgUrl.startsWith('//')) { + imgUrl = 'https:$imgUrl'; + } + if (imgUrl.startsWith('http://')) { + imgUrl = imgUrl.replaceAll('http://', 'https://'); + } + imgUrl = imgUrl.contains('@') ? imgUrl.split('@').first : imgUrl; + final bool isEmote = imgUrl.contains('/emote/'); + final bool isMall = imgUrl.contains('/mall/'); + if (isMall) { return const SizedBox(); } - }, - ), - ], - style: { - 'html': Style( - fontSize: FontSize.large, - lineHeight: LineHeight.percent(160), - letterSpacing: 0.3, - ), - // 'br': Style(margin: Margins.zero, padding: HtmlPaddings.zero), - 'body': Style(margin: Margins.zero, padding: HtmlPaddings.zero), - 'a': Style( - color: Theme.of(context).colorScheme.primary, - textDecoration: TextDecoration.none, - ), - 'br': Style( - lineHeight: LineHeight.percent(-1), - ), - 'p': Style( - margin: Margins.only(bottom: 4), - // margin: Margins.zero, - ), - 'span': Style( - fontSize: FontSize.medium, - height: Height(1.8), - ), - 'div': Style(height: Height.auto()), - 'li > p': Style( - display: Display.inline, - ), - 'li': Style( - padding: HtmlPaddings.only(bottom: 4), - textAlign: TextAlign.justify, - ), - 'img': Style(margin: Margins.only(top: 4, bottom: 4)), - 'h1,h2': Style( - fontSize: FontSize.xLarge, - fontWeight: FontWeight.bold, - margin: Margins.only(bottom: 8), - ), - 'h3,h4,h5': Style( - fontSize: FontSize.large, - fontWeight: FontWeight.bold, - margin: Margins.only(bottom: 4), - ), - 'figcaption': Style( - fontSize: FontSize.medium, - textAlign: TextAlign.center, - // margin: Margins.only(top: 4), - ), - 'strong': Style(fontWeight: FontWeight.bold), - 'figure': Style( - margin: Margins.zero, - ), - }, - )); - } + // bool inTable = + // extensionContext.element!.previousElementSibling == null || + // extensionContext.element!.nextElementSibling == null; + // imgUrl = Utils().imageUrl(imgUrl!); + // return Image.network( + // imgUrl, + // width: isEmote ? 22 : null, + // height: isEmote ? 22 : null, + // ); + return GestureDetector( + onTap: () { + showDialog( + useSafeArea: false, + context: context, + builder: (context) { + return ImagePreview( + initialPage: 0, + imgList: [imgUrl], + ); + }, + ); + }, + child: NetworkImgLayer( + width: isEmote ? 22 : constrainedWidth, + height: isEmote ? 22 : 200, + src: imgUrl, + ignoreHeight: !isEmote, + ), + ); + } catch (err) { + return const SizedBox(); + } + }, + ), + ], + style: { + 'html': Style( + fontSize: FontSize.large, + lineHeight: LineHeight.percent(160), + letterSpacing: 0.3, + ), + // 'br': Style(margin: Margins.zero, padding: HtmlPaddings.zero), + 'body': Style(margin: Margins.zero, padding: HtmlPaddings.zero), + 'a': Style( + color: Theme.of(context).colorScheme.primary, + textDecoration: TextDecoration.none, + ), + 'br': Style( + lineHeight: LineHeight.percent(-1), + ), + 'p': Style( + margin: Margins.only(bottom: 4), + // margin: Margins.zero, + ), + 'span': Style( + fontSize: FontSize.medium, + height: Height(1.8), + ), + 'div': Style(height: Height.auto()), + 'li > p': Style( + display: Display.inline, + ), + 'li': Style( + padding: HtmlPaddings.only(bottom: 4), + textAlign: TextAlign.justify, + ), + 'img': Style(margin: Margins.only(top: 4, bottom: 4)), + 'h1,h2': Style( + fontSize: FontSize.xLarge, + fontWeight: FontWeight.bold, + margin: Margins.only(bottom: 8), + ), + 'h3,h4,h5': Style( + fontSize: FontSize.large, + fontWeight: FontWeight.bold, + margin: Margins.only(bottom: 4), + ), + 'figcaption': Style( + fontSize: FontSize.medium, + textAlign: TextAlign.center, + // margin: Margins.only(top: 4), + ), + 'strong': Style(fontWeight: FontWeight.bold), + 'figure': Style( + margin: Margins.zero, + ), + }, + )); } diff --git a/lib/common/widgets/list_sheet.dart b/lib/common/widgets/list_sheet.dart index 6e335e30..ea39f586 100644 --- a/lib/common/widgets/list_sheet.dart +++ b/lib/common/widgets/list_sheet.dart @@ -13,53 +13,6 @@ import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import '../../utils/storage.dart'; import '../../utils/utils.dart'; -class ListSheet { - ListSheet({ - this.index, - this.season, - required this.episodes, - this.bvid, - this.aid, - required this.currentCid, - required this.changeFucCall, - required this.context, - this.scaffoldState, - }); - - final dynamic index; - final dynamic season; - final dynamic episodes; - final String? bvid; - final int? aid; - final int currentCid; - final Function changeFucCall; - final BuildContext context; - final ScaffoldState? scaffoldState; - - late PersistentBottomSheetController bottomSheetController; - - Widget get listSheetContent => ListSheetContent( - index: index, - season: season, - episodes: episodes, - bvid: bvid, - aid: aid, - currentCid: currentCid, - changeFucCall: changeFucCall, - onClose: bottomSheetController.close, - ); - - void buildShowBottomSheet() { - bottomSheetController = scaffoldState?.showBottomSheet( - (context) => listSheetContent, - ) ?? - showBottomSheet( - context: context, - builder: (context) => listSheetContent, - ); - } -} - class ListSheetContent extends StatefulWidget { const ListSheetContent({ super.key, @@ -80,7 +33,7 @@ class ListSheetContent extends StatefulWidget { final int? aid; final int currentCid; final Function changeFucCall; - final Function() onClose; + final VoidCallback? onClose; @override State createState() => _ListSheetContentState(); @@ -184,7 +137,7 @@ class _ListSheetContentState extends State } } SmartDialog.showToast('切换到:$title'); - widget.onClose(); + widget.onClose?.call(); widget.changeFucCall( episode is bangumi.EpisodeItem ? episode.epId : null, episode.runtimeType.toString() == "EpisodeItem" diff --git a/lib/common/widgets/live_card.dart b/lib/common/widgets/live_card.dart index d4eab75e..9d4d4e68 100644 --- a/lib/common/widgets/live_card.dart +++ b/lib/common/widgets/live_card.dart @@ -53,31 +53,21 @@ class LiveCard extends StatelessWidget { child: AnimatedOpacity( opacity: 1, duration: const Duration(milliseconds: 200), - child: LiveStat( - // view: liveItem.stat.view, - // danmaku: liveItem.stat.danmaku, - // duration: liveItem.duration, - online: liveItem.online as int, - ), + child: liveStat(context), ), ), ], ); }), ), - LiveContent(liveItem: liveItem) + liveContent(context) ], ), ), ); } -} -class LiveContent extends StatelessWidget { - final dynamic liveItem; - const LiveContent({super.key, required this.liveItem}); - @override - Widget build(BuildContext context) { + Widget liveContent(context) { return Padding( // 多列 padding: const EdgeInsets.fromLTRB(8, 8, 6, 7), @@ -109,15 +99,8 @@ class LiveContent extends StatelessWidget { ), ); } -} -class LiveStat extends StatelessWidget { - const LiveStat({super.key, required this.online}); - - final int? online; - - @override - Widget build(BuildContext context) { + Widget liveStat(context) { return Container( height: 45, padding: const EdgeInsets.only(top: 22, left: 8, right: 8), @@ -149,7 +132,7 @@ class LiveStat extends StatelessWidget { // ], // ), Text( - online.toString(), + liveItem.online.toString(), style: const TextStyle(fontSize: 11, color: Colors.white), ) ], diff --git a/lib/common/widgets/overlay_pop.dart b/lib/common/widgets/overlay_pop.dart index 493b2370..e972cdc9 100644 --- a/lib/common/widgets/overlay_pop.dart +++ b/lib/common/widgets/overlay_pop.dart @@ -53,7 +53,7 @@ class OverlayPop extends StatelessWidget { style: ButtonStyle( padding: WidgetStateProperty.all(EdgeInsets.zero), ), - onPressed: () => closeFn!(), + onPressed: () => closeFn?.call(), icon: const Icon( Icons.close, size: 18, @@ -93,7 +93,7 @@ class OverlayPop extends StatelessWidget { : (videoItem.cover as String).http2https) ], ); - closeFn!(); + closeFn?.call(); }, icon: const Icon(Icons.download, size: 20), ) diff --git a/lib/common/widgets/stat/danmu.dart b/lib/common/widgets/stat/danmu.dart index 556e9410..956913d9 100644 --- a/lib/common/widgets/stat/danmu.dart +++ b/lib/common/widgets/stat/danmu.dart @@ -1,40 +1,36 @@ import 'package:flutter/material.dart'; import 'package:PiliPalaX/utils/utils.dart'; -class StatDanMu extends StatelessWidget { - final String? theme; - final dynamic danmu; - final String? size; - - const StatDanMu({super.key, this.theme, this.danmu, this.size}); - - @override - Widget build(BuildContext context) { - Map colorObject = { - 'white': Colors.white, - 'gray': Theme.of(context).colorScheme.outline.withOpacity(0.8), - 'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.7), - }; - Color color = colorObject[theme]!; - return Row( - children: [ - Icon( - Icons.subtitles_outlined, - size: 14, +Widget statDanMu({ + required BuildContext context, + String? theme, + dynamic danmu, + String? size, +}) { + Map colorObject = { + 'white': Colors.white, + 'gray': Theme.of(context).colorScheme.outline.withOpacity(0.8), + 'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.7), + }; + Color color = colorObject[theme]!; + return Row( + children: [ + Icon( + Icons.subtitles_outlined, + size: 14, + color: color, + ), + const SizedBox(width: 2), + Text( + Utils.numFormat(danmu!), + style: TextStyle( + fontWeight: FontWeight.w400, + fontSize: size == 'medium' ? 12 : 11, color: color, ), - const SizedBox(width: 2), - Text( - Utils.numFormat(danmu!), - style: TextStyle( - fontWeight: FontWeight.w400, - fontSize: size == 'medium' ? 12 : 11, - color: color, - ), - overflow: TextOverflow.clip, - semanticsLabel: '${Utils.numFormat(danmu!)}条弹幕', - ) - ], - ); - } + overflow: TextOverflow.clip, + semanticsLabel: '${Utils.numFormat(danmu!)}条弹幕', + ) + ], + ); } diff --git a/lib/common/widgets/stat/view.dart b/lib/common/widgets/stat/view.dart index a56e088c..bbe0f41e 100644 --- a/lib/common/widgets/stat/view.dart +++ b/lib/common/widgets/stat/view.dart @@ -1,44 +1,40 @@ import 'package:flutter/material.dart'; import 'package:PiliPalaX/utils/utils.dart'; -class StatView extends StatelessWidget { - final String? theme; - final dynamic view; - final String? size; - final String? goto; - - const StatView({super.key, this.theme, this.view, this.size, this.goto}); - - @override - Widget build(BuildContext context) { - Map colorObject = { - 'white': Colors.white, - 'gray': Theme.of(context).colorScheme.outline.withOpacity(0.8), - 'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.7), - }; - Color color = colorObject[theme]!; - return Row( - children: [ - Icon( - goto == 'picture' - ? Icons.remove_red_eye_outlined - : Icons.play_circle_outlined, - size: 13, +Widget statView({ + required BuildContext context, + String? theme, + dynamic view, + String? size, + String? goto, +}) { + Map colorObject = { + 'white': Colors.white, + 'gray': Theme.of(context).colorScheme.outline.withOpacity(0.8), + 'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.7), + }; + Color color = colorObject[theme]!; + return Row( + children: [ + Icon( + goto == 'picture' + ? Icons.remove_red_eye_outlined + : Icons.play_circle_outlined, + size: 13, + color: color, + ), + const SizedBox(width: 2), + Text( + Utils.numFormat(view!), + style: TextStyle( + fontWeight: FontWeight.w400, + fontSize: size == 'medium' ? 12 : 11, color: color, ), - const SizedBox(width: 2), - Text( - Utils.numFormat(view!), - style: TextStyle( - fontWeight: FontWeight.w400, - fontSize: size == 'medium' ? 12 : 11, - color: color, - ), - overflow: TextOverflow.clip, - semanticsLabel: - '${Utils.numFormat(view!)}次${goto == "picture" ? "浏览" : "播放"}', - ), - ], - ); - } + overflow: TextOverflow.clip, + semanticsLabel: + '${Utils.numFormat(view!)}次${goto == "picture" ? "浏览" : "播放"}', + ), + ], + ); } diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index beb8874f..0d75248b 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -55,11 +55,7 @@ class VideoCardH extends StatelessWidget { }, child: InkWell( borderRadius: BorderRadius.circular(12), - onLongPress: () { - if (longPress != null) { - longPress!(); - } - }, + onLongPress: longPress, onTap: () async { if (type == 'ketang') { SmartDialog.showToast('课堂视频暂不支持播放'); @@ -120,14 +116,7 @@ class VideoCardH extends StatelessWidget { }, ), ), - VideoContent( - videoItem: videoItem, - source: source, - showOwner: showOwner, - showView: showView, - showDanmaku: showDanmaku, - showPubdate: showPubdate, - ) + videoContent(context) ], ); }, @@ -146,28 +135,8 @@ class VideoCardH extends StatelessWidget { ), ]); } -} -class VideoContent extends StatelessWidget { - final dynamic videoItem; - final String source; - final bool showOwner; - final bool showView; - final bool showDanmaku; - final bool showPubdate; - - const VideoContent({ - super.key, - required this.videoItem, - this.source = 'normal', - this.showOwner = true, - this.showView = true, - this.showDanmaku = true, - this.showPubdate = false, - }); - - @override - Widget build(BuildContext context) { + Widget videoContent(context) { String pubdate = showPubdate ? Utils.dateFormat(videoItem.pubdate!, formatType: 'day') : ''; @@ -258,14 +227,16 @@ class VideoContent extends StatelessWidget { Row( children: [ if (showView) ...[ - StatView( + statView( + context: context, theme: 'gray', view: videoItem.stat.view as int, ), const SizedBox(width: 8), ], if (showDanmaku) - StatDanMu( + statDanMu( + context: context, theme: 'gray', danmu: videoItem.stat.danmu as int, ), diff --git a/lib/common/widgets/video_card_h_grpc.dart b/lib/common/widgets/video_card_h_grpc.dart index f63d9836..9dfa07fd 100644 --- a/lib/common/widgets/video_card_h_grpc.dart +++ b/lib/common/widgets/video_card_h_grpc.dart @@ -50,11 +50,7 @@ class VideoCardHGrpc extends StatelessWidget { // }, child: InkWell( borderRadius: BorderRadius.circular(12), - onLongPress: () { - if (longPress != null) { - longPress!(); - } - }, + onLongPress: longPress, onTap: () async { if (type == 'ketang') { SmartDialog.showToast('课堂视频暂不支持播放'); @@ -121,14 +117,7 @@ class VideoCardHGrpc extends StatelessWidget { }, ), ), - VideoContent( - videoItem: videoItem, - source: source, - showOwner: showOwner, - showView: showView, - showDanmaku: showDanmaku, - showPubdate: showPubdate, - ) + videoContent(context) ], ); }, @@ -147,32 +136,8 @@ class VideoCardHGrpc extends StatelessWidget { // ), ]); } -} -class VideoContent extends StatelessWidget { - final card.Card videoItem; - final String source; - final bool showOwner; - final bool showView; - final bool showDanmaku; - final bool showPubdate; - - const VideoContent({ - super.key, - required this.videoItem, - this.source = 'normal', - this.showOwner = true, - this.showView = true, - this.showDanmaku = true, - this.showPubdate = false, - }); - - @override - Widget build(BuildContext context) { - // String pubdate = showPubdate - // ? Utils.dateFormat(videoItem.pubdate!, formatType: 'day') - // : ''; - // if (pubdate != '') pubdate += ' '; + Widget videoContent(context) { return Expanded( child: Padding( padding: const EdgeInsets.fromLTRB(10, 0, 6, 0), diff --git a/lib/common/widgets/video_card_h_member_video.dart b/lib/common/widgets/video_card_h_member_video.dart index f2bdbe86..1c69e801 100644 --- a/lib/common/widgets/video_card_h_member_video.dart +++ b/lib/common/widgets/video_card_h_member_video.dart @@ -30,11 +30,7 @@ class VideoCardHMemberVideo extends StatelessWidget { return Stack(children: [ InkWell( borderRadius: BorderRadius.circular(12), - onLongPress: () { - if (longPress != null) { - longPress!(); - } - }, + onLongPress: longPress, onTap: () async { try { Get.toNamed('/video?bvid=$bvid&cid=${videoItem.firstCid}', @@ -87,9 +83,7 @@ class VideoCardHMemberVideo extends StatelessWidget { }, ), ), - VideoContent( - videoItem: videoItem, - ) + videoContent(context) ], ); }, @@ -106,18 +100,8 @@ class VideoCardHMemberVideo extends StatelessWidget { ), ]); } -} -class VideoContent extends StatelessWidget { - final Item videoItem; - - const VideoContent({ - super.key, - required this.videoItem, - }); - - @override - Widget build(BuildContext context) { + Widget videoContent(context) { return Expanded( child: Padding( padding: const EdgeInsets.fromLTRB(10, 0, 6, 0), @@ -157,14 +141,16 @@ class VideoContent extends StatelessWidget { const SizedBox(height: 3), Row( children: [ - StatView( + statView( + context: context, theme: 'gray', // view: videoItem.season?['view_content'] ?? // videoItem.viewContent, view: videoItem.viewContent, ), const SizedBox(width: 8), - StatDanMu( + statDanMu( + context: context, theme: 'gray', // danmu: videoItem.season?['danmaku'] ?? videoItem.danmaku, danmu: videoItem.danmaku, diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 2b80e826..dc876136 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -158,11 +158,7 @@ class VideoCardV extends StatelessWidget { margin: EdgeInsets.zero, child: InkWell( onTap: () async => onPushDetail(heroTag), - onLongPress: () { - if (longPress != null) { - longPress!(); - } - }, + onLongPress: longPress, child: Column( children: [ AspectRatio( @@ -194,7 +190,7 @@ class VideoCardV extends StatelessWidget { ); }), ), - VideoContent(videoItem: videoItem) + videoContent(context) ], ), ), @@ -211,13 +207,8 @@ class VideoCardV extends StatelessWidget { )), ]); } -} -class VideoContent extends StatelessWidget { - final dynamic videoItem; - const VideoContent({super.key, required this.videoItem}); - @override - Widget build(BuildContext context) { + Widget videoContent(context) { return Expanded( child: Padding( padding: const EdgeInsets.fromLTRB(6, 5, 6, 5), @@ -241,9 +232,7 @@ class VideoContent extends StatelessWidget { ), const Spacer(), // const SizedBox(height: 2), - VideoStat( - videoItem: videoItem, - ), + videoStat(context), Row( children: [ if (videoItem.goto == 'bangumi') ...[ @@ -303,28 +292,20 @@ class VideoContent extends StatelessWidget { ), ); } -} -class VideoStat extends StatelessWidget { - final dynamic videoItem; - - const VideoStat({ - super.key, - required this.videoItem, - }); - - @override - Widget build(BuildContext context) { + Widget videoStat(context) { return Row( children: [ - StatView( + statView( + context: context, theme: 'gray', view: videoItem.stat.view, goto: videoItem.goto, ), const SizedBox(width: 6), if (videoItem.goto != 'picture') - StatDanMu( + statDanMu( + context: context, theme: 'gray', danmu: videoItem.stat.danmu, ), diff --git a/lib/common/widgets/video_card_v_member_home.dart b/lib/common/widgets/video_card_v_member_home.dart index 7101b512..759ba109 100644 --- a/lib/common/widgets/video_card_v_member_home.dart +++ b/lib/common/widgets/video_card_v_member_home.dart @@ -147,11 +147,7 @@ class VideoCardVMemberHome extends StatelessWidget { margin: EdgeInsets.zero, child: InkWell( onTap: () async => onPushDetail(heroTag), - onLongPress: () { - if (longPress != null) { - longPress!(); - } - }, + onLongPress: longPress, child: Column( children: [ AspectRatio( diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index d3a47d4d..713e948d 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -312,7 +312,8 @@ class _BangumiInfoState extends State ), Row( children: [ - StatView( + statView( + context: context, theme: 'gray', view: !widget.loadingStatus ? widget.bangumiDetail!.stat!['views'] @@ -320,7 +321,8 @@ class _BangumiInfoState extends State size: 'medium', ), const SizedBox(width: 6), - StatDanMu( + statDanMu( + context: context, theme: 'gray', danmu: !widget.loadingStatus ? widget diff --git a/lib/pages/bangumi/introduction/widgets/intro_detail.dart b/lib/pages/bangumi/introduction/widgets/intro_detail.dart index 4620124a..d8d15b2d 100644 --- a/lib/pages/bangumi/introduction/widgets/intro_detail.dart +++ b/lib/pages/bangumi/introduction/widgets/intro_detail.dart @@ -59,13 +59,15 @@ class IntroDetail extends StatelessWidget { const SizedBox(height: 4), Row( children: [ - StatView( + statView( + context: context, theme: 'gray', view: bangumiDetail!.stat!['views'], size: 'medium', ), const SizedBox(width: 6), - StatDanMu( + statDanMu( + context: context, theme: 'gray', danmu: bangumiDetail!.stat!['danmakus'], size: 'medium', diff --git a/lib/pages/bangumi/widgets/bangumi_card_v.dart b/lib/pages/bangumi/widgets/bangumi_card_v.dart index 0313c426..0571a052 100644 --- a/lib/pages/bangumi/widgets/bangumi_card_v.dart +++ b/lib/pages/bangumi/widgets/bangumi_card_v.dart @@ -123,20 +123,15 @@ class BangumiCardV extends StatelessWidget { }), ), ), - BangumiContent(bangumiItem: bangumiItem) + bagumiContent(context) ], ), ), ), ); } -} -class BangumiContent extends StatelessWidget { - const BangumiContent({super.key, required this.bangumiItem}); - final dynamic bangumiItem; - @override - Widget build(BuildContext context) { + Widget bagumiContent(context) { return Expanded( child: Padding( // 多列 diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart index dc5de00c..a9d0cff1 100644 --- a/lib/pages/dynamics/widgets/author_panel.dart +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -254,9 +254,7 @@ class MorePanel extends StatelessWidget { TextButton( onPressed: () { Get.back(); - if (onRemove != null) { - onRemove!(item.idStr); - } + onRemove?.call(item.idStr); }, child: const Text('确定'), ), diff --git a/lib/pages/dynamics/widgets/author_panel_grpc.dart b/lib/pages/dynamics/widgets/author_panel_grpc.dart index 91fada4c..e2c92853 100644 --- a/lib/pages/dynamics/widgets/author_panel_grpc.dart +++ b/lib/pages/dynamics/widgets/author_panel_grpc.dart @@ -255,9 +255,7 @@ class MorePanel extends StatelessWidget { TextButton( onPressed: () { Get.back(); - if (onRemove != null) { - onRemove!(item.idStr); - } + onRemove?.call(item.idStr); }, child: const Text('确定'), ), diff --git a/lib/pages/fav/widgets/item.dart b/lib/pages/fav/widgets/item.dart index 22f8f39b..d212f1d9 100644 --- a/lib/pages/fav/widgets/item.dart +++ b/lib/pages/fav/widgets/item.dart @@ -1,4 +1,3 @@ -import 'package:PiliPalaX/models/user/fav_folder.dart'; import 'package:PiliPalaX/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:PiliPalaX/common/constants.dart'; @@ -48,7 +47,7 @@ class FavItem extends StatelessWidget { }, ), ), - VideoContent(favFolderItem: favFolderItem) + videoContent(context) ], ), ); @@ -57,14 +56,8 @@ class FavItem extends StatelessWidget { ), ); } -} -class VideoContent extends StatelessWidget { - final FavFolderItemData favFolderItem; - const VideoContent({super.key, required this.favFolderItem}); - - @override - Widget build(BuildContext context) { + Widget videoContent(context) { return Expanded( child: Padding( padding: const EdgeInsets.fromLTRB(10, 2, 6, 0), diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index d71ee0d6..51896bdc 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -116,11 +116,7 @@ class FavVideoCardH extends StatelessWidget { }, ), ), - VideoContent( - videoItem: videoItem, - callFn: callFn, - searchType: searchType, - ) + videoContent(context) ], ), ); @@ -131,21 +127,8 @@ class FavVideoCardH extends StatelessWidget { ), ); } -} -class VideoContent extends StatelessWidget { - final dynamic videoItem; - final Function? callFn; - final int? searchType; - const VideoContent({ - super.key, - required this.videoItem, - this.callFn, - this.searchType, - }); - - @override - Widget build(BuildContext context) { + Widget videoContent(context) { return Expanded( child: Padding( padding: const EdgeInsets.fromLTRB(10, 2, 6, 0), @@ -195,13 +178,17 @@ class VideoContent extends StatelessWidget { padding: const EdgeInsets.only(top: 2), child: Row( children: [ - StatView( + statView( + context: context, theme: 'gray', view: videoItem.cntInfo['play'], ), const SizedBox(width: 8), - StatDanMu( - theme: 'gray', danmu: videoItem.cntInfo['danmaku']), + statDanMu( + context: context, + theme: 'gray', + danmu: videoItem.cntInfo['danmaku'], + ), const Spacer(), ], ), @@ -236,7 +223,7 @@ class VideoContent extends StatelessWidget { )), TextButton( onPressed: () async { - await callFn!(); + await callFn?.call(); Get.back(); }, child: const Text('确定取消'), diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index c92bb625..2d215b6f 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -37,7 +37,7 @@ class HistoryItem extends StatelessWidget { onTap: () async { if (ctr!.enableMultiple.value) { feedBack(); - onChoose!(); + onChoose?.call(); return; } if (videoItem.history.business.contains('article')) { @@ -158,8 +158,8 @@ class HistoryItem extends StatelessWidget { if (!ctr!.enableMultiple.value) { feedBack(); ctr!.enableMultiple.value = true; - onChoose!(); - onUpdateMultiple!(); + onChoose?.call(); + onUpdateMultiple?.call(); } }, child: Column( @@ -264,7 +264,7 @@ class HistoryItem extends StatelessWidget { ), onPressed: () { feedBack(); - onChoose!(); + onChoose?.call(); }, icon: Icon(Icons.done_all_outlined, color: Theme.of(context) @@ -301,7 +301,7 @@ class HistoryItem extends StatelessWidget { ) ], ), - VideoContent(videoItem: videoItem, ctr: ctr) + videoContent(context) ], ), ); @@ -312,15 +312,8 @@ class HistoryItem extends StatelessWidget { ), ); } -} -class VideoContent extends StatelessWidget { - final dynamic videoItem; - final dynamic ctr; - const VideoContent({super.key, required this.videoItem, this.ctr}); - - @override - Widget build(BuildContext context) { + Widget videoContent(context) { return Expanded( child: Padding( padding: const EdgeInsets.fromLTRB(10, 2, 6, 0), diff --git a/lib/pages/html/view.dart b/lib/pages/html/view.dart index fc3cbfa4..4c861632 100644 --- a/lib/pages/html/view.dart +++ b/lib/pages/html/view.dart @@ -448,12 +448,14 @@ class _HtmlRenderPageState extends State sliver: Obx( () => _htmlRenderCtr.loaded.value ? _htmlRenderCtr.response['isJsonContent'] == true - ? ArticleContent( + ? articleContent( + context: context, list: _htmlRenderCtr.response['content'], ) : SliverToBoxAdapter( child: LayoutBuilder( - builder: (_, constraints) => HtmlRender( + builder: (_, constraints) => htmlRender( + context: context, htmlContent: _htmlRenderCtr.response['content'], constrainedWidth: constraints.maxWidth, ), diff --git a/lib/pages/live/widgets/live_item.dart b/lib/pages/live/widgets/live_item.dart index 0ad9d6bd..7a389844 100644 --- a/lib/pages/live/widgets/live_item.dart +++ b/lib/pages/live/widgets/live_item.dart @@ -29,11 +29,7 @@ class LiveCardV extends StatelessWidget { Get.toNamed('/liveRoom?roomid=${liveItem.roomId}', arguments: {'liveItem': liveItem, 'heroTag': heroTag}); }, - onLongPress: () { - if (longPress != null) { - longPress!(); - } - }, + onLongPress: longPress, child: Column( children: [ ClipRRect( @@ -60,9 +56,7 @@ class LiveCardV extends StatelessWidget { child: AnimatedOpacity( opacity: 1, duration: const Duration(milliseconds: 200), - child: VideoStat( - liveItem: liveItem, - ), + child: videoStat(context), ), ), ], @@ -70,19 +64,14 @@ class LiveCardV extends StatelessWidget { }), ), ), - LiveContent(liveItem: liveItem) + liveContent(context) ], ), ), ); } -} -class LiveContent extends StatelessWidget { - final dynamic liveItem; - const LiveContent({super.key, required this.liveItem}); - @override - Widget build(BuildContext context) { + Widget liveContent(context) { return Expanded( flex: 1, child: Padding( @@ -92,7 +81,7 @@ class LiveContent extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - liveItem.title, + '${liveItem.title}', textAlign: TextAlign.start, style: const TextStyle( fontWeight: FontWeight.w400, @@ -105,7 +94,7 @@ class LiveContent extends StatelessWidget { children: [ Expanded( child: Text( - liveItem.uname, + '${liveItem.uname}', textAlign: TextAlign.start, style: TextStyle( fontSize: @@ -123,18 +112,8 @@ class LiveContent extends StatelessWidget { ), ); } -} -class VideoStat extends StatelessWidget { - final LiveItemModel? liveItem; - - const VideoStat({ - super.key, - required this.liveItem, - }); - - @override - Widget build(BuildContext context) { + Widget videoStat(context) { return Container( height: 50, padding: const EdgeInsets.only(top: 26, left: 10, right: 10), @@ -153,14 +132,14 @@ class VideoStat extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - liveItem!.areaName!, + '${liveItem.areaName}', style: const TextStyle(fontSize: 11, color: Colors.white), - semanticsLabel: "${liveItem!.areaName!}直播", + semanticsLabel: "${liveItem.areaName}直播", ), Text( - liveItem!.watchedShow!['text_small'], + liveItem.watchedShow!['text_small'], style: const TextStyle(fontSize: 11, color: Colors.white), - semanticsLabel: "${liveItem!.watchedShow!['text_small']}围观", + semanticsLabel: "${liveItem.watchedShow?['text_small']}围观", ), ], ), diff --git a/lib/pages/member_coin/widgets/item.dart b/lib/pages/member_coin/widgets/item.dart index b97ed52a..869fab48 100644 --- a/lib/pages/member_coin/widgets/item.dart +++ b/lib/pages/member_coin/widgets/item.dart @@ -68,7 +68,8 @@ class MemberCoinsItem extends StatelessWidget { const SizedBox(height: 4), Row( children: [ - StatView( + statView( + context: context, view: coinItem.view, theme: 'gray', ), diff --git a/lib/pages/member_seasons/widgets/item.dart b/lib/pages/member_seasons/widgets/item.dart index e6146b20..ae543b6f 100644 --- a/lib/pages/member_seasons/widgets/item.dart +++ b/lib/pages/member_seasons/widgets/item.dart @@ -72,7 +72,8 @@ class MemberSeasonsItem extends StatelessWidget { const SizedBox(height: 4), Row( children: [ - StatView( + statView( + context: context, view: seasonItem.view, theme: 'gray', ), diff --git a/lib/pages/search/widgets/hot_keyword.dart b/lib/pages/search/widgets/hot_keyword.dart index c8563dba..449b3b3e 100644 --- a/lib/pages/search/widgets/hot_keyword.dart +++ b/lib/pages/search/widgets/hot_keyword.dart @@ -27,7 +27,7 @@ class HotKeyword extends StatelessWidget { borderRadius: BorderRadius.circular(3), clipBehavior: Clip.hardEdge, child: InkWell( - onTap: () => onClick!(i.keyword), + onTap: () => onClick?.call(i.keyword), child: Padding( padding: EdgeInsets.only( left: 2, diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart index 33c3a41c..c4bd9302 100644 --- a/lib/pages/search/widgets/search_text.dart +++ b/lib/pages/search/widgets/search_text.dart @@ -29,10 +29,10 @@ class SearchText extends StatelessWidget { padding: EdgeInsets.zero, child: InkWell( onTap: () { - onSelect!(searchText); + onSelect?.call(searchText); }, onLongPress: () { - onLongSelect!(searchText); + onLongSelect?.call(searchText); }, borderRadius: BorderRadius.circular(6), child: Padding( diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index 8e21bb66..16051f96 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -163,7 +163,7 @@ class CustomFilterChip extends StatelessWidget { // Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5), backgroundColor: Colors.transparent, side: BorderSide.none, - onSelected: (bool selected) => callFn!(selected), + onSelected: (bool selected) => callFn?.call(selected), ), ); } diff --git a/lib/pages/subscription/widgets/item.dart b/lib/pages/subscription/widgets/item.dart index b10d1e7e..22494339 100644 --- a/lib/pages/subscription/widgets/item.dart +++ b/lib/pages/subscription/widgets/item.dart @@ -56,10 +56,7 @@ class SubItem extends StatelessWidget { }, ), ), - VideoContent( - subFolderItem: subFolderItem, - cancelSub: cancelSub, - ) + videoContent(context) ], ), ); @@ -68,15 +65,8 @@ class SubItem extends StatelessWidget { ), ); } -} -class VideoContent extends StatelessWidget { - final SubFolderItemData subFolderItem; - final Function(SubFolderItemData)? cancelSub; - const VideoContent({super.key, required this.subFolderItem, this.cancelSub}); - - @override - Widget build(BuildContext context) { + Widget videoContent(context) { // subFolderItem.type == 11:播单 // subFolderItem.type == 21:合集 // 其它:其它 @@ -130,7 +120,7 @@ class VideoContent extends StatelessWidget { height: 35, width: 35, child: IconButton( - onPressed: () => cancelSub?.call(subFolderItem), + onPressed: () => cancelSub(subFolderItem), style: TextButton.styleFrom( foregroundColor: Theme.of(context).colorScheme.outline, padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), diff --git a/lib/pages/subscription_detail/widget/sub_video_card.dart b/lib/pages/subscription_detail/widget/sub_video_card.dart index a3fcbfe5..58211a3b 100644 --- a/lib/pages/subscription_detail/widget/sub_video_card.dart +++ b/lib/pages/subscription_detail/widget/sub_video_card.dart @@ -91,10 +91,7 @@ class SubVideoCardH extends StatelessWidget { }, ), ), - VideoContent( - videoItem: videoItem, - searchType: searchType, - ) + videoContent(context) ], ), ); @@ -105,19 +102,8 @@ class SubVideoCardH extends StatelessWidget { ), ); } -} -class VideoContent extends StatelessWidget { - final dynamic videoItem; - final int? searchType; - const VideoContent({ - super.key, - required this.videoItem, - this.searchType, - }); - - @override - Widget build(BuildContext context) { + Widget videoContent(context) { return Expanded( child: Padding( padding: const EdgeInsets.fromLTRB(10, 2, 6, 0), @@ -127,7 +113,7 @@ class VideoContent extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - videoItem.title, + '${videoItem.title}', textAlign: TextAlign.start, style: const TextStyle( fontWeight: FontWeight.w400, @@ -147,13 +133,17 @@ class VideoContent extends StatelessWidget { padding: const EdgeInsets.only(top: 2), child: Row( children: [ - StatView( + statView( + context: context, theme: 'gray', - view: videoItem.cntInfo['play'], + view: videoItem.cntInfo?['play'], ), const SizedBox(width: 8), - StatDanMu( - theme: 'gray', danmu: videoItem.cntInfo['danmaku']), + statDanMu( + context: context, + theme: 'gray', + danmu: videoItem.cntInfo?['danmaku'], + ), const Spacer(), ], ), diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 5a563acd..6ded2301 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -444,7 +444,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { onTap: showIntroDetail, child: Row( children: [ - StatView( + statView( + context: context, theme: 'gray', view: !loadingStatus ? widget.videoDetail?.stat?.view ?? '-' @@ -452,7 +453,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { size: 'medium', ), const SizedBox(width: 10), - StatDanMu( + statDanMu( + context: context, theme: 'gray', danmu: !loadingStatus ? widget.videoDetail?.stat?.danmu ?? '-' diff --git a/lib/pages/video/detail/introduction/widgets/action_row_item.dart b/lib/pages/video/detail/introduction/widgets/action_row_item.dart index bb5df346..3cfbc3e9 100644 --- a/lib/pages/video/detail/introduction/widgets/action_row_item.dart +++ b/lib/pages/video/detail/introduction/widgets/action_row_item.dart @@ -32,13 +32,11 @@ class ActionRowItem extends StatelessWidget { child: InkWell( onTap: () => { feedBack(), - onTap!(), + onTap?.call(), }, onLongPress: () { feedBack(); - if (onLongPress != null) { - onLongPress!(); - } + onLongPress?.call(); }, child: Padding( padding: const EdgeInsets.fromLTRB(15, 7, 15, 7), diff --git a/lib/pages/video/detail/introduction/widgets/intro_detail.dart b/lib/pages/video/detail/introduction/widgets/intro_detail.dart index 9d59aac4..925d76d7 100644 --- a/lib/pages/video/detail/introduction/widgets/intro_detail.dart +++ b/lib/pages/video/detail/introduction/widgets/intro_detail.dart @@ -56,13 +56,15 @@ class IntroDetail extends StatelessWidget { const SizedBox(height: 6), Row( children: [ - StatView( + statView( + context: context, theme: 'gray', view: videoDetail!.stat!.view, size: 'medium', ), const SizedBox(width: 10), - StatDanMu( + statDanMu( + context: context, theme: 'gray', danmu: videoDetail!.stat!.danmu, size: 'medium', diff --git a/lib/pages/video/detail/introduction/widgets/menu_row.dart b/lib/pages/video/detail/introduction/widgets/menu_row.dart index 8ed4b729..ad52daf9 100644 --- a/lib/pages/video/detail/introduction/widgets/menu_row.dart +++ b/lib/pages/video/detail/introduction/widgets/menu_row.dart @@ -61,7 +61,7 @@ class MenuRow extends StatelessWidget { child: InkWell( onTap: () => { feedBack(), - onTap!(), + onTap?.call(), }, child: Container( padding: const EdgeInsets.fromLTRB(13, 5.5, 13, 4.5), @@ -120,7 +120,7 @@ class ActionRowLineItem extends StatelessWidget { child: InkWell( onTap: () => { feedBack(), - onTap!(), + onTap?.call(), }, child: Container( padding: const EdgeInsets.fromLTRB(13, 5.5, 13, 4.5), diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 96048094..4afa8a6e 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -60,9 +60,7 @@ class ReplyItem extends StatelessWidget { return MorePanel( item: replyItem!, onDelete: (rpid) { - if (onDelete != null) { - onDelete!(rpid, null); - } + onDelete?.call(rpid, null); }, ); }, @@ -318,9 +316,7 @@ class ReplyItem extends StatelessWidget { replyItem: replyItem, replyReply: replyReply, onDelete: (rpid) { - if (onDelete != null) { - onDelete!(rpid, replyItem!.rpid); - } + onDelete?.call(rpid, replyItem!.rpid); }, ), ), @@ -339,10 +335,7 @@ class ReplyItem extends StatelessWidget { child: TextButton( onPressed: () { feedBack(); - if (onReply != null) { - onReply!(); - return; - } + onReply?.call(); }, child: Row(children: [ Icon(Icons.reply, @@ -990,8 +983,8 @@ class MorePanel extends StatelessWidget { 'https://www.bilibili.com/h5/comment/report?mid=${item.mid}&oid=${item.oid}&pageType=1&rpid=${item.rpid}&platform=android', }, ); - if (result == true && onDelete != null) { - onDelete!(item.rpid!); + if (result == true) { + onDelete?.call(item.rpid!); } break; case 'copyAll': @@ -1056,10 +1049,7 @@ class MorePanel extends StatelessWidget { SmartDialog.dismiss(); if (result['status']) { SmartDialog.showToast('删除成功'); - // Get.back(); - if (onDelete != null) { - onDelete!(item.rpid!); - } + onDelete?.call(item.rpid!); } else { SmartDialog.showToast('删除失败, ${result["msg"]}'); } diff --git a/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart b/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart index 1f8f3940..8437342f 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart @@ -71,12 +71,11 @@ class ReplyItemGrpc extends StatelessWidget { useRootNavigator: true, isScrollControlled: true, builder: (context) { - return MorePanel( + return morePanel( + context: context, item: replyItem, onDelete: (rpid) { - if (onDelete != null) { - onDelete!(rpid, null); - } + onDelete?.call(rpid, null); }, ); }, @@ -339,10 +338,7 @@ class ReplyItemGrpc extends StatelessWidget { style: _style, onPressed: () { feedBack(); - if (onReply != null) { - onReply!(); - return; - } + onReply?.call(); }, child: Row(children: [ Icon(Icons.reply, @@ -439,7 +435,8 @@ class ReplyItemGrpc extends StatelessWidget { useRootNavigator: true, isScrollControlled: true, builder: (context) { - return MorePanel( + return morePanel( + context: context, item: replyItem.replies[i], onDelete: (rpid) { onDelete?.call(rpid, replyItem.id.toInt()); @@ -579,333 +576,132 @@ class ReplyItemGrpc extends StatelessWidget { ), ); } -} -InlineSpan buildContent( - BuildContext context, - replyItem, - replyReply, - fReplyItem, - textPainter, - didExceedMaxLines, - getTag, -) { - final String routePath = Get.currentRoute; - bool isVideoPage = routePath.startsWith('/video'); + InlineSpan buildContent( + BuildContext context, + replyItem, + replyReply, + fReplyItem, + textPainter, + didExceedMaxLines, + getTag, + ) { + final String routePath = Get.currentRoute; + bool isVideoPage = routePath.startsWith('/video'); - // replyItem 当前回复内容 - // replyReply 查看二楼回复(回复详情)回调 - // fReplyItem 父级回复内容,用作二楼回复(回复详情)展示 - final Content content = replyItem.content; - String message = content.message; - final List spanChildren = []; + // replyItem 当前回复内容 + // replyReply 查看二楼回复(回复详情)回调 + // fReplyItem 父级回复内容,用作二楼回复(回复详情)展示 + final Content content = replyItem.content; + String message = content.message; + final List spanChildren = []; - if (didExceedMaxLines == true) { - final textSize = textPainter.size; - final double maxHeight = textPainter.preferredLineHeight * 6; - var position = textPainter.getPositionForOffset( - Offset( - textSize.width, - maxHeight, // textSize.height, - ), - ); - // final endOffset = textPainter.getOffsetBefore(position.offset); - message = message.substring(0, position.offset); - } - - // return TextSpan(text: message); - - // 投票 - if (content.hasVote()) { - message.splitMapJoin(RegExp(r"\{vote:\d+?\}"), onMatch: (Match match) { - // String matchStr = match[0]!; - spanChildren.add( - TextSpan( - text: '投票: ${content.vote.title}', - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - ), - recognizer: TapGestureRecognizer()..onTap = () {} - // Get.toNamed( - // '/webviewnew', - // parameters: { - // 'url': content.vote['url'], - // 'type': 'vote', - // 'pageTitle': content.vote.title, - // }, - // ), - ), + if (didExceedMaxLines == true) { + final textSize = textPainter.size; + final double maxHeight = textPainter.preferredLineHeight * 6; + var position = textPainter.getPositionForOffset( + Offset( + textSize.width, + maxHeight, // textSize.height, + ), ); - return ''; - }, onNonMatch: (String str) { - return str; - }); - message = message.replaceAll(RegExp(r"\{vote:\d+?\}"), ""); - } - message = message - .replaceAll('&', '&') - .replaceAll('<', '<') - .replaceAll('>', '>') - .replaceAll('"', '"') - .replaceAll(''', "'") - .replaceAll(' ', ' '); - // 构建正则表达式 - final List specialTokens = [ - ...content.emote.keys, - ...content.topic.keys.map((e) => '#$e#'), - ...content.atNameToMid.keys.map((e) => '@$e'), - ]; - List jumpUrlKeysList = content.url.keys.map((String e) { - return e.replaceAllMapped( - RegExp(r'[?+*]'), (match) => '\\${match.group(0)}'); - }).toList(); - specialTokens.sort((a, b) => b.length.compareTo(a.length)); - String patternStr = specialTokens.map(RegExp.escape).join('|'); - if (patternStr.isNotEmpty) { - patternStr += "|"; - } - patternStr += r'(\b(?:\d+[::])?[0-5]?[0-9][::][0-5]?[0-9]\b)'; - if (jumpUrlKeysList.isNotEmpty) { - patternStr += '|${jumpUrlKeysList.map(RegExp.escape).join('|')}'; - } - patternStr += r'|https://b23\.tv/[a-zA-Z0-9]{7}'; - final RegExp pattern = RegExp(patternStr); - List matchedStrs = []; - void addPlainTextSpan(str) { - spanChildren.add(TextSpan( - text: str, - )); - // TextSpan( - // - // text: str, - // recognizer: TapGestureRecognizer() - // ..onTap = () => replyReply - // ?.call(replyItem.root == 0 ? replyItem : fReplyItem))))); - } + // final endOffset = textPainter.getOffsetBefore(position.offset); + message = message.substring(0, position.offset); + } - // 分割文本并处理每个部分 - message.splitMapJoin( - pattern, - onMatch: (Match match) { - String matchStr = match[0]!; - if (content.emote.containsKey(matchStr)) { - // 处理表情 - final int size = content.emote[matchStr]!.size.toInt(); - spanChildren.add(WidgetSpan( - child: ExcludeSemantics( - child: NetworkImgLayer( - src: content.emote[matchStr]!.url, - type: 'emote', - width: size * 20, - height: size * 20, - semanticsLabel: matchStr, - )), - )); - } else if (matchStr.startsWith("@") && - content.atNameToMid.containsKey(matchStr.substring(1))) { - // 处理@用户 - final String userName = matchStr.substring(1); - final int userId = content.atNameToMid[userName]!.toInt(); + // return TextSpan(text: message); + + // 投票 + if (content.hasVote()) { + message.splitMapJoin(RegExp(r"\{vote:\d+?\}"), onMatch: (Match match) { + // String matchStr = match[0]!; spanChildren.add( TextSpan( - text: matchStr, - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - ), - recognizer: TapGestureRecognizer() - ..onTap = () { - final String heroTag = Utils.makeHeroTag(userId); - Get.toNamed( - '/member?mid=$userId', - arguments: {'face': '', 'heroTag': heroTag}, - ); - }, - ), + text: '投票: ${content.vote.title}', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + recognizer: TapGestureRecognizer()..onTap = () {} + // Get.toNamed( + // '/webviewnew', + // parameters: { + // 'url': content.vote['url'], + // 'type': 'vote', + // 'pageTitle': content.vote.title, + // }, + // ), + ), ); - } else if (RegExp(r'^\b(?:\d+[::])?[0-5]?[0-9][::][0-5]?[0-9]\b$') - .hasMatch(matchStr)) { - matchStr = matchStr.replaceAll(':', ':'); - bool isValid = false; - try { - List split = matchStr - .split(':') - .map((item) => int.parse(item)) - .toList() - .reversed - .toList(); - int seek = 0; - for (int i = 0; i < split.length; i++) { - seek += split[i] * pow(60, i).toInt(); - } - int duration = Get.find( - tag: getTag?.call() ?? Get.arguments['heroTag'], - ).data.timeLength ?? - 0; - isValid = seek * 1000 <= duration; - } catch (e) { - debugPrint('failed to validate: $e'); - } - spanChildren.add( - TextSpan( - text: isValid ? ' $matchStr ' : matchStr, - style: isValid && isVideoPage - ? TextStyle( - color: Theme.of(context).colorScheme.primary, - ) - : null, - recognizer: isValid - ? (TapGestureRecognizer() - ..onTap = () { - // 跳转到指定位置 - if (isVideoPage) { - try { - SmartDialog.showToast('跳转至:$matchStr'); - Get.find( - tag: Get.arguments['heroTag']) - .plPlayerController - .seekTo(Duration(seconds: Utils.duration(matchStr)), - type: 'slider'); - } catch (e) { - SmartDialog.showToast('跳转失败: $e'); - } - } - }) - : null, - ), - ); - } else if (matchStr.startsWith('https://b23.tv/')) { - spanChildren.add( - TextSpan( - text: ' $matchStr ', - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - ), - recognizer: TapGestureRecognizer() - ..onTap = () => Get.toNamed( - '/webviewnew', - parameters: {'url': matchStr}, - ), - ), - ); - } else { - String appUrlSchema = ''; - final bool enableWordRe = GStorage.setting - .get(SettingBoxKey.enableWordRe, defaultValue: false) as bool; - if (content.url[matchStr] != null && !matchedStrs.contains(matchStr)) { - appUrlSchema = content.url[matchStr]!.appUrlSchema; - if (appUrlSchema.startsWith('bilibili://search') && !enableWordRe) { - addPlainTextSpan(matchStr); - return ""; - } - spanChildren.addAll( - [ - if (content.url[matchStr]?.hasPrefixIcon() == true) ...[ - WidgetSpan( - child: Image.network( - content.url[matchStr]!.prefixIcon, - height: 19, - color: Theme.of(context).colorScheme.primary, - ), - ) - ], - TextSpan( - text: content.url[matchStr]!.title, - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - ), - recognizer: TapGestureRecognizer() - ..onTap = () async { - final String title = content.url[matchStr]!.title; - if (appUrlSchema == '') { - if (matchStr.startsWith('BV')) { - UrlUtils.matchUrlPush( - matchStr, - title, - '', - ); - } else if (RegExp(r'^[Cc][Vv][0-9]+$') - .hasMatch(matchStr)) { - Get.toNamed('/htmlRender', parameters: { - 'url': 'https://www.bilibili.com/read/$matchStr', - 'title': title, - 'id': matchStr, - 'dynamicType': 'read' - }); - } else { - final String redirectUrl = - await UrlUtils.parseRedirectUrl(matchStr); - // if (redirectUrl == matchStr) { - // Clipboard.setData(ClipboardData(text: matchStr)); - // SmartDialog.showToast('地址可能有误'); - // return; - // } - Uri uri = Uri.parse(redirectUrl); - PiliScheme.routePush(uri); - // final String pathSegment = Uri.parse(redirectUrl).path; - // final String lastPathSegment = - // pathSegment.split('/').last; - // if (lastPathSegment.startsWith('BV')) { - // UrlUtils.matchUrlPush( - // lastPathSegment, - // title, - // redirectUrl, - // ); - // } else { - // Get.toNamed( - // '/webviewnew', - // parameters: { - // 'url': redirectUrl, - // 'type': 'url', - // 'pageTitle': title - // }, - // ); - // } - } - } else { - if (appUrlSchema.startsWith('bilibili://search')) { - Get.toNamed('/searchResult', - parameters: {'keyword': title}); - } else if (matchStr.startsWith('https://b23.tv')) { - final String redirectUrl = - await UrlUtils.parseRedirectUrl(matchStr); - final String pathSegment = Uri.parse(redirectUrl).path; - final String lastPathSegment = - pathSegment.split('/').last; - if (lastPathSegment.startsWith('BV')) { - UrlUtils.matchUrlPush( - lastPathSegment, - title, - redirectUrl, - ); - } else { - Get.toNamed( - '/webviewnew', - parameters: { - 'url': redirectUrl, - 'type': 'url', - 'pageTitle': title - }, - ); - } - } else { - Get.toNamed( - '/webviewnew', - parameters: { - 'url': matchStr, - 'type': 'url', - 'pageTitle': title - }, - ); - } - } - }, - ) - ], - ); - // 只显示一次 - matchedStrs.add(matchStr); - } else if (matchStr.length > 1 && - content.topic[matchStr.substring(1, matchStr.length - 1)] != null) { + return ''; + }, onNonMatch: (String str) { + return str; + }); + message = message.replaceAll(RegExp(r"\{vote:\d+?\}"), ""); + } + message = message + .replaceAll('&', '&') + .replaceAll('<', '<') + .replaceAll('>', '>') + .replaceAll('"', '"') + .replaceAll(''', "'") + .replaceAll(' ', ' '); + // 构建正则表达式 + final List specialTokens = [ + ...content.emote.keys, + ...content.topic.keys.map((e) => '#$e#'), + ...content.atNameToMid.keys.map((e) => '@$e'), + ]; + List jumpUrlKeysList = content.url.keys.map((String e) { + return e.replaceAllMapped( + RegExp(r'[?+*]'), (match) => '\\${match.group(0)}'); + }).toList(); + specialTokens.sort((a, b) => b.length.compareTo(a.length)); + String patternStr = specialTokens.map(RegExp.escape).join('|'); + if (patternStr.isNotEmpty) { + patternStr += "|"; + } + patternStr += r'(\b(?:\d+[::])?[0-5]?[0-9][::][0-5]?[0-9]\b)'; + if (jumpUrlKeysList.isNotEmpty) { + patternStr += '|${jumpUrlKeysList.map(RegExp.escape).join('|')}'; + } + patternStr += r'|https://b23\.tv/[a-zA-Z0-9]{7}'; + final RegExp pattern = RegExp(patternStr); + List matchedStrs = []; + void addPlainTextSpan(str) { + spanChildren.add(TextSpan( + text: str, + )); + // TextSpan( + // + // text: str, + // recognizer: TapGestureRecognizer() + // ..onTap = () => replyReply + // ?.call(replyItem.root == 0 ? replyItem : fReplyItem))))); + } + + // 分割文本并处理每个部分 + message.splitMapJoin( + pattern, + onMatch: (Match match) { + String matchStr = match[0]!; + if (content.emote.containsKey(matchStr)) { + // 处理表情 + final int size = content.emote[matchStr]!.size.toInt(); + spanChildren.add(WidgetSpan( + child: ExcludeSemantics( + child: NetworkImgLayer( + src: content.emote[matchStr]!.url, + type: 'emote', + width: size * 20, + height: size * 20, + semanticsLabel: matchStr, + )), + )); + } else if (matchStr.startsWith("@") && + content.atNameToMid.containsKey(matchStr.substring(1))) { + // 处理@用户 + final String userName = matchStr.substring(1); + final int userId = content.atNameToMid[userName]!.toInt(); spanChildren.add( TextSpan( text: matchStr, @@ -914,225 +710,424 @@ InlineSpan buildContent( ), recognizer: TapGestureRecognizer() ..onTap = () { - final String topic = - matchStr.substring(1, matchStr.length - 1); - Get.toNamed('/searchResult', parameters: {'keyword': topic}); + final String heroTag = Utils.makeHeroTag(userId); + Get.toNamed( + '/member?mid=$userId', + arguments: {'face': '', 'heroTag': heroTag}, + ); }, ), ); - } else { - addPlainTextSpan(matchStr); - } - } - return ''; - }, - onNonMatch: (String nonMatchStr) { - addPlainTextSpan(nonMatchStr); - return nonMatchStr; - }, - ); - - if (content.url.keys.isNotEmpty) { - List unmatchedItems = content.url.keys - .toList() - .where((item) => !content.message.contains(item)) - .toList(); - if (unmatchedItems.isNotEmpty) { - for (int i = 0; i < unmatchedItems.length; i++) { - String patternStr = unmatchedItems[i]; - spanChildren.addAll( - [ - if (content.url[patternStr]?.hasPrefixIcon() == true) ...[ - WidgetSpan( - child: Image.network( - content.url[patternStr]!.prefixIcon, - height: 19, - color: Theme.of(context).colorScheme.primary, - ), - ) - ], + } else if (RegExp(r'^\b(?:\d+[::])?[0-5]?[0-9][::][0-5]?[0-9]\b$') + .hasMatch(matchStr)) { + matchStr = matchStr.replaceAll(':', ':'); + bool isValid = false; + try { + List split = matchStr + .split(':') + .map((item) => int.parse(item)) + .toList() + .reversed + .toList(); + int seek = 0; + for (int i = 0; i < split.length; i++) { + seek += split[i] * pow(60, i).toInt(); + } + int duration = Get.find( + tag: getTag?.call() ?? Get.arguments['heroTag'], + ).data.timeLength ?? + 0; + isValid = seek * 1000 <= duration; + } catch (e) { + debugPrint('failed to validate: $e'); + } + spanChildren.add( TextSpan( - text: content.url[patternStr]!.title, + text: isValid ? ' $matchStr ' : matchStr, + style: isValid && isVideoPage + ? TextStyle( + color: Theme.of(context).colorScheme.primary, + ) + : null, + recognizer: isValid + ? (TapGestureRecognizer() + ..onTap = () { + // 跳转到指定位置 + if (isVideoPage) { + try { + SmartDialog.showToast('跳转至:$matchStr'); + Get.find( + tag: Get.arguments['heroTag']) + .plPlayerController + .seekTo( + Duration(seconds: Utils.duration(matchStr)), + type: 'slider'); + } catch (e) { + SmartDialog.showToast('跳转失败: $e'); + } + } + }) + : null, + ), + ); + } else if (matchStr.startsWith('https://b23.tv/')) { + spanChildren.add( + TextSpan( + text: ' $matchStr ', style: TextStyle( color: Theme.of(context).colorScheme.primary, ), recognizer: TapGestureRecognizer() - ..onTap = () { - Get.toNamed( - '/webviewnew', - parameters: { - 'url': patternStr, - 'type': 'url', - 'pageTitle': content.url[patternStr]!.title - }, - ); - }, - ) - ], - ); - } - } - } - - if (didExceedMaxLines == true) { - spanChildren.add( - TextSpan( - text: '\n查看更多', - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - ), - ), - ); - } - - // 图片渲染 - if (content.pictures.isNotEmpty) { - spanChildren.add(const TextSpan(text: '\n')); - spanChildren.add( - WidgetSpan( - child: LayoutBuilder( - builder: (_, constraints) => image( - constraints.maxWidth, - content.pictures - .map( - (item) => ImageModel( - width: item.imgWidth, - height: item.imgHeight, - url: item.imgSrc, + ..onTap = () => Get.toNamed( + '/webviewnew', + parameters: {'url': matchStr}, + ), + ), + ); + } else { + String appUrlSchema = ''; + final bool enableWordRe = GStorage.setting + .get(SettingBoxKey.enableWordRe, defaultValue: false) as bool; + if (content.url[matchStr] != null && + !matchedStrs.contains(matchStr)) { + appUrlSchema = content.url[matchStr]!.appUrlSchema; + if (appUrlSchema.startsWith('bilibili://search') && !enableWordRe) { + addPlainTextSpan(matchStr); + return ""; + } + spanChildren.addAll( + [ + if (content.url[matchStr]?.hasPrefixIcon() == true) ...[ + WidgetSpan( + child: Image.network( + content.url[matchStr]!.prefixIcon, + height: 19, + color: Theme.of(context).colorScheme.primary, + ), + ) + ], + TextSpan( + text: content.url[matchStr]!.title, + style: TextStyle( + color: Theme.of(context).colorScheme.primary, ), + recognizer: TapGestureRecognizer() + ..onTap = () async { + final String title = content.url[matchStr]!.title; + if (appUrlSchema == '') { + if (matchStr.startsWith('BV')) { + UrlUtils.matchUrlPush( + matchStr, + title, + '', + ); + } else if (RegExp(r'^[Cc][Vv][0-9]+$') + .hasMatch(matchStr)) { + Get.toNamed('/htmlRender', parameters: { + 'url': 'https://www.bilibili.com/read/$matchStr', + 'title': title, + 'id': matchStr, + 'dynamicType': 'read' + }); + } else { + final String redirectUrl = + await UrlUtils.parseRedirectUrl(matchStr); + // if (redirectUrl == matchStr) { + // Clipboard.setData(ClipboardData(text: matchStr)); + // SmartDialog.showToast('地址可能有误'); + // return; + // } + Uri uri = Uri.parse(redirectUrl); + PiliScheme.routePush(uri); + // final String pathSegment = Uri.parse(redirectUrl).path; + // final String lastPathSegment = + // pathSegment.split('/').last; + // if (lastPathSegment.startsWith('BV')) { + // UrlUtils.matchUrlPush( + // lastPathSegment, + // title, + // redirectUrl, + // ); + // } else { + // Get.toNamed( + // '/webviewnew', + // parameters: { + // 'url': redirectUrl, + // 'type': 'url', + // 'pageTitle': title + // }, + // ); + // } + } + } else { + if (appUrlSchema.startsWith('bilibili://search')) { + Get.toNamed('/searchResult', + parameters: {'keyword': title}); + } else if (matchStr.startsWith('https://b23.tv')) { + final String redirectUrl = + await UrlUtils.parseRedirectUrl(matchStr); + final String pathSegment = + Uri.parse(redirectUrl).path; + final String lastPathSegment = + pathSegment.split('/').last; + if (lastPathSegment.startsWith('BV')) { + UrlUtils.matchUrlPush( + lastPathSegment, + title, + redirectUrl, + ); + } else { + Get.toNamed( + '/webviewnew', + parameters: { + 'url': redirectUrl, + 'type': 'url', + 'pageTitle': title + }, + ); + } + } else { + Get.toNamed( + '/webviewnew', + parameters: { + 'url': matchStr, + 'type': 'url', + 'pageTitle': title + }, + ); + } + } + }, ) - .toList(), - ), - ), - ), - ); - } - - // 笔记链接 - if (content.hasRichText()) { - spanChildren.add( - TextSpan( - text: ' 笔记', - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - ), - recognizer: TapGestureRecognizer() - ..onTap = () => Get.toNamed( - '/webviewnew', - parameters: { - 'url': content.richText.note.clickUrl, - 'type': 'note', - 'pageTitle': '笔记预览' - }, - ), - ), - ); - } - // spanChildren.add(TextSpan(text: matchMember)); - return TextSpan(children: spanChildren); -} - -class MorePanel extends StatelessWidget { - final ReplyInfo item; - final Function(dynamic rpid)? onDelete; - - const MorePanel({super.key, required this.item, this.onDelete}); - - Future menuActionHandler(String type) async { - String message = item.content.message; - switch (type) { - case 'report': - Get.back(); - dynamic result = await Get.toNamed( - '/webviewnew', - parameters: { - 'url': - 'https://www.bilibili.com/h5/comment/report?mid=${item.mid}&oid=${item.oid}&pageType=1&rpid=${item.id}&platform=android', - }, - ); - if (result == true && onDelete != null) { - onDelete!(item.id.toInt()); - } - break; - case 'copyAll': - await Clipboard.setData(ClipboardData(text: message)); - SmartDialog.showToast('已复制'); - Get.back(); - break; - case 'copyFreedom': - Get.back(); - showDialog( - context: Get.context!, - builder: (context) { - return Dialog( - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 20, vertical: 16), - child: SelectableText(message), - ), - ); - }, - ); - break; - // case 'block': - // SmartDialog.showToast('加入黑名单'); - // break; - // case 'report': - // SmartDialog.showToast('举报'); - // break; - case 'delete': - //弹出确认提示: - Get.back(); - bool? isDelete = await showDialog( - context: Get.context!, - builder: (context) { - return AlertDialog( - title: const Text('删除评论(测试)'), - content: - Text('确定尝试删除这条评论吗?\n\n$message\n\n注:只能删除自己的评论,或自己管理的评论区下的评论'), - actions: [ - TextButton( - onPressed: () { - Get.back(result: false); - }, - child: const Text('取消'), - ), - TextButton( - onPressed: () { - Get.back(result: true); - }, - child: const Text('确定'), - ), ], ); - }, - ); - if (isDelete == null || !isDelete) { - return; - } - SmartDialog.showLoading(msg: '删除中...'); - var result = await VideoHttp.replyDel( - type: item.type.toInt(), - oid: item.oid.toInt(), - rpid: item.id.toInt(), - ); - SmartDialog.dismiss(); - if (result['status']) { - SmartDialog.showToast('删除成功'); - // Get.back(); - if (onDelete != null) { - onDelete!(item.id.toInt()); + // 只显示一次 + matchedStrs.add(matchStr); + } else if (matchStr.length > 1 && + content.topic[matchStr.substring(1, matchStr.length - 1)] != + null) { + spanChildren.add( + TextSpan( + text: matchStr, + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + final String topic = + matchStr.substring(1, matchStr.length - 1); + Get.toNamed('/searchResult', + parameters: {'keyword': topic}); + }, + ), + ); + } else { + addPlainTextSpan(matchStr); } - } else { - SmartDialog.showToast('删除失败, ${result["msg"]}'); } - break; - default: + return ''; + }, + onNonMatch: (String nonMatchStr) { + addPlainTextSpan(nonMatchStr); + return nonMatchStr; + }, + ); + + if (content.url.keys.isNotEmpty) { + List unmatchedItems = content.url.keys + .toList() + .where((item) => !content.message.contains(item)) + .toList(); + if (unmatchedItems.isNotEmpty) { + for (int i = 0; i < unmatchedItems.length; i++) { + String patternStr = unmatchedItems[i]; + spanChildren.addAll( + [ + if (content.url[patternStr]?.hasPrefixIcon() == true) ...[ + WidgetSpan( + child: Image.network( + content.url[patternStr]!.prefixIcon, + height: 19, + color: Theme.of(context).colorScheme.primary, + ), + ) + ], + TextSpan( + text: content.url[patternStr]!.title, + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + Get.toNamed( + '/webviewnew', + parameters: { + 'url': patternStr, + 'type': 'url', + 'pageTitle': content.url[patternStr]!.title + }, + ); + }, + ) + ], + ); + } + } } + + if (didExceedMaxLines == true) { + spanChildren.add( + TextSpan( + text: '\n查看更多', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + ), + ); + } + + // 图片渲染 + if (content.pictures.isNotEmpty) { + spanChildren.add(const TextSpan(text: '\n')); + spanChildren.add( + WidgetSpan( + child: LayoutBuilder( + builder: (_, constraints) => image( + constraints.maxWidth, + content.pictures + .map( + (item) => ImageModel( + width: item.imgWidth, + height: item.imgHeight, + url: item.imgSrc, + ), + ) + .toList(), + ), + ), + ), + ); + } + + // 笔记链接 + if (content.hasRichText()) { + spanChildren.add( + TextSpan( + text: ' 笔记', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () => Get.toNamed( + '/webviewnew', + parameters: { + 'url': content.richText.note.clickUrl, + 'type': 'note', + 'pageTitle': '笔记预览' + }, + ), + ), + ); + } + // spanChildren.add(TextSpan(text: matchMember)); + return TextSpan(children: spanChildren); } - @override - Widget build(BuildContext context) { + Widget morePanel({ + required BuildContext context, + required dynamic item, + required onDelete, + }) { + Future menuActionHandler(String type) async { + String message = item.content.message; + switch (type) { + case 'report': + Get.back(); + dynamic result = await Get.toNamed( + '/webviewnew', + parameters: { + 'url': + 'https://www.bilibili.com/h5/comment/report?mid=${item.mid}&oid=${item.oid}&pageType=1&rpid=${item.id}&platform=android', + }, + ); + if (result == true) { + onDelete?.call(item.id.toInt()); + } + break; + case 'copyAll': + await Clipboard.setData(ClipboardData(text: message)); + SmartDialog.showToast('已复制'); + Get.back(); + break; + case 'copyFreedom': + Get.back(); + showDialog( + context: Get.context!, + builder: (context) { + return Dialog( + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 20, vertical: 16), + child: SelectableText(message), + ), + ); + }, + ); + break; + // case 'block': + // SmartDialog.showToast('加入黑名单'); + // break; + // case 'report': + // SmartDialog.showToast('举报'); + // break; + case 'delete': + //弹出确认提示: + Get.back(); + bool? isDelete = await showDialog( + context: Get.context!, + builder: (context) { + return AlertDialog( + title: const Text('删除评论(测试)'), + content: Text( + '确定尝试删除这条评论吗?\n\n$message\n\n注:只能删除自己的评论,或自己管理的评论区下的评论'), + actions: [ + TextButton( + onPressed: () { + Get.back(result: false); + }, + child: const Text('取消'), + ), + TextButton( + onPressed: () { + Get.back(result: true); + }, + child: const Text('确定'), + ), + ], + ); + }, + ); + if (isDelete == null || !isDelete) { + return; + } + SmartDialog.showLoading(msg: '删除中...'); + var result = await VideoHttp.replyDel( + type: item.type.toInt(), + oid: item.oid.toInt(), + rpid: item.id.toInt(), + ); + SmartDialog.dismiss(); + if (result['status']) { + SmartDialog.showToast('删除成功'); + onDelete?.call(item.id.toInt()); + } else { + SmartDialog.showToast('删除失败, ${result["msg"]}'); + } + break; + default: + } + } + Color errorColor = Theme.of(context).colorScheme.error; return Container( padding: EdgeInsets.only( diff --git a/lib/pages/video/detail/reply_new/reply_page.dart b/lib/pages/video/detail/reply_new/reply_page.dart index 0dcae484..b74f55f1 100644 --- a/lib/pages/video/detail/reply_new/reply_page.dart +++ b/lib/pages/video/detail/reply_new/reply_page.dart @@ -292,9 +292,7 @@ class _ReplyPageState extends State _enablePublish = false; _publishStream.add(false); } - if (widget.onSaveReply != null) { - widget.onSaveReply!(value); - } + widget.onSaveReply?.call(value); }, focusNode: _focusNode, decoration: const InputDecoration( @@ -537,8 +535,6 @@ class _ReplyPageState extends State selection: TextSelection.collapsed(offset: cursorPosition + emote.text!.length), ); - if (widget.onSaveReply != null) { - widget.onSaveReply!(_replyContentController.text); - } + widget.onSaveReply?.call(_replyContentController.text); } } diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 7863a846..0ae5e156 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -1350,20 +1350,28 @@ class _VideoDetailPageState extends State } showEpisodes(index, season, episodes, bvid, aid, cid) { - ListSheet( - index: index, - season: season, - episodes: episodes, - bvid: bvid, - aid: aid, - currentCid: cid, - changeFucCall: videoDetailController.videoType == SearchType.media_bangumi - ? bangumiIntroController.changeSeasonOrbangu - : videoIntroController.changeSeasonOrbangu, - context: context, - scaffoldState: isFullScreen - ? videoDetailController.scaffoldKey.currentState - : videoDetailController.childKey.currentState, - ).buildShowBottomSheet(); + PersistentBottomSheetController? bottomSheetController; + + Widget listSheetContent() => ListSheetContent( + index: index, + season: season, + episodes: episodes, + bvid: bvid, + aid: aid, + currentCid: cid, + changeFucCall: + videoDetailController.videoType == SearchType.media_bangumi + ? bangumiIntroController.changeSeasonOrbangu + : videoIntroController.changeSeasonOrbangu, + onClose: bottomSheetController?.close, + ); + + bottomSheetController = isFullScreen + ? videoDetailController.scaffoldKey.currentState?.showBottomSheet( + (context) => listSheetContent(), + ) + : videoDetailController.scaffoldKey.currentState?.showBottomSheet( + (context) => listSheetContent(), + ); } } diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index a61d42ea..28baedc9 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -301,9 +301,7 @@ class PlPlayerController { static Future playIfExists( {bool repeat = false, bool hideControls = true}) async { // await _instance?.play(repeat: repeat, hideControls: hideControls); - if (_playCallBack != null) { - _playCallBack!(); - } + _playCallBack?.call(); } // try to get PlayerStatus diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 648b5634..5ca0c827 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -371,18 +371,14 @@ class _PLVideoPlayerState extends State .episodes!; // changeFucCall = bangumiIntroController!.changeSeasonOrbangu; } - if (widget.showEpisodes != null) { - widget.showEpisodes!( - index, - isPage - ? null - : videoIntroController?.videoDetail.value.ugcSeason, - episodes, - bvid, - IdUtils.bv2av(bvid), - currentCid, - ); - } + widget.showEpisodes?.call( + index, + isPage ? null : videoIntroController?.videoDetail.value.ugcSeason, + episodes, + bvid, + IdUtils.bv2av(bvid), + currentCid, + ); }, ), ), diff --git a/lib/plugin/pl_player/widgets/common_btn.dart b/lib/plugin/pl_player/widgets/common_btn.dart index 1da7691f..8ea36ac8 100644 --- a/lib/plugin/pl_player/widgets/common_btn.dart +++ b/lib/plugin/pl_player/widgets/common_btn.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; class ComBtn extends StatelessWidget { final Widget? icon; - final Function? fuc; + final GestureTapCallback? fuc; const ComBtn({ this.icon, @@ -16,9 +16,7 @@ class ComBtn extends StatelessWidget { width: 34, height: 34, child: InkWell( - onTap: () { - fuc!(); - }, + onTap: fuc, child: icon!, ), );