Compare commits

..

9 Commits

Author SHA1 Message Date
bggRGjQaUbCoE
21550815db fix: seek preview image
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-30 12:11:44 +08:00
bggRGjQaUbCoE
02af3a18ff opt: video seek preview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-30 10:08:15 +08:00
bggRGjQaUbCoE
a5a13b45cf fix: seek preview index
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-29 23:45:10 +08:00
bggRGjQaUbCoE
0fd232ab3a feat: video seek preview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-29 21:20:58 +08:00
bggRGjQaUbCoE
8d83143ca6 opt: fav
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-29 15:29:20 +08:00
bggRGjQaUbCoE
74452cd622 mod: save as livephoto for ios
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-29 12:07:00 +08:00
bggRGjQaUbCoE
cf2e8cec54 fix: horizontal preview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-28 23:27:46 +08:00
bggRGjQaUbCoE
5231faf254 opt: pages
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-28 21:57:57 +08:00
bggRGjQaUbCoE
959d4de78a opt: image preview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-28 18:33:40 +08:00
35 changed files with 1111 additions and 856 deletions

View File

@@ -39,51 +39,55 @@ class VideoCardHSkeleton extends StatelessWidget {
),
// VideoContent(videoItem: videoItem)
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 4, 6, 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
color: Theme.of(context).colorScheme.onInverseSurface,
width: 200,
height: 11,
margin: const EdgeInsets.only(bottom: 5),
),
Container(
color: Theme.of(context).colorScheme.onInverseSurface,
width: 150,
height: 13,
),
const Spacer(),
Container(
color: Theme.of(context).colorScheme.onInverseSurface,
width: 100,
height: 13,
margin: const EdgeInsets.only(bottom: 5),
),
Row(
children: [
Container(
color: Theme.of(context)
.colorScheme
.onInverseSurface,
width: 40,
height: 13,
margin: const EdgeInsets.only(right: 8),
),
Container(
color: Theme.of(context)
.colorScheme
.onInverseSurface,
width: 40,
height: 13,
),
],
)
],
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 4, 6, 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
color:
Theme.of(context).colorScheme.onInverseSurface,
width: 200,
height: 11,
margin: const EdgeInsets.only(bottom: 5),
),
Container(
color:
Theme.of(context).colorScheme.onInverseSurface,
width: 150,
height: 13,
),
const Spacer(),
Container(
color:
Theme.of(context).colorScheme.onInverseSurface,
width: 100,
height: 13,
margin: const EdgeInsets.only(bottom: 5),
),
Row(
children: [
Container(
color: Theme.of(context)
.colorScheme
.onInverseSurface,
width: 40,
height: 13,
margin: const EdgeInsets.only(right: 8),
),
Container(
color: Theme.of(context)
.colorScheme
.onInverseSurface,
width: 40,
height: 13,
),
],
)
],
),
),
)),
),
],
),
);

View File

@@ -92,6 +92,15 @@ Widget imageview(
late final enableLivePhoto = GStorage.enableLivePhoto;
int parseSize(size) {
return switch (size) {
int() => size,
double() => size.round(),
String() => int.tryParse(size) ?? 1,
_ => 1,
};
}
return NineGridView(
type: NineGridType.weiBo,
margin: const EdgeInsets.only(top: 6),
@@ -111,17 +120,19 @@ Widget imageview(
onViewImage?.call();
context.imageView(
initialPage: index,
imgList: picArr
.map(
(item) => SourceModel(
sourceType: item.isLivePhoto && enableLivePhoto
? SourceType.livePhoto
: SourceType.networkImage,
url: item.url,
liveUrl: item.liveUrl,
),
)
.toList(),
imgList: picArr.map(
(item) {
bool isLive = item.isLivePhoto && enableLivePhoto;
return SourceModel(
sourceType:
isLive ? SourceType.livePhoto : SourceType.networkImage,
url: item.url,
liveUrl: isLive ? item.liveUrl : null,
width: isLive ? parseSize(item.width) : null,
height: isLive ? parseSize(item.height) : null,
);
},
).toList(),
onDismissed: onDismissed,
);
}
@@ -162,7 +173,7 @@ Widget imageview(
},
),
),
if (picArr[index].liveUrl?.isNotEmpty == true)
if (picArr[index].isLivePhoto)
const PBadge(
text: 'Live',
right: 8,

View File

@@ -41,11 +41,15 @@ class SourceModel {
final SourceType sourceType;
final String url;
final String? liveUrl;
final int? width;
final int? height;
const SourceModel({
this.sourceType = SourceType.networkImage,
required this.url,
this.liveUrl,
this.width,
this.height,
});
}
@@ -387,17 +391,6 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
},
child: const Text("保存图片"),
),
if (widget.sources[currentIndex.value].sourceType ==
SourceType.livePhoto)
PopupMenuItem(
onTap: () {
DownloadUtils.downloadVideo(
context,
widget.sources[currentIndex.value].liveUrl!,
);
},
child: const Text("保存 Live"),
),
if (widget.sources.length > 1)
PopupMenuItem(
onTap: () {
@@ -410,6 +403,23 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
},
child: const Text("保存全部图片"),
),
if (widget.sources[currentIndex.value].sourceType ==
SourceType.livePhoto)
PopupMenuItem(
onTap: () {
DownloadUtils.downloadLivePhoto(
context: context,
url: widget.sources[currentIndex.value].url,
liveUrl: widget
.sources[currentIndex.value].liveUrl!,
width:
widget.sources[currentIndex.value].width!,
height: widget
.sources[currentIndex.value].height!,
);
},
child: const Text("保存 Live Photo"),
),
];
},
child: const Icon(Icons.more_horiz, color: Colors.white),
@@ -468,8 +478,14 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
// });
// },
),
SourceType.livePhoto =>
IgnorePointer(child: Video(controller: _videoController!)),
SourceType.livePhoto => Obx(() => currentIndex.value == index
? IgnorePointer(
child: Video(
controller: _videoController!,
fill: Colors.transparent,
),
)
: const SizedBox.shrink()),
},
),
);
@@ -559,22 +575,6 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
dense: true,
title: const Text('保存图片', style: TextStyle(fontSize: 14)),
),
if (widget.sources[currentIndex.value].sourceType ==
SourceType.livePhoto)
ListTile(
onTap: () {
Get.back();
DownloadUtils.downloadVideo(
context,
widget.sources[currentIndex.value].liveUrl!,
);
},
dense: true,
title: const Text(
'保存 Live',
style: TextStyle(fontSize: 14),
),
),
if (widget.sources.length > 1)
ListTile(
onTap: () {
@@ -587,6 +587,25 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
dense: true,
title: const Text('保存全部图片', style: TextStyle(fontSize: 14)),
),
if (widget.sources[currentIndex.value].sourceType ==
SourceType.livePhoto)
ListTile(
onTap: () {
Get.back();
DownloadUtils.downloadLivePhoto(
context: context,
url: widget.sources[currentIndex.value].url,
liveUrl: widget.sources[currentIndex.value].liveUrl!,
width: widget.sources[currentIndex.value].width!,
height: widget.sources[currentIndex.value].height!,
);
},
dense: true,
title: const Text(
'保存 Live Photo',
style: TextStyle(fontSize: 14),
),
),
],
),
);

