feat: 长按保存封面

This commit is contained in:
guozhigq
2024-05-01 19:46:27 +08:00
parent e5c71bef64
commit 1b25d821f3
21 changed files with 430 additions and 515 deletions

View File

@ -5,7 +5,9 @@ import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/badge.dart';
import 'package:pilipala/http/search.dart';
import 'package:pilipala/models/bangumi/info.dart';
import 'package:pilipala/models/bangumi/list.dart';
import 'package:pilipala/models/common/search_type.dart';
import 'package:pilipala/utils/image_save.dart';
import 'package:pilipala/utils/utils.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
@ -14,109 +16,87 @@ class BangumiCardV extends StatelessWidget {
const BangumiCardV({
super.key,
required this.bangumiItem,
this.longPress,
this.longPressEnd,
});
final bangumiItem;
final Function()? longPress;
final Function()? longPressEnd;
final BangumiListItemModel bangumiItem;
@override
Widget build(BuildContext context) {
String heroTag = Utils.makeHeroTag(bangumiItem.mediaId);
return Card(
elevation: 0,
clipBehavior: Clip.hardEdge,
margin: EdgeInsets.zero,
child: GestureDetector(
// onLongPress: () {
// if (longPress != null) {
// longPress!();
// }
// },
// onLongPressEnd: (details) {
// if (longPressEnd != null) {
// longPressEnd!();
// }
// },
child: InkWell(
onTap: () async {
final int seasonId = bangumiItem.seasonId;
SmartDialog.showLoading(msg: '获取中...');
final res = await SearchHttp.bangumiInfo(seasonId: seasonId);
SmartDialog.dismiss().then((value) {
if (res['status']) {
if (res['data'].episodes.isEmpty) {
SmartDialog.showToast('资源加载失败');
return;
}
EpisodeItem episode = res['data'].episodes.first;
String bvid = episode.bvid!;
int cid = episode.cid!;
String pic = episode.cover!;
String heroTag = Utils.makeHeroTag(cid);
Get.toNamed(
'/video?bvid=$bvid&cid=$cid&seasonId=$seasonId',
arguments: {
'pic': pic,
'heroTag': heroTag,
'videoType': SearchType.media_bangumi,
'bangumiItem': res['data'],
},
return InkWell(
onTap: () async {
final int seasonId = bangumiItem.seasonId!;
SmartDialog.showLoading(msg: '获取中...');
final res = await SearchHttp.bangumiInfo(seasonId: seasonId);
SmartDialog.dismiss().then((value) {
if (res['status']) {
if (res['data'].episodes.isEmpty) {
SmartDialog.showToast('资源加载失败');
return;
}
EpisodeItem episode = res['data'].episodes.first;
String bvid = episode.bvid!;
int cid = episode.cid!;
String pic = episode.cover!;
String heroTag = Utils.makeHeroTag(cid);
Get.toNamed(
'/video?bvid=$bvid&cid=$cid&seasonId=$seasonId',
arguments: {
'pic': pic,
'heroTag': heroTag,
'videoType': SearchType.media_bangumi,
'bangumiItem': res['data'],
},
);
}
});
},
onLongPress: () =>
imageSaveDialog(context, bangumiItem, SmartDialog.dismiss),
child: Column(
children: [
ClipRRect(
borderRadius: const BorderRadius.all(
StyleString.imgRadius,
),
child: AspectRatio(
aspectRatio: 0.65,
child: LayoutBuilder(builder: (context, boxConstraints) {
final double maxWidth = boxConstraints.maxWidth;
final double maxHeight = boxConstraints.maxHeight;
return Stack(
children: [
Hero(
tag: heroTag,
child: NetworkImgLayer(
src: bangumiItem.cover,
width: maxWidth,
height: maxHeight,
),
),
if (bangumiItem.badge != null)
PBadge(
text: bangumiItem.badge,
top: 6,
right: 6,
bottom: null,
left: null),
if (bangumiItem.order != null)
PBadge(
text: bangumiItem.order,
top: null,
right: null,
bottom: 6,
left: 6,
type: 'gray',
),
],
);
}
});
},
child: Column(
children: [
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: StyleString.imgRadius,
topRight: StyleString.imgRadius,
bottomLeft: StyleString.imgRadius,
bottomRight: StyleString.imgRadius,
),
child: AspectRatio(
aspectRatio: 0.65,
child: LayoutBuilder(builder: (context, boxConstraints) {
final double maxWidth = boxConstraints.maxWidth;
final double maxHeight = boxConstraints.maxHeight;
return Stack(
children: [
Hero(
tag: heroTag,
child: NetworkImgLayer(
src: bangumiItem.cover,
width: maxWidth,
height: maxHeight,
),
),
if (bangumiItem.badge != null)
PBadge(
text: bangumiItem.badge,
top: 6,
right: 6,
bottom: null,
left: null),
if (bangumiItem.order != null)
PBadge(
text: bangumiItem.order,
top: null,
right: null,
bottom: 6,
left: 6,
type: 'gray',
),
],
);
}),
),
),
BangumiContent(bangumiItem: bangumiItem)
],
}),
),
),
),
BangumiContent(bangumiItem: bangumiItem)
],
),
);
}

View File

@ -1,3 +1,4 @@
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:flutter/material.dart';
import 'package:pilipala/common/constants.dart';
@ -7,6 +8,7 @@ import 'package:pilipala/http/search.dart';
import 'package:pilipala/http/video.dart';
import 'package:pilipala/models/common/search_type.dart';
import 'package:pilipala/utils/id_utils.dart';
import 'package:pilipala/utils/image_save.dart';
import 'package:pilipala/utils/utils.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import '../../../common/widgets/badge.dart';
@ -61,6 +63,11 @@ class FavVideoCardH extends StatelessWidget {
epId != null ? SearchType.media_bangumi : SearchType.video,
});
},
onLongPress: () => imageSaveDialog(
context,
videoItem,
SmartDialog.dismiss,
),
child: Column(
children: [
Padding(

View File

@ -3,8 +3,6 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/animated_dialog.dart';
import 'package:pilipala/common/widgets/overlay_pop.dart';
import 'package:pilipala/common/skeleton/video_card_h.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/common/widgets/video_card_h.dart';
@ -78,15 +76,6 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
return VideoCardH(
videoItem: _hotController.videoList[index],
showPubdate: true,
longPress: () {
_hotController.popupDialog = _createPopupDialog(
_hotController.videoList[index]);
Overlay.of(context)
.insert(_hotController.popupDialog!);
},
longPressEnd: () {
_hotController.popupDialog?.remove();
},
);
}, childCount: _hotController.videoList.length),
),
@ -122,14 +111,4 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
),
);
}
OverlayEntry _createPopupDialog(videoItem) {
return OverlayEntry(
builder: (context) => AnimatedDialog(
closeFn: _hotController.popupDialog?.remove,
child: OverlayPop(
videoItem: videoItem, closeFn: _hotController.popupDialog?.remove),
),
);
}
}

