mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
Refactor member page (#3)
* refactor: member page * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
|
||||
197
lib/common/widgets/dynamic_sliver_appbar.dart
Normal file
197
lib/common/widgets/dynamic_sliver_appbar.dart
Normal 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),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
261
lib/common/widgets/video_card_h_member_video.dart
Normal file
261
lib/common/widgets/video_card_h_member_video.dart
Normal 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),
|
||||
// ],
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
358
lib/common/widgets/video_card_v_member_home.dart
Normal file
358
lib/common/widgets/video_card_v_member_home.dart
Normal 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),
|
||||
// ]
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
Reference in New Issue
Block a user