View File

@@ -410,7 +410,7 @@ class _NineGridViewState extends State<NineGridView> {
@override
Widget build(BuildContext context) {
Widget? child = Container();
Widget? child;
double? realWidth = widget.width;
double? realHeight = widget.height;
switch (widget.type) {

View File

@@ -151,7 +151,8 @@ class VideoCardH extends StatelessWidget {
},
),
),
videoContent(context)
const SizedBox(width: 10),
videoContent(context),
],
),
),
@@ -176,107 +177,102 @@ class VideoCardH extends StatelessWidget {
: '';
if (pubdate != '') pubdate += ' ';
return Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (videoItem.title is String)
Expanded(
child: Text(
videoItem.title as String,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (videoItem.title is String)
Expanded(
child: Text(
videoItem.title,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
),
)
else
Expanded(
child: RichText(
overflow: TextOverflow.ellipsis,
maxLines: 2,
textScaler: MediaQuery.textScalerOf(context),
text: TextSpan(
children: [
for (final i in videoItem.title) ...[
TextSpan(
text: i['text'] as String,
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.bodyMedium!
.fontSize,
letterSpacing: 0.3,
color: i['type'] == 'em'
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurface,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
)
else
Expanded(
child: Text.rich(
overflow: TextOverflow.ellipsis,
maxLines: 2,
TextSpan(
children: [
for (final i in videoItem.title) ...[
TextSpan(
text: i['text'] as String,
style: TextStyle(
fontSize:
Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
color: i['type'] == 'em'
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurface,
),
]
],
),
),
]
],
),
),
// 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)
Expanded(
flex: 0,
child: Text(
"$pubdate ${showOwner ? videoItem.owner.name : ''}",
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
height: 1,
color: Theme.of(context).colorScheme.outline,
overflow: TextOverflow.clip,
),
),
),
const SizedBox(height: 3),
Row(
children: [
if (showView) ...[
statView(
context: context,
theme: 'gray',
view: videoItem.stat.view as int,
),
const SizedBox(width: 8),
],
if (showDanmaku)
statDanMu(
context: context,
theme: 'gray',
danmu: videoItem.stat.danmu as int,
),
const Spacer(),
if (source == 'normal') const SizedBox(width: 24),
],
),
],
),
// 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)
Expanded(
flex: 0,
child: Text(
"$pubdate ${showOwner ? videoItem.owner.name : ''}",
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
height: 1,
color: Theme.of(context).colorScheme.outline,
overflow: TextOverflow.clip,
),
),
),
const SizedBox(height: 3),
Row(
children: [
if (showView) ...[
statView(
context: context,
theme: 'gray',
view: videoItem.stat.view as int,
),
const SizedBox(width: 8),
],
if (showDanmaku)
statDanMu(
context: context,
theme: 'gray',
danmu: videoItem.stat.danmu as int,
),
const Spacer(),
if (source == 'normal') const SizedBox(width: 24),
],
),
],
),
);
}

View File

@@ -111,7 +111,8 @@ class VideoCardHGrpc extends StatelessWidget {
},
),
),
videoContent(context)
const SizedBox(width: 10),
videoContent(context),
],
);
},
@@ -133,58 +134,43 @@ class VideoCardHGrpc extends StatelessWidget {
Widget videoContent(context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...[
Expanded(
child: Text(
videoItem.smallCoverV5.base.title,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
videoItem.smallCoverV5.base.title,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
),
],
// 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.smallCoverV5.rightDesc1,
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
height: 1,
color: Theme.of(context).colorScheme.outline,
overflow: TextOverflow.clip,
),
),
const SizedBox(height: 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.smallCoverV5.rightDesc2,
videoItem.smallCoverV5.rightDesc1,
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
@@ -193,26 +179,36 @@ class VideoCardHGrpc extends StatelessWidget {
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),
// ],
// ),
],
),
const SizedBox(height: 3),
Text(
videoItem.smallCoverV5.rightDesc2,
maxLines: 1,
style: TextStyle(
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

@@ -103,7 +103,8 @@ class VideoCardHMemberVideo extends StatelessWidget {
},
),
),
videoContent(context)
const SizedBox(width: 10),
videoContent(context),
],
);
},
@@ -125,66 +126,61 @@ class VideoCardHMemberVideo extends StatelessWidget {
Widget videoContent(context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...[
Expanded(
child: Text(
// videoItem.season?['title'] ?? videoItem.title ?? '',
videoItem.title ?? '',
textAlign: TextAlign.start,
style: TextStyle(
fontWeight: videoItem.bvid != null && videoItem.bvid == bvid
? FontWeight.bold
: null,
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
color: videoItem.bvid != null && videoItem.bvid == bvid
? Theme.of(context).colorScheme.primary
: null,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
// videoItem.season?['title'] ?? videoItem.title ?? '',
videoItem.title ?? '',
textAlign: TextAlign.start,
style: TextStyle(
fontWeight: videoItem.bvid != null && videoItem.bvid == bvid
? FontWeight.bold
: null,
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
color: videoItem.bvid != null && videoItem.bvid == bvid
? Theme.of(context).colorScheme.primary
: null,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
Text(
videoItem.season != null
? Utils.dateFormat(videoItem.season?['mtime'])
: videoItem.publishTimeText ?? '',
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
height: 1,
color: Theme.of(context).colorScheme.outline,
overflow: TextOverflow.clip,
),
),
const SizedBox(height: 3),
Row(
children: [
statView(
context: context,
theme: 'gray',
// view: videoItem.season?['view_content'] ??
// videoItem.viewContent,
view: videoItem.viewContent,
),
const SizedBox(width: 8),
statDanMu(
context: context,
theme: 'gray',
// danmu: videoItem.season?['danmaku'] ?? videoItem.danmaku,
danmu: videoItem.danmaku,
),
],
Text(
videoItem.season != null
? Utils.dateFormat(videoItem.season?['mtime'])
: videoItem.publishTimeText ?? '',
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
height: 1,
color: Theme.of(context).colorScheme.outline,
overflow: TextOverflow.clip,
),
),
const SizedBox(height: 3),
Row(
children: [
statView(
context: context,
theme: 'gray',
// view: videoItem.season?['view_content'] ??
// videoItem.viewContent,
view: videoItem.viewContent,
),
const SizedBox(width: 8),
statDanMu(
context: context,
theme: 'gray',
// danmu: videoItem.season?['danmaku'] ?? videoItem.danmaku,
danmu: videoItem.danmaku,
),
],
),
],
),
),
],
),
);
}

View File

