mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-08 12:04:50 +08:00
@@ -1,180 +0,0 @@
|
||||
import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart';
|
||||
import 'package:PiliPlus/common/widgets/selectable_text.dart';
|
||||
import 'package:PiliPlus/models_new/video/video_ai_conclusion/model_result.dart';
|
||||
import 'package:PiliPlus/pages/common/slide/common_slide_page.dart';
|
||||
import 'package:PiliPlus/pages/video/controller.dart';
|
||||
import 'package:PiliPlus/utils/duration_utils.dart';
|
||||
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class AiConclusionPanel extends CommonSlidePage {
|
||||
final AiConclusionResult item;
|
||||
|
||||
const AiConclusionPanel({
|
||||
super.key,
|
||||
required this.item,
|
||||
});
|
||||
|
||||
@override
|
||||
State<AiConclusionPanel> createState() => _AiDetailState();
|
||||
}
|
||||
|
||||
class _AiDetailState extends State<AiConclusionPanel>
|
||||
with SingleTickerProviderStateMixin, CommonSlideMixin {
|
||||
@override
|
||||
Widget buildPage(ThemeData theme) {
|
||||
return Material(
|
||||
color: theme.colorScheme.surface,
|
||||
child: Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: Get.back,
|
||||
child: SizedBox(
|
||||
height: 35,
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 3,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.primary,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(3)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: enableSlide ? slideList(theme) : buildList(theme),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
late Key _key;
|
||||
late bool _isNested;
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
final controller = PrimaryScrollController.of(context);
|
||||
_isNested = controller is ExtendedNestedScrollController;
|
||||
_key = ValueKey(controller.hashCode);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildList(ThemeData theme) {
|
||||
final child = _buildContent(theme, widget.item);
|
||||
if (_isNested) {
|
||||
return ExtendedVisibilityDetector(
|
||||
uniqueKey: const Key('ai-conclusion'),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
Widget _buildContent(ThemeData theme, AiConclusionResult res) {
|
||||
return CustomScrollView(
|
||||
key: _key,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
slivers: [
|
||||
if (res.summary?.isNotEmpty == true) ...[
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14),
|
||||
child: selectableText(
|
||||
res.summary!,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (res.outline?.isNotEmpty == true)
|
||||
SliverToBoxAdapter(
|
||||
child: Divider(
|
||||
height: 20,
|
||||
color: theme.dividerColor.withValues(alpha: 0.1),
|
||||
thickness: 6,
|
||||
),
|
||||
),
|
||||
],
|
||||
if (res.outline?.isNotEmpty == true)
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 14,
|
||||
right: 14,
|
||||
bottom: MediaQuery.viewPaddingOf(context).bottom + 100,
|
||||
),
|
||||
sliver: SliverList.builder(
|
||||
itemCount: res.outline!.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = res.outline![index];
|
||||
return SelectionArea(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (index != 0) const SizedBox(height: 10),
|
||||
Text(
|
||||
item.title!,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
...?item.partOutline?.map(
|
||||
(item) => Wrap(
|
||||
children: [
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: theme.colorScheme.onSurface,
|
||||
height: 1.5,
|
||||
),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: DurationUtils.formatDuration(
|
||||
item.timestamp,
|
||||
),
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
recognizer:
|
||||
(NoDeadlineTapGestureRecognizer()
|
||||
..onTap = () {
|
||||
try {
|
||||
Get.find<VideoDetailController>(
|
||||
tag: Get.arguments['heroTag'],
|
||||
).plPlayerController.seekTo(
|
||||
Duration(
|
||||
seconds: item.timestamp!,
|
||||
),
|
||||
isSeek: false,
|
||||
);
|
||||
} catch (_) {}
|
||||
}),
|
||||
),
|
||||
const TextSpan(text: ' '),
|
||||
TextSpan(text: item.content!),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/stat/stat.dart';
|
||||
import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
import 'package:PiliPlus/models/common/stat_type.dart';
|
||||
import 'package:PiliPlus/models/common/video/video_quality.dart';
|
||||
import 'package:PiliPlus/models_new/pgc/pgc_info_model/episode.dart' as pgc;
|
||||
@@ -364,11 +363,11 @@ class _DownloadPanelState extends State<DownloadPanel> {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
child: SizedBox(
|
||||
height: 98,
|
||||
height: 110,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
type: .transparency,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (_onDownload(
|
||||
@@ -389,12 +388,12 @@ class _DownloadPanelState extends State<DownloadPanel> {
|
||||
children: [
|
||||
if (cover?.isNotEmpty == true)
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
clipBehavior: .none,
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
src: cover,
|
||||
width: 140.8,
|
||||
height: 88,
|
||||
width: 160,
|
||||
height: 110,
|
||||
cacheWidth: cacheWidth,
|
||||
),
|
||||
if (duration != null && duration > 0)
|
||||
@@ -402,14 +401,14 @@ class _DownloadPanelState extends State<DownloadPanel> {
|
||||
text: DurationUtils.formatDuration(duration),
|
||||
right: 6.0,
|
||||
bottom: 6.0,
|
||||
type: PBadgeType.gray,
|
||||
type: .gray,
|
||||
),
|
||||
if (isCharging == true)
|
||||
const PBadge(
|
||||
text: '充电专属',
|
||||
top: 6,
|
||||
right: 6,
|
||||
type: PBadgeType.error,
|
||||
type: .error,
|
||||
)
|
||||
else if (episode.badge != null)
|
||||
PBadge(
|
||||
@@ -417,9 +416,9 @@ class _DownloadPanelState extends State<DownloadPanel> {
|
||||
top: 6,
|
||||
right: 6,
|
||||
type: switch (episode.badge) {
|
||||
'预告' => PBadgeType.gray,
|
||||
'限免' => PBadgeType.free,
|
||||
_ => PBadgeType.primary,
|
||||
'预告' => .gray,
|
||||
'限免' => .free,
|
||||
_ => .primary,
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@@ -88,7 +88,7 @@ class LocalIntroController extends CommonIntroController {
|
||||
}
|
||||
|
||||
final index = (-1).obs;
|
||||
double get _offset => index * 100 + 7 - 35;
|
||||
double get _offset => index * 112 + 7 - 35;
|
||||
final list = RxList<BiliDownloadEntryInfo>();
|
||||
|
||||
@override
|
||||
|
||||
@@ -3,7 +3,6 @@ import 'dart:io';
|
||||
import 'package:PiliPlus/common/style.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
import 'package:PiliPlus/models/common/video/video_quality.dart';
|
||||
import 'package:PiliPlus/models_new/download/bili_download_entry_info.dart';
|
||||
import 'package:PiliPlus/pages/video/introduction/local/controller.dart';
|
||||
@@ -41,7 +40,7 @@ class _LocalIntroPanelState extends State<LocalIntroPanel>
|
||||
final item = _controller.list[index];
|
||||
return _buildItem(theme, currIndex == index, index, item);
|
||||
},
|
||||
itemExtent: 100,
|
||||
itemExtent: 112,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -56,11 +55,11 @@ class _LocalIntroPanelState extends State<LocalIntroPanel>
|
||||
final cover = File(path.join(entry.entryDirPath, PathUtils.coverName));
|
||||
final cacheWidth = entry.pageData?.cacheWidth ?? false;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
padding: const .only(bottom: 2),
|
||||
child: SizedBox(
|
||||
height: 98,
|
||||
height: 110,
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
type: .transparency,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (isCurr) {
|
||||
@@ -69,7 +68,7 @@ class _LocalIntroPanelState extends State<LocalIntroPanel>
|
||||
_controller.playIndex(index, entry: entry);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
padding: const .symmetric(
|
||||
horizontal: Style.safeSpace,
|
||||
vertical: 5,
|
||||
),
|
||||
@@ -77,24 +76,24 @@ class _LocalIntroPanelState extends State<LocalIntroPanel>
|
||||
spacing: 10,
|
||||
children: [
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
clipBehavior: .none,
|
||||
children: [
|
||||
cover.existsSync()
|
||||
? ClipRRect(
|
||||
borderRadius: Style.mdRadius,
|
||||
child: Image.file(
|
||||
cover,
|
||||
width: 140.8,
|
||||
height: 88,
|
||||
width: 160,
|
||||
height: 110,
|
||||
fit: BoxFit.cover,
|
||||
cacheWidth: cacheWidth ? 140.8 : null,
|
||||
cacheHeight: cacheWidth ? null : 88,
|
||||
cacheWidth: cacheWidth ? 160 : null,
|
||||
cacheHeight: cacheWidth ? null : 110,
|
||||
),
|
||||
)
|
||||
: NetworkImgLayer(
|
||||
src: entry.cover,
|
||||
width: 140.8,
|
||||
height: 88,
|
||||
width: 160,
|
||||
height: 110,
|
||||
),
|
||||
PBadge(
|
||||
text: DurationUtils.formatDuration(
|
||||
@@ -102,14 +101,14 @@ class _LocalIntroPanelState extends State<LocalIntroPanel>
|
||||
),
|
||||
right: 6.0,
|
||||
bottom: 6.0,
|
||||
type: PBadgeType.gray,
|
||||
type: .gray,
|
||||
),
|
||||
if (entry.videoQuality case final videoQuality?)
|
||||
PBadge(
|
||||
text: VideoQuality.fromCode(videoQuality).shortDesc,
|
||||
right: 6.0,
|
||||
top: 6.0,
|
||||
type: PBadgeType.gray,
|
||||
type: .gray,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -5,7 +5,6 @@ import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/stat/stat.dart';
|
||||
import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
import 'package:PiliPlus/models/common/stat_type.dart';
|
||||
import 'package:PiliPlus/models_new/media_list/media_list.dart';
|
||||
import 'package:PiliPlus/models_new/video/video_detail/episode.dart';
|
||||
@@ -131,7 +130,7 @@ class _MediaListPanelState extends State<MediaListPanel>
|
||||
),
|
||||
sliver: Obx(
|
||||
() => SliverFixedExtentList.builder(
|
||||
itemExtent: 100,
|
||||
itemExtent: 112,
|
||||
itemCount: widget.mediaList.length,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == widget.mediaList.length - 1 &&
|
||||
@@ -164,11 +163,11 @@ class _MediaListPanelState extends State<MediaListPanel>
|
||||
bvid: item.bvid,
|
||||
);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
padding: const .only(bottom: 2),
|
||||
child: SizedBox(
|
||||
height: 98,
|
||||
height: 110,
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
type: .transparency,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (item.type != 2) {
|
||||
@@ -181,23 +180,20 @@ class _MediaListPanelState extends State<MediaListPanel>
|
||||
onLongPress: onLongPress,
|
||||
onSecondaryTap: PlatformUtils.isMobile ? null : onLongPress,
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
clipBehavior: .none,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 5,
|
||||
),
|
||||
padding: const .symmetric(horizontal: 12, vertical: 5),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
crossAxisAlignment: .start,
|
||||
children: [
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
clipBehavior: .none,
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
src: item.cover,
|
||||
width: 140.8,
|
||||
height: 88,
|
||||
width: 160,
|
||||
height: 110,
|
||||
),
|
||||
if (item.badge?.isNotEmpty == true)
|
||||
PBadge(
|
||||
@@ -205,8 +201,8 @@ class _MediaListPanelState extends State<MediaListPanel>
|
||||
right: 6.0,
|
||||
top: 6.0,
|
||||
type: switch (item.badge) {
|
||||
'充电专属' => PBadgeType.error,
|
||||
_ => PBadgeType.primary,
|
||||
'充电专属' => .error,
|
||||
_ => .primary,
|
||||
},
|
||||
),
|
||||
PBadge(
|
||||
@@ -215,7 +211,7 @@ class _MediaListPanelState extends State<MediaListPanel>
|
||||
),
|
||||
right: 6.0,
|
||||
bottom: 6.0,
|
||||
type: PBadgeType.gray,
|
||||
type: .gray,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -177,7 +177,7 @@ class _HorizontalMemberPageState extends State<HorizontalMemberPage> {
|
||||
Loading() => SliverFixedExtentList.builder(
|
||||
itemCount: 10,
|
||||
itemBuilder: (_, _) => const VideoCardHSkeleton(),
|
||||
itemExtent: 100,
|
||||
itemExtent: 112,
|
||||
),
|
||||
Success(:final response) =>
|
||||
response != null && response.isNotEmpty
|
||||
@@ -191,7 +191,7 @@ class _HorizontalMemberPageState extends State<HorizontalMemberPage> {
|
||||
}
|
||||
final videoItem = response[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
padding: const .only(bottom: 2),
|
||||
child: VideoCardHMemberVideo(
|
||||
videoItem: videoItem,
|
||||
bvid: _bvid,
|
||||
@@ -209,7 +209,7 @@ class _HorizontalMemberPageState extends State<HorizontalMemberPage> {
|
||||
);
|
||||
},
|
||||
itemCount: response.length,
|
||||
itemExtent: 100,
|
||||
itemExtent: 112,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -199,13 +199,14 @@ class _PayCoinsPageState extends State<PayCoinsPage>
|
||||
}
|
||||
|
||||
Widget _buildCoinWidget(int index, double factor) {
|
||||
final filter = _getPayFilter(index);
|
||||
final coinSize = 35 + (factor * 15);
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
height: 70 + (factor * 30),
|
||||
width: 70 + (factor * 30),
|
||||
child: SizedBox.square(
|
||||
dimension: 70 + (factor * 30),
|
||||
child: ColorFiltered(
|
||||
colorFilter: ColorFilter.mode(
|
||||
_getPayFilter(index),
|
||||
filter,
|
||||
BlendMode.srcATop,
|
||||
),
|
||||
child: Stack(
|
||||
@@ -221,8 +222,8 @@ class _PayCoinsPageState extends State<PayCoinsPage>
|
||||
child: FadeTransition(
|
||||
opacity: _coinFadeAnim,
|
||||
child: Image.asset(
|
||||
height: 35 + (factor * 15),
|
||||
width: 35 + (factor * 15),
|
||||
height: coinSize,
|
||||
width: coinSize,
|
||||
index == 0 ? Assets.coinsOne : Assets.coinsTwo,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -408,7 +408,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
return Obx(
|
||||
() {
|
||||
final isFullScreen = this.isFullScreen;
|
||||
return scaffold(
|
||||
return scaffold_(
|
||||
appBar: removeAppBar(isFullScreen)
|
||||
? null
|
||||
: PreferredSize(
|
||||
@@ -693,7 +693,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
Widget get childWhenDisabledLandscape => Obx(
|
||||
() {
|
||||
final isFullScreen = this.isFullScreen;
|
||||
return scaffold(
|
||||
return scaffold_(
|
||||
appBar: removeAppBar(isFullScreen)
|
||||
? null
|
||||
: AppBar(backgroundColor: Colors.black, toolbarHeight: 0),
|
||||
@@ -859,7 +859,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
|
||||
Widget get childWhenDisabledAlmostSquare => Obx(() {
|
||||
final isFullScreen = this.isFullScreen;
|
||||
return scaffold(
|
||||
return scaffold_(
|
||||
appBar: removeAppBar(isFullScreen)
|
||||
? null
|
||||
: AppBar(backgroundColor: Colors.black, toolbarHeight: 0),
|
||||
@@ -1236,6 +1236,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
height: height,
|
||||
child: GestureDetector(
|
||||
onTap: handlePlay,
|
||||
behavior: .opaque,
|
||||
@@ -1344,7 +1345,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
Positioned(
|
||||
left: 16,
|
||||
bottom: isFullScreen ? max(75, maxHeight * 0.25) : 75,
|
||||
width: MediaQuery.textScalerOf(context).scale(120),
|
||||
width: 120,
|
||||
child: AnimatedList(
|
||||
padding: EdgeInsets.zero,
|
||||
key: videoDetailController.listKey,
|
||||
|
||||
@@ -94,7 +94,7 @@ class _ViewPointsPageState extends State<ViewPointsPage>
|
||||
final child = ListView.builder(
|
||||
key: _key,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
padding: EdgeInsets.only(
|
||||
padding: .only(
|
||||
top: 7,
|
||||
bottom: MediaQuery.viewPaddingOf(context).bottom + 100,
|
||||
),
|
||||
@@ -125,7 +125,7 @@ class _ViewPointsPageState extends State<ViewPointsPage>
|
||||
Widget _buildItem(ThemeData theme, ViewPointSegment segment, bool isCurr) {
|
||||
final theme = Theme.of(context);
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
type: .transparency,
|
||||
child: InkWell(
|
||||
onTap: segment.from != null
|
||||
? () {
|
||||
@@ -137,29 +137,26 @@ class _ViewPointsPageState extends State<ViewPointsPage>
|
||||
}
|
||||
: null,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: Style.safeSpace,
|
||||
vertical: 5,
|
||||
),
|
||||
padding: const .symmetric(horizontal: Style.safeSpace, vertical: 5),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
crossAxisAlignment: .start,
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
src: segment.url,
|
||||
width: 140.8,
|
||||
height: 88,
|
||||
width: 160,
|
||||
height: 110,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
spacing: 10,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: .start,
|
||||
children: [
|
||||
Text(
|
||||
segment.title ?? '',
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
overflow: .ellipsis,
|
||||
style: isCurr
|
||||
? TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
|
||||
Reference in New Issue
Block a user