View File

@ -84,7 +84,7 @@ class _LaterPageState extends State<LaterPage> {
return VideoCardH(
videoItem: videoItem,
source: 'later',
longPress: () => _laterController.toViewDel(
onPressedFn: () => _laterController.toViewDel(
aid: videoItem.aid));
}, childCount: _laterController.laterList.length),
)

View File

@ -5,9 +5,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/skeleton/video_card_v.dart';
import 'package:pilipala/common/widgets/animated_dialog.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/common/widgets/overlay_pop.dart';
import 'package:pilipala/utils/main_stream.dart';
import 'controller.dart';
@ -112,16 +110,6 @@ class _LivePageState extends State<LivePage>
);
}
OverlayEntry _createPopupDialog(liveItem) {
return OverlayEntry(
builder: (context) => AnimatedDialog(
closeFn: _liveController.popupDialog?.remove,
child: OverlayPop(
videoItem: liveItem, closeFn: _liveController.popupDialog?.remove),
),
);
}
Widget contentGrid(ctr, liveList) {
// double maxWidth = Get.size.width;
// int baseWidth = 500;
@ -152,14 +140,6 @@ class _LivePageState extends State<LivePage>
? LiveCardV(
liveItem: liveList[index],
crossAxisCount: crossAxisCount,
longPress: () {
_liveController.popupDialog =
_createPopupDialog(liveList[index]);
Overlay.of(context).insert(_liveController.popupDialog!);
},
longPressEnd: () {
_liveController.popupDialog?.remove();
},
)
: const VideoCardVSkeleton();
},

View File