@@ -314,10 +314,9 @@ class VideoCardV extends StatelessWidget {
const Spacer(),
Expanded(
flex: 0,
child: RichText(
child: Text.rich(
maxLines: 1,
textScaler: MediaQuery.textScalerOf(context),
text: TextSpan(
TextSpan(
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelSmall!.fontSize,
@@ -337,10 +336,9 @@ class VideoCardV extends StatelessWidget {
const Spacer(),
Expanded(
flex: 0,
child: RichText(
child: Text.rich(
maxLines: 1,
textScaler: MediaQuery.textScalerOf(context),
text: TextSpan(
TextSpan(
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelSmall!.fontSize,

View File

@@ -6,6 +6,7 @@ import 'package:PiliPlus/http/user.dart';
import 'package:PiliPlus/pages/common/common_controller.dart';
import 'package:PiliPlus/pages/video/detail/introduction/controller.dart';
import 'package:PiliPlus/pages/video/detail/introduction/pay_coins_page.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
@@ -59,8 +60,7 @@ class BangumiIntroController extends CommonController {
dynamic videoTags;
bool isLogin = false;
Rx<FavFolderData> favFolderData = FavFolderData().obs;
List addMediaIdsNew = [];
List delMediaIdsNew = [];
List? favIds;
dynamic userInfo;
late final enableQuickFav =
@@ -314,12 +314,20 @@ class BangumiIntroController extends CommonController {
});
return;
}
List addMediaIdsNew = [];
List delMediaIdsNew = [];
try {
for (var i in favFolderData.value.list!.toList()) {
bool isFaved = favIds?.contains(i.id) == true;
if (i.favState == 1) {
addMediaIdsNew.add(i.id);
if (isFaved.not) {
addMediaIdsNew.add(i.id);
}
} else {
delMediaIdsNew.add(i.id);
if (isFaved) {
delMediaIdsNew.add(i.id);
}
}
}
} catch (_) {}
@@ -330,8 +338,6 @@ class BangumiIntroController extends CommonController {
delIds: delMediaIdsNew.join(','),
);
if (result['status']) {
addMediaIdsNew = [];
delMediaIdsNew = [];
SmartDialog.showToast('操作成功');
Get.back();
Future.delayed(const Duration(milliseconds: 255), () {
@@ -394,14 +400,10 @@ class BangumiIntroController extends CommonController {
onChoose(bool checkValue, int index) {
feedBack();
List<FavFolderItemData> datalist = favFolderData.value.list!;
for (var i = 0; i < datalist.length; i++) {
if (i == index) {
datalist[i].favState = checkValue == true ? 1 : 0;
datalist[i].mediaCount = checkValue == true
? datalist[i].mediaCount! + 1
: datalist[i].mediaCount! - 1;
}
}
datalist[index].favState = checkValue ? 1 : 0;
datalist[index].mediaCount = checkValue
? datalist[index].mediaCount! + 1
: datalist[index].mediaCount! - 1;
favFolderData.value.list = datalist;
favFolderData.refresh();
}
@@ -487,6 +489,7 @@ class BangumiIntroController extends CommonController {
}
Future queryVideoInFolder() async {
favIds = null;
var result = await VideoHttp.videoInFolder(
mid: userInfo.mid,
rid: epId, // bangumi
@@ -494,6 +497,10 @@ class BangumiIntroController extends CommonController {
);
if (result['status']) {
favFolderData.value = result['data'];
favIds = favFolderData.value.list
?.where((item) => item.favState == 1)
.map((item) => item.id)
.toList();
}
return result;
}

View File

@@ -103,8 +103,8 @@ Widget forWard(item, context, source, callback, {floor = 1}) {
picsNodes(item.modules.moduleDynamic.major.opus.pics, callback),
// semanticsLabel: '动态图片',
),
if (item.modules.moduleDynamic.additional != null)
const SizedBox(height: 4),
// if (item.modules.moduleDynamic.additional != null)
// const SizedBox(height: 4),
],
const SizedBox(height: 4),
],

View File

@@ -53,7 +53,8 @@ class FavItem extends StatelessWidget {
},
),
),
videoContent(context)
const SizedBox(width: 10),
videoContent(context),
],
),
);
@@ -65,43 +66,40 @@ class FavItem extends StatelessWidget {
Widget videoContent(context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 2, 6, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
favFolderItem.title ?? '',
textAlign: TextAlign.start,
style: const TextStyle(
letterSpacing: 0.3,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
favFolderItem.title ?? '',
textAlign: TextAlign.start,
style: const TextStyle(
letterSpacing: 0.3,
),
if (favFolderItem.intro?.isNotEmpty == true)
Text(
favFolderItem.intro!,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
),
if (favFolderItem.intro?.isNotEmpty == true)
Text(
'${favFolderItem.mediaCount}个内容',
favFolderItem.intro!,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
const Spacer(),
Text(
Utils.isPublicText(favFolderItem.attr ?? 0),
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
Text(
'${favFolderItem.mediaCount}个内容',
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
],
),
),
const Spacer(),
Text(
Utils.isPublicText(favFolderItem.attr ?? 0),
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
],
),
);
}

View File

@@ -61,6 +61,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
}
},
child: Scaffold(
resizeToAvoidBottomInset: false,
floatingActionButton: Obx(
() => (_favDetailController.item.value.mediaCount ?? -1) > 0
? FloatingActionButton.extended(

View File

@@ -137,7 +137,8 @@ class FavVideoCardH extends StatelessWidget {
},
),
),
videoContent(context)
const SizedBox(width: 10),
videoContent(context),
],
),
);
@@ -149,113 +150,106 @@ class FavVideoCardH extends StatelessWidget {
Widget videoContent(context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 2, 6, 0),
child: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
videoItem.title,
textAlign: TextAlign.start,
style: const TextStyle(
letterSpacing: 0.3,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
child: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
videoItem.title,
textAlign: TextAlign.start,
style: const TextStyle(
letterSpacing: 0.3,
),
if (videoItem.ogv != null) ...[
Text(
videoItem.intro,
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
],
const Spacer(),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
if (videoItem.ogv != null) ...[
Text(
Utils.dateFormat(videoItem.favTime),
videoItem.intro,
style: TextStyle(
fontSize: 11,
color: Theme.of(context).colorScheme.outline),
),
if (videoItem.owner.name != '') ...[
Text(
videoItem.owner.name,
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
],
Padding(
padding: const EdgeInsets.only(top: 2),
child: Row(
children: [
statView(
context: context,
theme: 'gray',
view: videoItem.cntInfo['play'],
),
const SizedBox(width: 8),
statDanMu(
context: context,
theme: 'gray',
danmu: videoItem.cntInfo['danmaku'],
),
const Spacer(),
],
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
],
),
if (searchType != 1 && isOwner)
Positioned(
right: 0,
bottom: 0,
child: iconButton(
context: context,
icon: Icons.clear,
tooltip: '取消收藏',
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
bgColor: Colors.transparent,
onPressed: () {
showDialog(
context: Get.context!,
builder: (context) {
return AlertDialog(
title: const Text('提示'),
content: const Text('要取消收藏吗?'),
actions: [
TextButton(
onPressed: Get.back,
child: Text(
'取消',
style: TextStyle(
color:
Theme.of(context).colorScheme.outline),
),
),
TextButton(
onPressed: () async {
await callFn?.call();
Get.back();
},
child: const Text('确定取消'),
)
],
);
},
);
},
const Spacer(),
Text(
Utils.dateFormat(videoItem.favTime),
style: TextStyle(
fontSize: 11, color: Theme.of(context).colorScheme.outline),
),
if (videoItem.owner.name != '') ...[
Text(
videoItem.owner.name,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
],
Padding(
padding: const EdgeInsets.only(top: 2),
child: Row(
children: [
statView(
context: context,
theme: 'gray',
view: videoItem.cntInfo['play'],
),
const SizedBox(width: 8),
statDanMu(
context: context,
theme: 'gray',
danmu: videoItem.cntInfo['danmaku'],
),
const Spacer(),
],
),
),
],
),
],
),
if (searchType != 1 && isOwner)
Positioned(
right: 0,
bottom: 0,
child: iconButton(
context: context,
icon: Icons.clear,
tooltip: '取消收藏',
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
bgColor: Colors.transparent,
onPressed: () {
showDialog(
context: Get.context!,
builder: (context) {
return AlertDialog(
title: const Text('提示'),
content: const Text('要取消收藏吗?'),
actions: [
TextButton(
onPressed: Get.back,
child: Text(
'取消',
style: TextStyle(
color: Theme.of(context).colorScheme.outline),
),
),
TextButton(
onPressed: () async {
await callFn?.call();
Get.back();
},
child: const Text('确定取消'),
)
],
);
},
);
},
),
),
],
),
);
}

View File

@@ -291,7 +291,8 @@ class HistoryItem extends StatelessWidget {
),
],
),
videoContent(context)
const SizedBox(width: 10),
videoContent(context),
],
),
);
@@ -303,113 +304,108 @@ class HistoryItem extends StatelessWidget {
Widget videoContent(context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 2, 6, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
videoItem.title,
textAlign: TextAlign.start,
style: const TextStyle(
letterSpacing: 0.3,
),
maxLines: videoItem.videos > 1 ? 1 : 2,
overflow: TextOverflow.ellipsis,
),
if (videoItem.isFullScreen != null) ...[
const SizedBox(height: 2),
Text(
videoItem.title,
videoItem.isFullScreen,
textAlign: TextAlign.start,
style: const TextStyle(
letterSpacing: 0.3,
),
maxLines: videoItem.videos > 1 ? 1 : 2,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
if (videoItem.isFullScreen != null) ...[
const SizedBox(height: 2),
Text(
videoItem.isFullScreen,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
const Spacer(),
if (videoItem.authorName != '')
Row(
children: [
Text(
videoItem.authorName,
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
],
),
],
const Spacer(),
if (videoItem.authorName != '')
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
Utils.dateFormat(videoItem.viewAt!),
videoItem.authorName,
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline),
),
SizedBox(
width: 24,
height: 24,
child: PopupMenuButton<String>(
padding: EdgeInsets.zero,
tooltip: '功能菜单',
icon: Icon(
Icons.more_vert_outlined,
color: Theme.of(context).colorScheme.outline,
size: 18,
),
position: PopupMenuPosition.under,
// constraints: const BoxConstraints(maxHeight: 35),
onSelected: (String type) {},
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<String>>[
if (videoItem.history?.business != 'pgc' &&
videoItem.badge != '番剧' &&
!videoItem.tagName.contains('动画') &&
videoItem.history.business != 'live' &&
!videoItem.history.business.contains('article'))
PopupMenuItem<String>(
onTap: () async {
var res = await UserHttp.toViewLater(
bvid: videoItem.history.bvid);
SmartDialog.showToast(res['msg']);
},
value: 'pause',
height: 35,
child: const Row(
children: [
Icon(Icons.watch_later_outlined, size: 16),
SizedBox(width: 6),
Text('稍后再看', style: TextStyle(fontSize: 13))
],
),
),
PopupMenuItem<String>(
onTap: () => ctr!.delHistory(
videoItem.kid, videoItem.history.business),
value: 'pause',
height: 35,
child: const Row(
children: [
Icon(Icons.close_outlined, size: 16),
SizedBox(width: 6),
Text('删除记录', style: TextStyle(fontSize: 13))
],
),
),
],
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
],
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
Utils.dateFormat(videoItem.viewAt!),
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline),
),
SizedBox(
width: 24,
height: 24,
child: PopupMenuButton<String>(
padding: EdgeInsets.zero,
tooltip: '功能菜单',
icon: Icon(
Icons.more_vert_outlined,
color: Theme.of(context).colorScheme.outline,
size: 18,
),
position: PopupMenuPosition.under,
// constraints: const BoxConstraints(maxHeight: 35),
onSelected: (String type) {},
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<String>>[
if (videoItem.history?.business != 'pgc' &&
videoItem.badge != '番剧' &&
!videoItem.tagName.contains('动画') &&
videoItem.history.business != 'live' &&
!videoItem.history.business.contains('article'))
PopupMenuItem<String>(
onTap: () async {
var res = await UserHttp.toViewLater(
bvid: videoItem.history.bvid);
SmartDialog.showToast(res['msg']);
},
value: 'pause',
height: 35,
child: const Row(
children: [
Icon(Icons.watch_later_outlined, size: 16),
SizedBox(width: 6),
Text('稍后再看', style: TextStyle(fontSize: 13))
],
),
),
PopupMenuItem<String>(
onTap: () => ctr!.delHistory(
videoItem.kid, videoItem.history.business),
value: 'pause',
height: 35,
child: const Row(
children: [
Icon(Icons.close_outlined, size: 16),
SizedBox(width: 6),
Text('删除记录', style: TextStyle(fontSize: 13))
],
),
),
],
),
),
],
),
],
),
);
}

