mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
mod: show all the staffs on intro panel
This commit is contained in:
67
lib/common/widgets/self_sized_horizontal_list.dart
Normal file
67
lib/common/widgets/self_sized_horizontal_list.dart
Normal file
@@ -0,0 +1,67 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// https://stackoverflow.com/a/76605401
|
||||
|
||||
class SelfSizedHorizontalList extends StatefulWidget {
|
||||
final Widget Function(int) childBuilder;
|
||||
final int itemCount;
|
||||
final double gapSize;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
const SelfSizedHorizontalList({
|
||||
super.key,
|
||||
required this.childBuilder,
|
||||
required this.itemCount,
|
||||
this.gapSize = 5,
|
||||
this.padding,
|
||||
});
|
||||
|
||||
@override
|
||||
State<SelfSizedHorizontalList> createState() =>
|
||||
_SelfSizedHorizontalListState();
|
||||
}
|
||||
|
||||
class _SelfSizedHorizontalListState extends State<SelfSizedHorizontalList> {
|
||||
final infoKey = GlobalKey();
|
||||
|
||||
double? prevHeight;
|
||||
double? get height {
|
||||
if (prevHeight != null) return prevHeight;
|
||||
prevHeight = infoKey.globalPaintBounds?.height;
|
||||
return prevHeight;
|
||||
}
|
||||
|
||||
bool get isInit => height == null;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (height == null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((v) => setState(() {}));
|
||||
}
|
||||
if (widget.itemCount == 0) return const SizedBox();
|
||||
if (isInit) return Container(key: infoKey, child: widget.childBuilder(0));
|
||||
|
||||
return SizedBox(
|
||||
height: height,
|
||||
child: ListView.separated(
|
||||
padding: widget.padding,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: widget.itemCount,
|
||||
itemBuilder: (c, i) => widget.childBuilder.call(i),
|
||||
separatorBuilder: (c, i) => SizedBox(width: widget.gapSize),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension GlobalKeyExtension on GlobalKey {
|
||||
Rect? get globalPaintBounds {
|
||||
final renderObject = currentContext?.findRenderObject();
|
||||
final translation = renderObject?.getTransformTo(null).getTranslation();
|
||||
if (translation != null && renderObject?.paintBounds != null) {
|
||||
final offset = Offset(translation.x, translation.y);
|
||||
return renderObject!.paintBounds.shift(offset);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ class VideoDetailData {
|
||||
String? likeIcon;
|
||||
bool? needJumpBv;
|
||||
String? epId;
|
||||
List<Staff>? staff;
|
||||
|
||||
VideoDetailData({
|
||||
this.bvid,
|
||||
@@ -103,6 +104,7 @@ class VideoDetailData {
|
||||
this.likeIcon,
|
||||
this.needJumpBv,
|
||||
this.epId,
|
||||
this.staff,
|
||||
});
|
||||
|
||||
VideoDetailData.fromJson(Map<String, dynamic> json) {
|
||||
@@ -152,6 +154,9 @@ class VideoDetailData {
|
||||
: HonorReply.fromJson(json["honor_reply"]);
|
||||
likeIcon = json["like_icon"];
|
||||
needJumpBv = json["need_jump_bv"];
|
||||
staff = json["staff"] == null
|
||||
? null
|
||||
: (json["staff"] as List).map((item) => Staff.fromJson(item)).toList();
|
||||
if (json['redirect_url'] != null) {
|
||||
epId = resolveEpId(json['redirect_url']);
|
||||
}
|
||||
@@ -276,6 +281,42 @@ class Dimension {
|
||||
}
|
||||
}
|
||||
|
||||
class Staff {
|
||||
dynamic mid;
|
||||
String? title;
|
||||
String? name;
|
||||
String? face;
|
||||
Vip? vip;
|
||||
|
||||
Staff({
|
||||
this.mid,
|
||||
this.title,
|
||||
this.name,
|
||||
this.face,
|
||||
this.vip,
|
||||
});
|
||||
|
||||
Staff.fromJson(Map<String, dynamic> json) {
|
||||
mid = json["mid"];
|
||||
title = json["title"];
|
||||
name = json["name"];
|
||||
face = json["face"];
|
||||
vip = json["vip"] == null ? null : Vip.fromJson(json["vip"]);
|
||||
}
|
||||
}
|
||||
|
||||
class Vip {
|
||||
dynamic type;
|
||||
|
||||
Vip({
|
||||
this.type,
|
||||
});
|
||||
|
||||
Vip.fromJson(Map<String, dynamic> json) {
|
||||
type = json["type"];
|
||||
}
|
||||
}
|
||||
|
||||
class HonorReply {
|
||||
List<Honor>? honor;
|
||||
|
||||
|
||||
@@ -126,6 +126,7 @@ class VideoIntroController extends GetxController {
|
||||
var result = await VideoHttp.videoIntro(bvid: bvid);
|
||||
if (result['status']) {
|
||||
videoDetail.value = result['data']!;
|
||||
videoItem!['staff'] = result['data'].staff;
|
||||
if (videoDetailController.videoItem['pic'] == null ||
|
||||
videoDetailController.videoItem['pic'] == '') {
|
||||
try {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:PiliPalaX/common/widgets/self_sized_horizontal_list.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
@@ -238,13 +239,15 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
child: videoItem['staff'] == null
|
||||
? GestureDetector(
|
||||
onTap: onPushMember,
|
||||
child: Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 1, horizontal: 0),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 1, horizontal: 0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -262,7 +265,8 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
loadingStatus
|
||||
@@ -271,11 +275,13 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 12, color: t.colorScheme.primary),
|
||||
fontSize: 12,
|
||||
color: t.colorScheme.primary),
|
||||
// semanticsLabel: "Up主:${owner.name}",
|
||||
),
|
||||
const SizedBox(height: 0),
|
||||
Obx(() => Text(
|
||||
Obx(
|
||||
() => Text(
|
||||
Utils.numFormat(videoIntroController
|
||||
.userStat.value['follower']),
|
||||
semanticsLabel:
|
||||
@@ -284,19 +290,69 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
fontSize: 12,
|
||||
color: t.colorScheme.outline,
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
),
|
||||
followButton(context, t),
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
)
|
||||
: SelfSizedHorizontalList(
|
||||
gapSize: 10,
|
||||
itemCount: videoItem['staff'].length,
|
||||
childBuilder: (index) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Get.toNamed(
|
||||
'/member?mid=${videoItem['staff'][index].mid}',
|
||||
arguments: {
|
||||
'face': videoItem['staff'][index].face,
|
||||
heroTag: Utils.makeHeroTag(
|
||||
videoItem['staff'][index].mid),
|
||||
}),
|
||||
child: NetworkImgLayer(
|
||||
type: 'avatar',
|
||||
src: videoItem['staff'][index].face,
|
||||
width: 40,
|
||||
height: 40,
|
||||
fadeInDuration: Duration.zero,
|
||||
fadeOutDuration: Duration.zero,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
videoItem['staff'][index].name.length > 5
|
||||
? '${videoItem['staff'][index].name.toString().substring(0, 5)}...'
|
||||
: videoItem['staff'][index].name,
|
||||
style: TextStyle(
|
||||
color:
|
||||
videoItem['staff'][index].vip.type == 2
|
||||
? Utils.vipColor
|
||||
: null,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
videoItem['staff'][index].title,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color:
|
||||
Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isHorizontal) ...[
|
||||
const SizedBox(width: 10),
|
||||
Expanded(child: actionGrid(context, videoIntroController)),
|
||||
]
|
||||
]),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
|
||||
Reference in New Issue
Block a user