Refactor member page (#3)

* refactor: member page

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip
This commit is contained in:
bggRGjQaUbCoE
2024-10-17 11:18:59 +08:00
committed by GitHub
parent 270bf0a4b6
commit f77088b870
205 changed files with 70072 additions and 119 deletions

View File

@@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:flutter/material.dart';
class StyleString {
@@ -22,8 +24,8 @@ class Constants {
'11111111111111111111111111111111:1111111111111111:0:0';
static const String userAgent =
'Mozilla/5.0 BiliDroid/1.46.2 (bbcallen@gmail.com) os/android model/vivo mobi_app/android build/1462100 channel/bili innerVer/1462100 osVer/14 network/2';
static const String statistics =
'%7B%22appId%22%3A5%2C%22platform%22%3A3%2C%22version%22%3A%221.46.2%22%2C%22abtest%22%3A%22%22%7D';
static final String statistics = jsonEncode(
{"appId": 5, "platform": 3, "version": "1.46.2", "abtest": ""});
//Uri.encodeComponent('{"appId": 5,"platform": 3,"version": "1.46.2","abtest": ""}');
//内容来自 https://passport.bilibili.com/web/generic/country/list

View File

@@ -11,6 +11,7 @@ class PBadge extends StatelessWidget {
final String? stack;
final double? fs;
final String? semanticsLabel;
final bool bold;
const PBadge({
super.key,
@@ -24,6 +25,7 @@ class PBadge extends StatelessWidget {
this.stack = 'position',
this.fs = 11,
this.semanticsLabel,
this.bold = true,
});
@override
@@ -73,13 +75,13 @@ class PBadge extends StatelessWidget {
height: 1,
fontSize: fs ?? fontSize,
color: color,
fontWeight: FontWeight.bold,
fontWeight: bold ? FontWeight.bold : null,
),
strutStyle: StrutStyle(
leading: 0,
height: 1,
fontSize: fs ?? fontSize,
fontWeight: FontWeight.bold,
fontWeight: bold ? FontWeight.bold : null,
),
semanticsLabel: semanticsLabel,
),

View File

@@ -0,0 +1,197 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
/// https://github.com/flutter/flutter/issues/18345#issuecomment-1627644396
class DynamicSliverAppBar extends StatefulWidget {
const DynamicSliverAppBar({
this.flexibleSpace,
super.key,
this.leading,
this.automaticallyImplyLeading = true,
this.title,
this.actions,
this.bottom,
this.elevation,
this.scrolledUnderElevation,
this.shadowColor,
this.surfaceTintColor,
this.forceElevated = false,
this.backgroundColor,
this.backgroundGradient,
this.foregroundColor,
this.iconTheme,
this.actionsIconTheme,
this.primary = true,
this.centerTitle,
this.excludeHeaderSemantics = false,
this.titleSpacing,
this.collapsedHeight,
this.expandedHeight,
this.floating = false,
this.pinned = false,
this.snap = false,
this.stretch = false,
this.stretchTriggerOffset = 100.0,
this.onStretchTrigger,
this.shape,
this.toolbarHeight = kToolbarHeight + 20,
this.leadingWidth,
this.toolbarTextStyle,
this.titleTextStyle,
this.systemOverlayStyle,
this.forceMaterialTransparency = false,
this.clipBehavior,
this.appBarClipper,
});
final Widget? flexibleSpace;
final Widget? leading;
final bool automaticallyImplyLeading;
final Widget? title;
final List<Widget>? actions;
final PreferredSizeWidget? bottom;
final double? elevation;
final double? scrolledUnderElevation;
final Color? shadowColor;
final Color? surfaceTintColor;
final bool forceElevated;
final Color? backgroundColor;
/// If backgroundGradient is non null, backgroundColor will be ignored
final LinearGradient? backgroundGradient;
final Color? foregroundColor;
final IconThemeData? iconTheme;
final IconThemeData? actionsIconTheme;
final bool primary;
final bool? centerTitle;
final bool excludeHeaderSemantics;
final double? titleSpacing;
final double? expandedHeight;
final double? collapsedHeight;
final bool floating;
final bool pinned;
final ShapeBorder? shape;
final double toolbarHeight;
final double? leadingWidth;
final TextStyle? toolbarTextStyle;
final TextStyle? titleTextStyle;
final SystemUiOverlayStyle? systemOverlayStyle;
final bool forceMaterialTransparency;
final Clip? clipBehavior;
final bool snap;
final bool stretch;
final double stretchTriggerOffset;
final AsyncCallback? onStretchTrigger;
final CustomClipper<Path>? appBarClipper;
@override
_DynamicSliverAppBarState createState() => _DynamicSliverAppBarState();
}
class _DynamicSliverAppBarState extends State<DynamicSliverAppBar> {
final GlobalKey _childKey = GlobalKey();
// As long as the height is 0 instead of the sliver app bar a sliver to box adapter will be used
// to calculate dynamically the size for the sliver app bar
double _height = 0;
@override
void initState() {
super.initState();
_updateHeight();
}
@override
void didUpdateWidget(covariant DynamicSliverAppBar oldWidget) {
super.didUpdateWidget(oldWidget);
_updateHeight();
}
void _updateHeight() {
// Gets the new height and updates the sliver app bar. Needs to be called after the last frame has been rebuild
// otherwise this will throw an error
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if (_childKey.currentContext == null) return;
setState(() {
_height = (_childKey.currentContext!.findRenderObject()! as RenderBox)
.size
.height;
});
});
}
@override
Widget build(BuildContext context) {
//Needed to lay out the flexibleSpace the first time, so we can calculate its intrinsic height
if (_height == 0) {
return SliverToBoxAdapter(
child: Stack(
children: [
Padding(
// Padding which centers the flexible space within the app bar
padding: EdgeInsets.symmetric(
vertical: MediaQuery.paddingOf(context).top / 2),
child: Container(
key: _childKey,
child:
widget.flexibleSpace ?? SizedBox(height: kToolbarHeight)),
),
Positioned.fill(
// 10 is the magic number which the app bar is pushed down within the sliver app bar. Couldnt find exactly where this number
// comes from and found it through trial and error.
top: 10,
child: Align(
alignment: Alignment.topCenter,
child: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
leading: widget.leading,
actions: widget.actions,
),
),
)
],
),
);
}
return SliverAppBar(
leading: widget.leading,
automaticallyImplyLeading: widget.automaticallyImplyLeading,
title: widget.title,
actions: widget.actions,
bottom: widget.bottom,
elevation: widget.elevation,
scrolledUnderElevation: widget.scrolledUnderElevation,
shadowColor: widget.shadowColor,
surfaceTintColor: widget.surfaceTintColor,
forceElevated: widget.forceElevated,
backgroundColor: widget.backgroundColor,
foregroundColor: widget.foregroundColor,
iconTheme: widget.iconTheme,
actionsIconTheme: widget.actionsIconTheme,
primary: widget.primary,
centerTitle: widget.centerTitle,
excludeHeaderSemantics: widget.excludeHeaderSemantics,
titleSpacing: widget.titleSpacing,
collapsedHeight: widget.collapsedHeight,
floating: widget.floating,
pinned: widget.pinned,
snap: widget.snap,
stretch: widget.stretch,
stretchTriggerOffset: widget.stretchTriggerOffset,
onStretchTrigger: widget.onStretchTrigger,
shape: widget.shape,
toolbarHeight: widget.toolbarHeight,
expandedHeight: _height,
leadingWidth: widget.leadingWidth,
toolbarTextStyle: widget.toolbarTextStyle,
titleTextStyle: widget.titleTextStyle,
systemOverlayStyle: widget.systemOverlayStyle,
forceMaterialTransparency: widget.forceMaterialTransparency,
clipBehavior: widget.clipBehavior,
flexibleSpace: FlexibleSpaceBar(background: widget.flexibleSpace),
);
}
}

View File

@@ -24,6 +24,7 @@ class NetworkImgLayer extends StatelessWidget {
this.semanticsLabel,
this.ignoreHeight,
this.radius,
this.imageBuilder,
});
final String? src;
@@ -37,6 +38,7 @@ class NetworkImgLayer extends StatelessWidget {
final String? semanticsLabel;
final bool? ignoreHeight;
final double? radius;
final ImageWidgetBuilder? imageBuilder;
@override
Widget build(BuildContext context) {
@@ -84,6 +86,7 @@ class NetworkImgLayer extends StatelessWidget {
placeholder(context),
placeholder: (BuildContext context, String url) =>
placeholder(context),
imageBuilder: imageBuilder,
),
)
: placeholder(context);

View File

@@ -0,0 +1,261 @@
import 'package:PiliPalaX/models/space_archive/item.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import '../../utils/utils.dart';
import '../constants.dart';
import 'badge.dart';
import 'network_img_layer.dart';
// 视频卡片 - 水平布局
class VideoCardHMemberVideo extends StatelessWidget {
const VideoCardHMemberVideo({
super.key,
required this.videoItem,
this.longPress,
this.longPressEnd,
this.source = 'normal',
this.showOwner = true,
this.showView = true,
this.showDanmaku = true,
this.showPubdate = false,
});
final Item videoItem;
final Function()? longPress;
final Function()? longPressEnd;
final String source;
final bool showOwner;
final bool showView;
final bool showDanmaku;
final bool showPubdate;
@override
Widget build(BuildContext context) {
final int aid = int.tryParse(videoItem.param ?? '') ?? -1;
final String bvid = videoItem.bvid ?? '';
String type = 'video';
// try {
// type = videoItem.type;
// } catch (_) {}
// List<VideoCustomAction> actions =
// VideoCustomActions(videoItem, context).actions;
final String heroTag = Utils.makeHeroTag(aid);
return Stack(children: [
Semantics(
// label: Utils.videoItemSemantics(videoItem),
excludeSemantics: true,
// customSemanticsActions: <CustomSemanticsAction, void Function()>{
// for (var item in actions)
// CustomSemanticsAction(label: item.title): item.onTap!,
// },
child: InkWell(
borderRadius: BorderRadius.circular(12),
onLongPress: () {
if (longPress != null) {
longPress!();
}
},
onTap: () async {
if (type == 'ketang') {
SmartDialog.showToast('课堂视频暂不支持播放');
return;
}
try {
// PiliScheme.routePush(Uri.parse(videoItem.smallCoverV5.base.uri));
// final int cid =
// videoItem.smallCoverV5.base.playerArgs.cid.toInt() ??
// await SearchHttp.ab2c(aid: aid, bvid: bvid);
Get.toNamed('/video?bvid=$bvid&cid=${videoItem.firstCid}',
arguments: {'heroTag': heroTag});
// Get.toNamed('/video?bvid=$bvid&cid=$cid',
// arguments: {'videoItem': videoItem, 'heroTag': heroTag});
} catch (err) {
SmartDialog.showToast(err.toString());
}
},
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints boxConstraints) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(
builder: (BuildContext context,
BoxConstraints boxConstraints) {
final double maxWidth = boxConstraints.maxWidth;
final double maxHeight = boxConstraints.maxHeight;
return Stack(
children: [
Hero(
tag: heroTag,
child: NetworkImgLayer(
src: videoItem.cover,
width: maxWidth,
height: maxHeight,
),
),
if (videoItem.duration != null)
PBadge(
text: Utils.timeFormat(videoItem.duration),
right: 6.0,
bottom: 6.0,
type: 'gray',
),
if (type != 'video')
PBadge(
text: type,
left: 6.0,
bottom: 6.0,
type: 'primary',
),
// if (videoItem.rcmdReason != null &&
// videoItem.rcmdReason.content != '')
// pBadge(videoItem.rcmdReason.content, context,
// 6.0, 6.0, null, null),
],
);
},
),
),
VideoContent(
videoItem: videoItem,
source: source,
showOwner: showOwner,
showView: showView,
showDanmaku: showDanmaku,
showPubdate: showPubdate,
)
],
);
},
),
),
),
// if (source == 'normal')
// Positioned(
// bottom: 0,
// right: 0,
// child: VideoPopupMenu(
// size: 29,
// iconSize: 17,
// actions: actions,
// ),
// ),
]);
}
}
class VideoContent extends StatelessWidget {
final Item 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 += ' ';
return Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...[
Expanded(
child: Text(
videoItem.title ?? '',
textAlign: TextAlign.start,
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
],
// 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),
// ),
// ),
// const SizedBox(height: 4),
if (showOwner || showPubdate)
Text(
videoItem.publishTimeText ?? '',
maxLines: 1,
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
height: 1,
color: Theme.of(context).colorScheme.outline,
overflow: TextOverflow.clip,
),
),
const SizedBox(height: 3),
Text(
'${videoItem.viewContent} · ${videoItem.danmaku}',
maxLines: 1,
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
height: 1,
color: Theme.of(context).colorScheme.outline,
overflow: TextOverflow.clip,
),
),
// Row(
// children: [
// if (showView) ...[
// StatView(
// theme: 'gray',
// view: videoItem.stat.view as int,
// ),
// const SizedBox(width: 8),
// ],
// if (showDanmaku)
// StatDanMu(
// theme: 'gray',
// danmu: videoItem.stat.danmu as int,
// ),
// const Spacer(),
// if (source == 'normal') const SizedBox(width: 24),
// ],
// ),
],
),
),
);
}
}