View File

@@ -34,6 +34,7 @@ class _LaterPageState extends State<LaterPage> {
}
},
child: Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBarWidget(
visible: _laterController.enableMultiSelect.value,
child1: AppBar(

View File

@@ -58,7 +58,8 @@ class SeasonSeriesCard extends StatelessWidget {
},
),
),
videoContent(context)
const SizedBox(width: 10),
videoContent(context),
],
),
),
@@ -67,36 +68,33 @@ class SeasonSeriesCard extends StatelessWidget {
Widget videoContent(context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item['meta']['name'],
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item['meta']['name'],
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
),
const Spacer(),
Text(
Utils.dateFormat(item['meta']['ptime']),
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
height: 1,
color: Theme.of(context).colorScheme.outline,
overflow: TextOverflow.clip,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const Spacer(),
Text(
Utils.dateFormat(item['meta']['ptime']),
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
height: 1,
color: Theme.of(context).colorScheme.outline,
overflow: TextOverflow.clip,
),
const Spacer(),
],
),
),
const Spacer(),
],
),
);
}

View File

@@ -1,3 +1,5 @@
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
show SourceModel;
import 'package:PiliPlus/utils/extension.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
@@ -28,7 +30,10 @@ class ProfilePanel extends StatelessWidget {
GestureDetector(
onTap: () => context.imageView(
imgList: [
!loadingStatus ? memberInfo.face : ctr.face.value
SourceModel(
url:
!loadingStatus ? memberInfo.face : ctr.face.value,
)
],
),
child: NetworkImgLayer(

View File

@@ -69,7 +69,11 @@ class _SearchPanelState extends State<SearchPanel>
SliverGrid(
gridDelegate: SliverGridDelegateWithExtentAndRatio(
mainAxisSpacing: 2,
maxCrossAxisExtent: Grid.smallCardWidth * 2,
maxCrossAxisExtent: (widget.searchType == SearchType.video ||
widget.searchType == SearchType.article
? Grid.mediumCardWidth
: Grid.smallCardWidth) *
2,
childAspectRatio: StyleString.aspectRatio *
(widget.searchType == SearchType.media_bangumi ||
widget.searchType == SearchType.media_ft

View File

@@ -153,61 +153,54 @@ Widget searchArticlePanel(context, searchPanelCtr, LoadingState loadingState) {
);
}),
),
const SizedBox(width: 10),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(
10, 2, 6, 0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
RichText(
maxLines: 2,
textScaler:
MediaQuery.textScalerOf(
context),
text: TextSpan(
children: [
for (var i in loadingState
.response[index]
.title) ...[
TextSpan(
text: i['text'],
style: TextStyle(
color: i['type'] == 'em'
? Theme.of(context)
.colorScheme
.primary
: Theme.of(context)
.colorScheme
.onSurface,
),
),
]
],
),
),
const Spacer(),
Text(
Utils.dateFormat(
loadingState.response[index]
.pubTime,
formatType: 'detail'),
style: textStyle),
Row(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text.rich(
maxLines: 2,
TextSpan(
children: [
Text(
'${loadingState.response[index].view}浏览',
style: textStyle),
Text('', style: textStyle),
Text(
'${loadingState.response[index].reply}评论',
style: textStyle),
for (var i in loadingState
.response[index].title) ...[
TextSpan(
text: i['text'],
style: TextStyle(
color: i['type'] == 'em'
? Theme.of(context)
.colorScheme
.primary
: Theme.of(context)
.colorScheme
.onSurface,
),
),
]
],
),
],
),
),
const Spacer(),
Text(
Utils.dateFormat(
loadingState
.response[index].pubTime,
formatType: 'detail'),
style: textStyle),
Row(
children: [
Text(
'${loadingState.response[index].view}浏览',
style: textStyle),
Text('', style: textStyle),
Text(
'${loadingState.response[index].reply}评论',
style: textStyle),
],
),
],
),
),
],