@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/models/live/item.dart';
import 'package:pilipala/utils/image_save.dart';
import 'package:pilipala/utils/utils.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
@ -9,81 +11,66 @@ import 'package:pilipala/common/widgets/network_img_layer.dart';
class LiveCardV extends StatelessWidget {
final LiveItemModel liveItem;
final int crossAxisCount;
final Function()? longPress;
final Function()? longPressEnd;
const LiveCardV({
Key? key,
required this.liveItem,
required this.crossAxisCount,
this.longPress,
this.longPressEnd,
}) : super(key: key);
@override
Widget build(BuildContext context) {
String heroTag = Utils.makeHeroTag(liveItem.roomId);
return Card(
elevation: 0,
clipBehavior: Clip.hardEdge,
margin: EdgeInsets.zero,
child: GestureDetector(
onLongPress: () {
if (longPress != null) {
longPress!();
}
},
// onLongPressEnd: (details) {
// if (longPressEnd != null) {
// longPressEnd!();
// }
// },
child: InkWell(
onTap: () async {
Get.toNamed('/liveRoom?roomid=${liveItem.roomId}',
arguments: {'liveItem': liveItem, 'heroTag': heroTag});
},
child: Column(
children: [
ClipRRect(
borderRadius: const BorderRadius.all(StyleString.imgRadius),
child: AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(builder: (context, boxConstraints) {
double maxWidth = boxConstraints.maxWidth;
double maxHeight = boxConstraints.maxHeight;
return Stack(
children: [
Hero(
tag: heroTag,
child: NetworkImgLayer(
src: liveItem.cover!,
width: maxWidth,
height: maxHeight,
return InkWell(
onLongPress: () => imageSaveDialog(
context,
liveItem,
SmartDialog.dismiss,
),
borderRadius: BorderRadius.circular(16),
onTap: () async {
Get.toNamed('/liveRoom?roomid=${liveItem.roomId}',
arguments: {'liveItem': liveItem, 'heroTag': heroTag});
},
child: Column(
children: [
ClipRRect(
borderRadius: const BorderRadius.all(StyleString.imgRadius),
child: AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(builder: (context, boxConstraints) {
double maxWidth = boxConstraints.maxWidth;
double maxHeight = boxConstraints.maxHeight;
return Stack(
children: [
Hero(
tag: heroTag,
child: NetworkImgLayer(
src: liveItem.cover!,
width: maxWidth,
height: maxHeight,
),
),
if (crossAxisCount != 1)
Positioned(
left: 0,
right: 0,
bottom: 0,
child: AnimatedOpacity(
opacity: 1,
duration: const Duration(milliseconds: 200),
child: VideoStat(
liveItem: liveItem,
),
),
if (crossAxisCount != 1)
Positioned(
left: 0,
right: 0,
bottom: 0,
child: AnimatedOpacity(
opacity: 1,
duration: const Duration(milliseconds: 200),
child: VideoStat(
liveItem: liveItem,
),
),
),
],
);
}),
),
),
LiveContent(liveItem: liveItem, crossAxisCount: crossAxisCount)
],
),
],
);
}),
),
),
),
LiveContent(liveItem: liveItem, crossAxisCount: crossAxisCount)
],
),
);
}

View File

@ -1,10 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/badge.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/common/widgets/stat/view.dart';
import 'package:pilipala/http/search.dart';
import 'package:pilipala/utils/image_save.dart';
import 'package:pilipala/utils/utils.dart';
class MemberSeasonsItem extends StatelessWidget {
@ -29,6 +31,11 @@ class MemberSeasonsItem extends StatelessWidget {
Get.toNamed('/video?bvid=${seasonItem.bvid}&cid=$cid',
arguments: {'videoItem': seasonItem, 'heroTag': heroTag});
},
onLongPress: () => imageSaveDialog(
context,
seasonItem,
SmartDialog.dismiss,
),
child: Column(
children: [
AspectRatio(

View File

@ -3,8 +3,6 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/animated_dialog.dart';
import 'package:pilipala/common/widgets/overlay_pop.dart';
import 'package:pilipala/common/skeleton/video_card_h.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/common/widgets/video_card_h.dart';
@ -82,15 +80,6 @@ class _ZonePageState extends State<ZonePage>
return VideoCardH(
videoItem: _zoneController.videoList[index],
showPubdate: true,
longPress: () {
_zoneController.popupDialog = _createPopupDialog(
_zoneController.videoList[index]);
Overlay.of(context)
.insert(_zoneController.popupDialog!);
},
longPressEnd: () {
_zoneController.popupDialog?.remove();
},
);
}, childCount: _zoneController.videoList.length),
),
@ -126,14 +115,4 @@ class _ZonePageState extends State<ZonePage>
),
);
}
OverlayEntry _createPopupDialog(videoItem) {
return OverlayEntry(
builder: (context) => AnimatedDialog(
closeFn: _zoneController.popupDialog?.remove,
child: OverlayPop(
videoItem: videoItem, closeFn: _zoneController.popupDialog?.remove),
),
);
}
}

View File

@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/utils/image_save.dart';
import 'package:pilipala/utils/utils.dart';
Widget searchLivePanel(BuildContext context, ctr, list) {
@ -42,15 +44,15 @@ class LiveItem extends StatelessWidget {
Get.toNamed('/liveRoom?roomid=${liveItem.roomid}',
arguments: {'liveItem': liveItem, 'heroTag': heroTag});
},
onLongPress: () => imageSaveDialog(
context,
liveItem,
SmartDialog.dismiss,
),
child: Column(
children: [
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: StyleString.imgRadius,
topRight: StyleString.imgRadius,
bottomLeft: StyleString.imgRadius,
bottomRight: StyleString.imgRadius,
),
borderRadius: const BorderRadius.all(StyleString.imgRadius),
child: AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(builder: (context, boxConstraints) {
@ -108,7 +110,7 @@ class LiveContent extends StatelessWidget {
RichText(
text: TextSpan(
children: [
for (var i in liveItem.title) ...[
for (var i in liveItem.titleList) ...[
TextSpan(
text: i['text'],
style: TextStyle(

View File

@ -63,7 +63,7 @@ Widget searchMbangumiPanel(BuildContext context, ctr, list) {
style: TextStyle(
color: Theme.of(context).colorScheme.onSurface),
children: [
for (var i in i.title) ...[
for (var i in i.titleList) ...[
TextSpan(
text: i['text'],
style: TextStyle(

View File

@ -35,7 +35,11 @@ class SearchVideoPanel extends StatelessWidget {
padding: index == 0
? const EdgeInsets.only(top: 2)
: EdgeInsets.zero,
child: VideoCardH(videoItem: i, showPubdate: true),
child: VideoCardH(
videoItem: i,
showPubdate: true,
source: 'search',
),
);
},
),

View File

@ -1,3 +1,4 @@
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:flutter/material.dart';
import 'package:pilipala/common/constants.dart';
@ -5,6 +6,7 @@ import 'package:pilipala/common/widgets/stat/danmu.dart';
import 'package:pilipala/common/widgets/stat/view.dart';
import 'package:pilipala/http/search.dart';
import 'package:pilipala/models/common/search_type.dart';
import 'package:pilipala/utils/image_save.dart';
import 'package:pilipala/utils/utils.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import '../../../common/widgets/badge.dart';
@ -40,6 +42,11 @@ class SubVideoCardH extends StatelessWidget {
'videoType': SearchType.video,
});
},
onLongPress: () => imageSaveDialog(
context,
videoItem,
SmartDialog.dismiss,
),
child: Column(
children: [
Padding(

View File

@ -1,9 +1,7 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/video_card_h.dart';
import 'package:pilipala/common/widgets/animated_dialog.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/common/widgets/overlay_pop.dart';
import 'package:pilipala/common/widgets/video_card_h.dart';
import './controller.dart';
@ -54,20 +52,6 @@ class _RelatedVideoPanelState extends State<RelatedVideoPanel>
child: VideoCardH(
videoItem: relatedVideoList[index],
showPubdate: true,
longPress: () {
try {
_releatedController.popupDialog =
_createPopupDialog(_releatedController
.relatedVideoList[index]);
Overlay.of(context)
.insert(_releatedController.popupDialog!);
} catch (err) {
return {};
}
},
longPressEnd: () {
_releatedController.popupDialog?.remove();
},
),
);
}
@ -89,15 +73,4 @@ class _RelatedVideoPanelState extends State<RelatedVideoPanel>
},
);
}
OverlayEntry _createPopupDialog(videoItem) {
return OverlayEntry(
builder: (BuildContext context) => AnimatedDialog(
closeFn: _releatedController.popupDialog?.remove,
child: OverlayPop(
videoItem: videoItem,
closeFn: _releatedController.popupDialog?.remove),
),
);
}
}