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;
|
String? likeIcon;
|
||||||
bool? needJumpBv;
|
bool? needJumpBv;
|
||||||
String? epId;
|
String? epId;
|
||||||
|
List<Staff>? staff;
|
||||||
|
|
||||||
VideoDetailData({
|
VideoDetailData({
|
||||||
this.bvid,
|
this.bvid,
|
||||||
@@ -103,6 +104,7 @@ class VideoDetailData {
|
|||||||
this.likeIcon,
|
this.likeIcon,
|
||||||
this.needJumpBv,
|
this.needJumpBv,
|
||||||
this.epId,
|
this.epId,
|
||||||
|
this.staff,
|
||||||
});
|
});
|
||||||
|
|
||||||
VideoDetailData.fromJson(Map<String, dynamic> json) {
|
VideoDetailData.fromJson(Map<String, dynamic> json) {
|
||||||
@@ -152,6 +154,9 @@ class VideoDetailData {
|
|||||||
: HonorReply.fromJson(json["honor_reply"]);
|
: HonorReply.fromJson(json["honor_reply"]);
|
||||||
likeIcon = json["like_icon"];
|
likeIcon = json["like_icon"];
|
||||||
needJumpBv = json["need_jump_bv"];
|
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) {
|
if (json['redirect_url'] != null) {
|
||||||
epId = resolveEpId(json['redirect_url']);
|
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 {
|
class HonorReply {
|
||||||
List<Honor>? honor;
|
List<Honor>? honor;
|
||||||
|
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ class VideoIntroController extends GetxController {
|
|||||||
var result = await VideoHttp.videoIntro(bvid: bvid);
|
var result = await VideoHttp.videoIntro(bvid: bvid);
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
videoDetail.value = result['data']!;
|
videoDetail.value = result['data']!;
|
||||||
|
videoItem!['staff'] = result['data'].staff;
|
||||||
if (videoDetailController.videoItem['pic'] == null ||
|
if (videoDetailController.videoItem['pic'] == null ||
|
||||||
videoDetailController.videoItem['pic'] == '') {
|
videoDetailController.videoItem['pic'] == '') {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:PiliPalaX/common/widgets/self_sized_horizontal_list.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
@@ -238,65 +239,120 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(children: [
|
Row(
|
||||||
Expanded(
|
children: [
|
||||||
child: GestureDetector(
|
Expanded(
|
||||||
onTap: onPushMember,
|
child: videoItem['staff'] == null
|
||||||
child: Container(
|
? GestureDetector(
|
||||||
padding:
|
onTap: onPushMember,
|
||||||
const EdgeInsets.symmetric(vertical: 1, horizontal: 0),
|
child: Container(
|
||||||
child: Row(
|
padding: const EdgeInsets.symmetric(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
vertical: 1, horizontal: 0),
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Row(
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
NetworkImgLayer(
|
mainAxisSize: MainAxisSize.min,
|
||||||
type: 'avatar',
|
children: [
|
||||||
src: loadingStatus
|
NetworkImgLayer(
|
||||||
? videoItem['owner']?.face ?? ""
|
type: 'avatar',
|
||||||
: widget.videoDetail!.owner!.face,
|
src: loadingStatus
|
||||||
width: 30,
|
? videoItem['owner']?.face ?? ""
|
||||||
height: 30,
|
: widget.videoDetail!.owner!.face,
|
||||||
fadeInDuration: Duration.zero,
|
width: 30,
|
||||||
fadeOutDuration: Duration.zero,
|
height: 30,
|
||||||
),
|
fadeInDuration: Duration.zero,
|
||||||
const SizedBox(width: 10),
|
fadeOutDuration: Duration.zero,
|
||||||
Expanded(
|
),
|
||||||
child: Column(
|
const SizedBox(width: 10),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Expanded(
|
||||||
children: [
|
child: Column(
|
||||||
Text(
|
crossAxisAlignment:
|
||||||
loadingStatus
|
CrossAxisAlignment.start,
|
||||||
? videoItem['owner']?.name ?? ""
|
children: [
|
||||||
: widget.videoDetail!.owner!.name,
|
Text(
|
||||||
maxLines: 1,
|
loadingStatus
|
||||||
overflow: TextOverflow.ellipsis,
|
? videoItem['owner']?.name ?? ""
|
||||||
style: TextStyle(
|
: widget.videoDetail!.owner!.name,
|
||||||
fontSize: 12, color: t.colorScheme.primary),
|
maxLines: 1,
|
||||||
// semanticsLabel: "Up主:${owner.name}",
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: t.colorScheme.primary),
|
||||||
|
// semanticsLabel: "Up主:${owner.name}",
|
||||||
|
),
|
||||||
|
const SizedBox(height: 0),
|
||||||
|
Obx(
|
||||||
|
() => Text(
|
||||||
|
Utils.numFormat(videoIntroController
|
||||||
|
.userStat.value['follower']),
|
||||||
|
semanticsLabel:
|
||||||
|
"${Utils.numFormat(videoIntroController.userStat.value['follower'])}粉丝",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: t.colorScheme.outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
followButton(context, t),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 0),
|
)
|
||||||
Obx(() => Text(
|
: SelfSizedHorizontalList(
|
||||||
Utils.numFormat(videoIntroController
|
gapSize: 10,
|
||||||
.userStat.value['follower']),
|
itemCount: videoItem['staff'].length,
|
||||||
semanticsLabel:
|
childBuilder: (index) => Column(
|
||||||
"${Utils.numFormat(videoIntroController.userStat.value['follower'])}粉丝",
|
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(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: t.colorScheme.outline,
|
color:
|
||||||
|
Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
)),
|
),
|
||||||
],
|
],
|
||||||
)),
|
),
|
||||||
followButton(context, t),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)),
|
if (isHorizontal) ...[
|
||||||
if (isHorizontal) ...[
|
const SizedBox(width: 10),
|
||||||
const SizedBox(width: 10),
|
Expanded(child: actionGrid(context, videoIntroController)),
|
||||||
Expanded(child: actionGrid(context, videoIntroController)),
|
]
|
||||||
]
|
],
|
||||||
]),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
|
|||||||
Reference in New Issue
Block a user