View File

@@ -127,9 +127,8 @@ class LiveContent extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
RichText(
textScaler: MediaQuery.textScalerOf(context),
text: TextSpan(
Text.rich(
TextSpan(
children: [
for (var i in liveItem.title) ...[
TextSpan(

View File

@@ -75,12 +75,10 @@ Widget searchBangumiPanel(context, ctr, LoadingState loadingState) {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 4),
RichText(
Text.rich(
// maxLines: 1,
// overflow: TextOverflow.ellipsis,
textScaler:
MediaQuery.textScalerOf(context),
text: TextSpan(
TextSpan(
style: TextStyle(
color: Theme.of(context)
.colorScheme

View File

@@ -1917,11 +1917,18 @@ List<SettingsModel> get extraSettings => [
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '预览 Live Photo',
subtitle: '开启则以视频形式预览Live Photo否则预览静态图片',
subtitle: '开启则以视频形式预览 Live Photo否则预览静态图片',
leading: Icon(Icons.image_outlined),
setKey: SettingBoxKey.enableLivePhoto,
defaultVal: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '滑动跳转预览视频缩略图',
leading: Icon(Icons.preview_outlined),
setKey: SettingBoxKey.showSeekPreview,
defaultVal: true,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
enableFeedback: true,

View File

@@ -62,7 +62,8 @@ class SubItem extends StatelessWidget {
},
),
),
videoContent(context)
const SizedBox(width: 10),
videoContent(context),
],
),
);
@@ -84,39 +85,36 @@ class SubItem extends StatelessWidget {
return Expanded(
child: Stack(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(10, 2, 6, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
subFolderItem.title!,
textAlign: TextAlign.start,
style: const TextStyle(
letterSpacing: 0.3,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
subFolderItem.title!,
textAlign: TextAlign.start,
style: const TextStyle(
letterSpacing: 0.3,
),
const SizedBox(height: 2),
Text(
'[$typeString] UP主${subFolderItem.upper!.name!}',
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
const SizedBox(height: 2),
Text(
'[$typeString] UP主${subFolderItem.upper!.name!}',
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
const SizedBox(height: 2),
Text(
'${subFolderItem.mediaCount}个视频',
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
const SizedBox(height: 2),
Text(
'${subFolderItem.mediaCount}个视频',
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
const Spacer(),
],
),
),
const Spacer(),
],
),
Positioned(
bottom: 0,

View File

@@ -97,7 +97,8 @@ class SubVideoCardH extends StatelessWidget {
},
),
),
videoContent(context)
const SizedBox(width: 10),
videoContent(context),
],
),
);
@@ -109,52 +110,48 @@ class SubVideoCardH extends StatelessWidget {
Widget videoContent(context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 2, 6, 0),
child: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${videoItem.title}',
textAlign: TextAlign.start,
style: const TextStyle(
letterSpacing: 0.3,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
child: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${videoItem.title}',
textAlign: TextAlign.start,
style: const TextStyle(
letterSpacing: 0.3,
),
const Spacer(),
Text(
Utils.dateFormat(videoItem.pubtime),
style: TextStyle(
fontSize: 11,
color: Theme.of(context).colorScheme.outline),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const Spacer(),
Text(
Utils.dateFormat(videoItem.pubtime),
style: TextStyle(
fontSize: 11, color: Theme.of(context).colorScheme.outline),
),
Padding(
padding: const EdgeInsets.only(top: 2),
child: Row(
children: [
statView(
context: context,
theme: 'gray',
view: videoItem.cntInfo?['play'],
),
const SizedBox(width: 8),
statDanMu(
context: context,
theme: 'gray',
danmu: videoItem.cntInfo?['danmaku'],
),
const Spacer(),
],
),
Padding(
padding: const EdgeInsets.only(top: 2),
child: Row(
children: [
statView(
context: context,
theme: 'gray',
view: videoItem.cntInfo?['play'],
),
const SizedBox(width: 8),
statDanMu(
context: context,
theme: 'gray',
danmu: videoItem.cntInfo?['danmaku'],
),
const Spacer(),
],
),
),
],
),
],
),
),
],
),
],
),
);
}

View File

@@ -61,8 +61,7 @@ class VideoIntroController extends GetxController
RxBool hasFav = false.obs;
bool isLogin = false;
Rx<FavFolderData> favFolderData = FavFolderData().obs;
List addMediaIdsNew = [];
List delMediaIdsNew = [];
List? favIds;
// 关注状态 默认未关注
RxMap followStatus = {}.obs;
@@ -414,12 +413,20 @@ class VideoIntroController extends GetxController
});
return;
}
List addMediaIdsNew = [];
List delMediaIdsNew = [];
try {
for (var i in favFolderData.value.list!.toList()) {
bool isFaved = favIds?.contains(i.id) == true;
if (i.favState == 1) {
addMediaIdsNew.add(i.id);
if (isFaved.not) {
addMediaIdsNew.add(i.id);
}
} else {
delMediaIdsNew.add(i.id);
if (isFaved) {
delMediaIdsNew.add(i.id);
}
}
}
} catch (e) {
@@ -433,8 +440,6 @@ class VideoIntroController extends GetxController
);
SmartDialog.dismiss();
if (result['status']) {
addMediaIdsNew = [];
delMediaIdsNew = [];
Get.back();
// 重新获取收藏状态
await queryHasFavVideo();
@@ -495,10 +500,15 @@ class VideoIntroController extends GetxController
}
Future queryVideoInFolder() async {
favIds = null;
var result = await VideoHttp.videoInFolder(
mid: userInfo.mid, rid: IdUtils.bv2av(bvid));
if (result['status']) {
favFolderData.value = result['data'];
favIds = favFolderData.value.list
?.where((item) => item.favState == 1)
.map((item) => item.id)
.toList();
}
return result;
}
@@ -507,14 +517,10 @@ class VideoIntroController extends GetxController
onChoose(bool checkValue, int index) {
feedBack();
List<FavFolderItemData> datalist = favFolderData.value.list!;
for (var i = 0; i < datalist.length; i++) {
if (i == index) {
datalist[i].favState = checkValue == true ? 1 : 0;
datalist[i].mediaCount = checkValue == true
? datalist[i].mediaCount! + 1
: datalist[i].mediaCount! - 1;
}
}
datalist[index].favState = checkValue ? 1 : 0;
datalist[index].mediaCount = checkValue
? datalist[index].mediaCount! + 1
: datalist[index].mediaCount! - 1;
favFolderData.value.list = datalist;
favFolderData.refresh();
}