View File

@@ -0,0 +1,358 @@
import 'package:PiliPalaX/models/bangumi/info.dart';
import 'package:PiliPalaX/models/space/item.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import '../../models/home/rcmd/result.dart';
import '../../models/model_rec_video_item.dart';
import 'stat/danmu.dart';
import 'stat/view.dart';
import '../../http/dynamics.dart';
import '../../http/search.dart';
import '../../models/common/search_type.dart';
import '../../utils/id_utils.dart';
import '../../utils/utils.dart';
import '../constants.dart';
import 'badge.dart';
import 'network_img_layer.dart';
import 'video_popup_menu.dart';
// 视频卡片 - 垂直布局
class VideoCardVMemberHome extends StatelessWidget {
final Item videoItem;
final Function()? longPress;
final Function()? longPressEnd;
const VideoCardVMemberHome({
super.key,
required this.videoItem,
this.longPress,
this.longPressEnd,
});
bool isStringNumeric(String str) {
RegExp numericRegex = RegExp(r'^\d+$');
return numericRegex.hasMatch(str);
}
void onPushDetail(heroTag) async {
String goto = videoItem.goto ?? '';
switch (goto) {
// case 'bangumi':
// if (videoItem.bangumiBadge == '电影') {
// SmartDialog.showToast('暂不支持电影观看');
// return;
// }
// int epId = videoItem.param;
// Utils.viewBangumi(epId: epId);
// SmartDialog.showLoading(msg: '资源获取中');
// var result = await SearchHttp.bangumiInfo(seasonId: null, epId: epId);
// SmartDialog.dismiss();
// if (result['status']) {
// var bangumiDetail = result['data'];
// EpisodeItem episode = result['data'].episodes.first;
// int? epId = result['data'].userStatus?.progress?.lastEpId;
// if (epId == null) {
// epId = episode.epId;
// } else {
// for (var item in result['data'].episodes) {
// if (item.epId == epId) {
// episode = item;
// break;
// }
// }
// }
// String bvid = episode.bvid!;
// int cid = episode.cid!;
// String pic = episode.cover!;
// String seasonId = bangumiDetail.seasonId;
// dynamic heroTag = Utils.makeHeroTag(cid);
// Get.toNamed(
// '/video?bvid=$bvid&cid=$cid&seasonId=$seasonId&epId=$epId',
// arguments: {
// 'pic': pic,
// 'heroTag': heroTag,
// 'videoType': SearchType.media_bangumi,
// },
// );
// } else {
// SmartDialog.showToast(result['msg']);
// }
// break;
case 'av':
String bvid = videoItem.bvid ?? '';
Get.toNamed('/video?bvid=$bvid&cid=${videoItem.firstCid}', arguments: {
// 'videoItem': videoItem,
'pic': videoItem.cover,
'heroTag': heroTag,
});
break;
// 动态
// case 'picture':
// try {
// String dynamicType = 'picture';
// String uri = videoItem.uri;
// String id = '';
// if (videoItem.uri.startsWith('bilibili://article/')) {
// // https://www.bilibili.com/read/cv27063554
// dynamicType = 'read';
// RegExp regex = RegExp(r'\d+');
// Match match = regex.firstMatch(videoItem.uri)!;
// String matchedNumber = match.group(0)!;
// videoItem.param = int.parse(matchedNumber);
// id = 'cv${videoItem.param}';
// }
// if (uri.startsWith('http')) {
// String path = Uri.parse(uri).path;
// if (isStringNumeric(path.split('/')[1])) {
// // 请求接口
// var res =
// await DynamicsHttp.dynamicDetail(id: path.split('/')[1]);
// if (res['status']) {
// Get.toNamed('/dynamicDetail', arguments: {
// 'item': res['data'],
// 'floor': 1,
// 'action': 'detail'
// });
// } else {
// SmartDialog.showToast(res['msg']);
// }
// return;
// }
// }
// Get.toNamed('/htmlRender', parameters: {
// 'url': uri,
// 'title': videoItem.title,
// 'id': id,
// 'dynamicType': dynamicType
// });
// } catch (err) {
// SmartDialog.showToast(err.toString());
// }
// break;
default:
SmartDialog.showToast(goto);
Get.toNamed(
'/webviewnew',
parameters: {
'url': videoItem.uri ?? '',
'type': 'url',
'pageTitle': videoItem.title ?? '',
},
);
}
}
@override
Widget build(BuildContext context) {
String heroTag = Utils.makeHeroTag(videoItem.bvid);
// List<VideoCustomAction> actions =
// VideoCustomActions(videoItem, context).actions;
return Stack(children: [
Semantics(
// label: Utils.videoItemSemantics(videoItem),
excludeSemantics: true,
// customSemanticsActions: <CustomSemanticsAction, void Function()>{
// for (var item in actions)
// CustomSemanticsAction(label: item.title): item.onTap!,
// },
child: Card(
clipBehavior: Clip.hardEdge,
margin: EdgeInsets.zero,
child: InkWell(
onTap: () async => onPushDetail(heroTag),
onLongPress: () {
if (longPress != null) {
longPress!();
}
},
child: Column(
children: [
AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(builder: (context, boxConstraints) {
double maxWidth = boxConstraints.maxWidth;
double maxHeight = boxConstraints.maxHeight;
return Stack(
children: [
Hero(
tag: heroTag,
child: NetworkImgLayer(
src: videoItem.cover,
width: maxWidth,
height: maxHeight,
),
),
if ((videoItem.duration ?? -1) > 0)
PBadge(
bottom: 6,
right: 7,
size: 'small',
type: 'gray',
text: Utils.timeFormat(videoItem.duration),
// semanticsLabel:
// '时长${Utils.durationReadFormat(Utils.timeFormat(videoItem.duration))}',
)
],
);
}),
),
videoContent(context, videoItem)
],
),
),
),
),
// if (videoItem.goto == 'av')
// Positioned(
// right: -5,
// bottom: -2,
// child: VideoPopupMenu(
// size: 29,
// iconSize: 17,
// actions: actions,
// )),
]);
}
}
Widget videoContent(BuildContext context, Item videoItem) {
return Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(6, 5, 6, 5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Expanded(
child: Text('${videoItem.title}\n',
// semanticsLabel: "${videoItem.title}",
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontWeight: FontWeight.w400,
height: 1.38,
)),
),
],
),
const Spacer(),
// const SizedBox(height: 2),
// VideoStat(
// videoItem: videoItem,
// ),
Row(
children: [
// if (videoItem.goto == 'bangumi') ...[
// PBadge(
// text: videoItem.bangumiBadge,
// stack: 'normal',
// size: 'small',
// type: 'line',
// fs: 9,
// )
// ],
// if (videoItem.rcmdReason != null) ...[
// PBadge(
// text: videoItem.rcmdReason,
// stack: 'normal',
// size: 'small',
// type: 'color',
// )
// ],
if (videoItem.goto == 'picture') ...[
const PBadge(
text: '动态',
stack: 'normal',
size: 'small',
type: 'line',
fs: 9,
)
],
// if (videoItem.isFollowed == 1) ...[
// const PBadge(
// text: '已关注',
// stack: 'normal',
// size: 'small',
// type: 'color',
// )
// ],
Expanded(
flex: 1,
child: Text(
videoItem.author ?? '',
// semanticsLabel: "Up主${videoItem.owner.name}",
maxLines: 1,
overflow: TextOverflow.clip,
style: TextStyle(
height: 1.5,
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
),
if (videoItem.goto == 'av') const SizedBox(width: 10)
],
),
],
),
),
);
}
// Widget videoStat(BuildContext context, Item videoItem) {
// return Row(
// children: [
// StatView(
// theme: 'gray',
// view: videoItem.stat.view,
// goto: videoItem.goto,
// ),
// const SizedBox(width: 6),
// if (videoItem.goto != 'picture')
// StatDanMu(
// theme: 'gray',
// danmu: videoItem.stat.danmu,
// ),
// if (videoItem is RecVideoItemModel) ...<Widget>[
// const Spacer(),
// Expanded(
// flex: 0,
// child: RichText(
// maxLines: 1,
// text: TextSpan(
// style: TextStyle(
// fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
// color:
// Theme.of(context).colorScheme.outline.withOpacity(0.8),
// ),
// text: Utils.formatTimestampToRelativeTime(videoItem.pubdate)),
// )),
// const SizedBox(width: 2),
// ],
// if (videoItem is RecVideoItemAppModel &&
// videoItem.desc != null &&
// videoItem.desc.contains(' · ')) ...<Widget>[
// const Spacer(),
// Expanded(
// flex: 0,
// child: RichText(
// maxLines: 1,
// text: TextSpan(
// style: TextStyle(
// fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
// color:
// Theme.of(context).colorScheme.outline.withOpacity(0.8),
// ),
// text: Utils.shortenChineseDateString(
// videoItem.desc.split(' · ').last)),
// )),
// const SizedBox(width: 2),
// ]
// ],
// );
// }