View File

@@ -1,6 +1,8 @@
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
import 'package:PiliPlus/common/widgets/icon_button.dart';
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
show SourceModel;
import 'package:PiliPlus/common/widgets/loading_widget.dart';
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
import 'package:PiliPlus/common/widgets/video_card_h_member_video.dart';
@@ -380,7 +382,7 @@ class _HorizontalMemberPageState extends State<HorizontalMemberPage> {
onTap: () {
widget.videoDetailController.onViewImage();
context.imageView(
imgList: [face],
imgList: [SourceModel(url: face)],
onDismissed: widget.videoDetailController.onDismissed,
);
},

View File

@@ -182,60 +182,56 @@ class _MediaListPanelState extends State<MediaListPanel> {
},
),
),
const SizedBox(width: 10),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.title as String,
textAlign: TextAlign.start,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: item.bvid == widget.getBvId()
? FontWeight.bold
: null,
color: item.bvid == widget.getBvId()
? Theme.of(context)
.colorScheme
.primary
: null,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.title as String,
textAlign: TextAlign.start,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: item.bvid == widget.getBvId()
? FontWeight.bold
: null,
color: item.bvid == widget.getBvId()
? Theme.of(context).colorScheme.primary
: null,
),
),
const Spacer(),
Text(
item.upper?.name as String,
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize,
color:
Theme.of(context).colorScheme.outline,
),
),
const SizedBox(height: 2),
Row(
children: [
statView(
context: context,
theme: 'gray',
view: item.cntInfo!['play'] as int,
),
),
const Spacer(),
Text(
item.upper?.name as String,
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize,
color:
Theme.of(context).colorScheme.outline,
const SizedBox(width: 8),
statDanMu(
context: context,
theme: 'gray',
danmu: item.cntInfo!['danmaku'] as int,
),
),
const SizedBox(height: 2),
Row(
children: [
statView(
context: context,
theme: 'gray',
view: item.cntInfo!['play'] as int,
),
const SizedBox(width: 8),
statDanMu(
context: context,
theme: 'gray',
danmu: item.cntInfo!['danmaku'] as int,
),
],
),
],
),
],
),
],
),
)
),
],
),
);

View File

@@ -1,10 +1,10 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/segment_progress_bar.dart';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/models/common/audio_normalization.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/utils.dart';
@@ -509,6 +509,12 @@ class PlPlayerController {
_subType = subType;
_enableHeart = enableHeart;
if (showSeekPreview) {
videoShot = null;
showPreview.value = false;
previewDx.value = 0;
}
if (_videoPlayerController != null &&
_videoPlayerController!.state.playing) {
await pause(notify: false);
@@ -1564,4 +1570,35 @@ class PlPlayerController {
videoPlayerController?.setVideoTrack(
_onlyPlayAudio.value ? VideoTrack.no() : VideoTrack.auto());
}
late final showSeekPreview = GStorage.showSeekPreview;
late bool _isQueryingVideoShot = false;
Map? videoShot;
late final RxBool showPreview = false.obs;
late final RxDouble previewDx = 0.0.obs;
void getVideoShot() async {
if (_isQueryingVideoShot) {
return;
}
_isQueryingVideoShot = true;
dynamic res = await Request().get(
'https://api.bilibili.com/x/player/videoshot',
queryParameters: {
// 'aid': IdUtils.bv2av(_bvid),
'bvid': _bvid,
'cid': _cid,
'index': 1,
},
);
if (res.data['code'] == 0) {
videoShot = {
'status': true,
'data': res.data['data'],
};
} else {
videoShot = {'status': false};
}
_isQueryingVideoShot = false;
}
}

View File

@@ -5,7 +5,9 @@ import 'package:PiliPlus/common/widgets/segment_progress_bar.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/common/super_resolution_type.dart';
import 'package:PiliPlus/pages/video/detail/introduction/controller.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/id_utils.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
@@ -686,6 +688,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
if (_gestureType == 'horizontal') {
// live模式下禁用
if (plPlayerController.videoType.value == 'live') return;
final int curSliderPosition =
plPlayerController.sliderPosition.value.inMilliseconds;
final double scale = 90000 / renderBox.size.width;
@@ -696,6 +699,16 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
pos.clamp(Duration.zero, plPlayerController.duration.value);
plPlayerController.onUpdatedSliderProgress(result);
plPlayerController.onChangedSliderStart();
if (plPlayerController.showSeekPreview) {
try {
plPlayerController.previewDx.value = result.inMilliseconds /
plPlayerController.duration.value.inMilliseconds *
context.size!.width;
if (plPlayerController.showPreview.value.not) {
plPlayerController.showPreview.value = true;
}
} catch (_) {}
}
} else if (_gestureType == 'left') {
// 左边区域 👈
final double level = renderBox.size.height * 3;
@@ -742,6 +755,9 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
}
},
onInteractionEnd: (ScaleEndDetails details) {
if (plPlayerController.showSeekPreview) {
plPlayerController.showPreview.value = false;
}
if (plPlayerController.isSliderMoving.value) {
plPlayerController.onChangedSliderEnd();
plPlayerController.seekTo(
@@ -1034,8 +1050,8 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
),
),
const Spacer(),
ClipRect(
child: AppBarAni(
if (plPlayerController.showControls.value)
AppBarAni(
controller: animationController,
visible: !plPlayerController.controlsLock.value &&
plPlayerController.showControls.value,
@@ -1046,7 +1062,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
buildBottomControl: buildBottomControl(),
),
),
),
],
),
),
@@ -1059,27 +1074,27 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
final int max = plPlayerController.durationSeconds.value;
final int buffer = plPlayerController.bufferedSeconds.value;
if (plPlayerController.showControls.value) {
return Container();
return const SizedBox.shrink();
}
if (defaultBtmProgressBehavior ==
BtmProgressBehavior.alwaysHide.code) {
return const SizedBox();
return const SizedBox.shrink();
}
if (defaultBtmProgressBehavior ==
BtmProgressBehavior.onlyShowFullScreen.code &&
!isFullScreen) {
return const SizedBox();
return const SizedBox.shrink();
} else if (defaultBtmProgressBehavior ==
BtmProgressBehavior.onlyHideFullScreen.code &&
isFullScreen) {
return const SizedBox();
return const SizedBox.shrink();
}
if (plPlayerController.videoType.value == 'live') {
return Container();
return const SizedBox.shrink();
}
if (value > max || max <= 0) {
return const SizedBox();
return const SizedBox.shrink();
}
return Positioned(
bottom: -1,
@@ -1090,6 +1105,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
value: '${(value / max * 100).round()}%',
// enabled: false,
child: Stack(
clipBehavior: Clip.none,
alignment: Alignment.bottomCenter,
children: [
if (plPlayerController.viewPointList.isNotEmpty &&
@@ -1137,35 +1153,32 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
thumbColor: colorTheme,
barHeight: 3.5,
thumbRadius: draggingFixedProgressBar.value ? 7 : 2.5,
// onDragStart: (duration) {
// draggingFixedProgressBar.value = true;
// feedBack();
// _.onChangedSliderStart();
// },
// onDragUpdate: (duration) {
// double newProgress = duration.timeStamp.inSeconds / max;
// if ((newProgress - _lastAnnouncedValue).abs() > 0.02) {
// _accessibilityDebounce?.cancel();
// _accessibilityDebounce =
// Timer(const Duration(milliseconds: 200), () {
// SemanticsService.announce(
// "${(newProgress * 100).round()}%",
// TextDirection.ltr);
// _lastAnnouncedValue = newProgress;
// });
// }
// _.onUpdatedSliderProgress(duration.timeStamp);
// },
// onSeek: (duration) {
// draggingFixedProgressBar.value = false;
// _.onChangedSliderEnd();
// _.onChangedSlider(duration.inSeconds.toDouble());
// _.seekTo(Duration(seconds: duration.inSeconds),
// type: 'slider');
// SemanticsService.announce(
// "${(duration.inSeconds / max * 100).round()}%",
// TextDirection.ltr);
// },
onDragStart: (duration) {
feedBack();
plPlayerController.onChangedSliderStart();
},
onDragUpdate: (duration) {
plPlayerController
.onUpdatedSliderProgress(duration.timeStamp);
if (plPlayerController.showSeekPreview) {
if (plPlayerController.showPreview.value.not) {
plPlayerController.showPreview.value = true;
}
plPlayerController.previewDx.value =
duration.localPosition.dx;
}
},
onSeek: (duration) {
if (plPlayerController.showSeekPreview) {
plPlayerController.showPreview.value = false;
}
plPlayerController.onChangedSliderEnd();
plPlayerController
.onChangedSlider(duration.inSeconds.toDouble());
plPlayerController.seekTo(
Duration(seconds: duration.inSeconds),
type: 'slider');
},
),
if (plPlayerController.segmentList.isNotEmpty)
Positioned(
@@ -1196,6 +1209,13 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
),
),
),
if (plPlayerController.showSeekPreview)
Positioned(
left: 0,
right: 0,
bottom: 12,
child: buildSeekPreviewWidget(plPlayerController),
),
],
),
// SlideTransition(
@@ -1371,7 +1391,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
]),
)));
} else {
return const SizedBox();
return const SizedBox.shrink();
}
}),
@@ -1421,7 +1441,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
},
),
)
: const SizedBox(),
: const SizedBox.shrink(),
),
const Spacer(),
// Expanded(
@@ -1467,7 +1487,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
},
),
)
: const SizedBox(),
: const SizedBox.shrink(),
),
],
),
@@ -1478,3 +1498,106 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
);
}
}
Widget buildSeekPreviewWidget(PlPlayerController plPlayerController) {
return Obx(() {
if (plPlayerController.showPreview.value.not) {
return SizedBox.shrink(
key: ValueKey(plPlayerController.previewDx.value),
);
}
if (plPlayerController.videoShot == null) {
plPlayerController.getVideoShot();
return SizedBox.shrink(
key: ValueKey(plPlayerController.previewDx.value),
);
} else if (plPlayerController.videoShot!['status'] == false) {
return SizedBox.shrink(
key: ValueKey(plPlayerController.previewDx.value),
);
}
return LayoutBuilder(
key: ValueKey(plPlayerController.previewDx.value),
builder: (context, constraints) {
try {
double scale = plPlayerController.isFullScreen.value &&
plPlayerController.direction.value == 'horizontal'
? 4
: 2.5;
// offset
double left = (plPlayerController.previewDx.value - 48 * scale / 2)
.clamp(8, constraints.maxWidth - 48 * scale - 8);
// index
// int index = plPlayerController.sliderPositionSeconds.value ~/ 5;
int index = max(
0,
(List<int>.from(plPlayerController.videoShot!['data']['index'])
.where((item) =>
item <=
plPlayerController.sliderPositionSeconds.value)
.length -
2));
// pageIndex
int pageIndex = (index ~/ 100).clamp(
0,
(plPlayerController.videoShot!['data']['image'] as List).length,
);
// alignment
double cal(m) {
return -1 + 2 / 9 * m;
}
int align = index % 100;
int x = align % 10;
int y = align ~/ 10;
double dx = cal(x);
double dy = cal(y);
Alignment alignment = Alignment(dx, dy);
// url
String parseUrl(String url) {
return url.startsWith('//') ? 'https:$url' : url;
}
return Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.only(left: left),
child: UnconstrainedBox(
child: ClipRRect(
// clipBehavior: Clip.antiAlias,
// decoration: BoxDecoration(
// border: Border.all(
// color: Colors.white,
// strokeAlign: BorderSide.strokeAlignOutside,
// ),
// borderRadius: BorderRadius.circular(scale == 2.5 ? 6 : 10),
// ),
borderRadius: BorderRadius.circular(scale == 2.5 ? 6 : 10),
child: Align(
widthFactor: 0.1,
heightFactor: 0.1,
alignment: alignment,
child: CachedNetworkImage(
fit: BoxFit.fill,
width: 480 * scale,
height: 270 * scale,
imageUrl: parseUrl(plPlayerController.videoShot!['data']
['image'][pageIndex]),
),
),
),
),
);
} catch (e) {
debugPrint('seek preview: $e');
return SizedBox.shrink(
key: ValueKey(plPlayerController.previewDx.value),
);
}
});
});
}

View File

@@ -1,11 +1,13 @@
import 'dart:async';
import 'package:PiliPlus/common/widgets/segment_progress_bar.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart';
import 'package:nil/nil.dart';
import 'package:PiliPlus/plugin/pl_player/index.dart';
import 'package:PiliPlus/plugin/pl_player/index.dart'
show PlPlayerController, buildSeekPreviewWidget;
import 'package:PiliPlus/utils/feed_back.dart';
import '../../../common/widgets/audio_video_progress_bar.dart';
@@ -28,9 +30,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
//阅读器限制
Timer? accessibilityDebounce;
double lastAnnouncedValue = -1;
return Container(
color: Colors.transparent,
height: 90,
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
@@ -50,6 +50,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
value: '${(value / max * 100).round()}%',
// enabled: false,
child: Stack(
clipBehavior: Clip.none,
alignment: Alignment.bottomCenter,
children: [
if (controller?.viewPointList.isNotEmpty == true &&
@@ -101,6 +102,13 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
onDragUpdate: (duration) {
double newProgress =
duration.timeStamp.inSeconds / max;
if (controller!.showSeekPreview) {
if (controller!.showPreview.value.not) {
controller!.showPreview.value = true;
}
controller!.previewDx.value =
duration.localPosition.dx;
}
if ((newProgress - lastAnnouncedValue).abs() > 0.02) {
accessibilityDebounce?.cancel();
accessibilityDebounce =
@@ -115,6 +123,9 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
.onUpdatedSliderProgress(duration.timeStamp);
},
onSeek: (duration) {
if (controller!.showSeekPreview) {
controller!.showPreview.value = false;
}
controller!.onChangedSliderEnd();
controller!
.onChangedSlider(duration.inSeconds.toDouble());
@@ -155,6 +166,13 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
),
),
),
if (controller?.showSeekPreview == true)
Positioned(
left: 0,
right: 0,
bottom: 18,
child: buildSeekPreviewWidget(controller!),
),
],
),
),

View File

@@ -6,6 +6,7 @@ import 'package:device_info_plus/device_info_plus.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:live_photo_maker/live_photo_maker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:saver_gallery/saver_gallery.dart';
@@ -86,28 +87,65 @@ class DownloadUtils {
return await requestStoragePer(context);
}
static Future downloadVideo(BuildContext context, String url) async {
static Future downloadLivePhoto({
required BuildContext context,
required String url,
required String liveUrl,
required int width,
required int height,
}) async {
try {
if (!await checkPermissionDependOnSdkInt(context)) {
return;
}
SmartDialog.showLoading(msg: '正在下载');
String tmpPath = (await getTemporaryDirectory()).path;
String time = DateTime.now()
.toString()
.replaceAll(' ', '_')
.replaceAll(':', '-')
.split('.')
.first;
late String imageName =
"cover_$time.${url.split('.').lastOrNull ?? 'jpg'}";
late String imagePath = '$tmpPath/$imageName';
String videoName =
"video_${DateTime.now().toString().replaceAll(' ', '_').replaceAll(':', '-').split('.').first}.${url.split('.').lastOrNull ?? 'mp4'}";
String savePath = '${(await getTemporaryDirectory()).path}/$videoName';
await Request.dio.download(url, savePath);
SmartDialog.showLoading(msg: '正在保存');
final SaveResult result = await SaverGallery.saveFile(
filePath: savePath,
fileName: videoName,
androidRelativePath: "Pictures/PiliPlus",
skipIfExists: false,
);
SmartDialog.dismiss();
if (result.isSuccess) {
SmartDialog.showToast('$videoName」已保存 ');
"video_$time.${liveUrl.split('.').lastOrNull ?? 'mp4'}";
String videoPath = '$tmpPath/$videoName';
await Request.dio.download(liveUrl, videoPath);
if (Platform.isIOS) {
await Request.dio.download(url, imagePath);
SmartDialog.showLoading(msg: '正在保存');
bool success = await LivePhotoMaker.create(
coverImage: imagePath,
imagePath: null,
voicePath: videoPath,
width: width,
height: height,
);
SmartDialog.dismiss();
if (success) {
SmartDialog.showToast(' Live Photo 已保存 ');
} else {
SmartDialog.showToast('保存失败');
}
} else {
SmartDialog.showToast('保存失败,${result.errorMessage}');
SmartDialog.showLoading(msg: '正在保存');
final SaveResult result = await SaverGallery.saveFile(
filePath: videoPath,
fileName: videoName,
androidRelativePath: "Pictures/PiliPlus",
skipIfExists: false,
);
SmartDialog.dismiss();
if (result.isSuccess) {
SmartDialog.showToast(' 已保存 ');
} else {
SmartDialog.showToast('保存失败,${result.errorMessage}');
}
}
return true;

View File

@@ -357,6 +357,9 @@ class GStorage {
static bool get enableLivePhoto =>
GStorage.setting.get(SettingBoxKey.enableLivePhoto, defaultValue: true);
static bool get showSeekPreview =>
GStorage.setting.get(SettingBoxKey.showSeekPreview, defaultValue: true);
static List<double> get dynamicDetailRatio => List<double>.from(setting
.get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0]));
@@ -585,6 +588,7 @@ class SettingBoxKey {
searchSuggestion = 'searchSuggestion',
showDynDecorate = 'showDynDecorate',
enableLivePhoto = 'enableLivePhoto',
showSeekPreview = 'showSeekPreview',
// Sponsor Block
enableSponsorBlock = 'enableSponsorBlock',

View File

@@ -339,14 +339,20 @@ class Utils {
}
}
static void onHorizontalPreview(GlobalKey<ScaffoldState> key,
transitionAnimationController, ctr, imgList, index, onClose) {
static void onHorizontalPreview(
GlobalKey<ScaffoldState> key,
transitionAnimationController,
ctr,
List<String> imgList,
index,
onClose,
) {
key.currentState?.showBottomSheet(
(context) {
return FadeTransition(
opacity: Tween<double>(begin: 0, end: 1).animate(ctr),
child: InteractiveviewerGallery(
sources: imgList,
sources: imgList.map((url) => SourceModel(url: url)).toList(),
initIndex: index,
setStatusBar: false,
onClose: onClose,

View File

@@ -1079,6 +1079,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.2"
live_photo_maker:
dependency: "direct main"
description:
name: live_photo_maker
sha256: "9af3965bd9d2ab55b0d4d0a1e4041fdcc9ef6c6c44543c8412667541a054f58b"
url: "https://pub.dev"
source: hosted
version: "0.0.6"
logger:
dependency: "direct main"
description:

View File

@@ -180,6 +180,7 @@ dependencies:
brotli: ^0.6.0
expandable: ^5.0.1
flex_seed_scheme: ^3.4.1
live_photo_maker: ^0.0.6
dependency_overrides:
screen_brightness: ^2.0.1