fix:增加视频日志中的视频缩略图
This commit is contained in:
parent
4f6781a0b0
commit
de6d7bca58
@ -2,5 +2,5 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
|
#distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
|
||||||
|
distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-7.4-all.zip
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import 'package:star_lock/main/lockDetail/doorLockLog/doorLockLog_state.dart';
|
|||||||
import 'package:star_lock/main/lockDetail/doorLockLog/exportRecordDialog/exportRecordDialog_page.dart';
|
import 'package:star_lock/main/lockDetail/doorLockLog/exportRecordDialog/exportRecordDialog_page.dart';
|
||||||
import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart';
|
import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart';
|
||||||
import 'package:star_lock/main/lockDetail/videoLog/widget/full_screenImage_page.dart';
|
import 'package:star_lock/main/lockDetail/videoLog/widget/full_screenImage_page.dart';
|
||||||
import 'package:star_lock/main/lockDetail/videoLog/widget/video_thumbnail.dart';
|
import 'package:star_lock/main/lockDetail/videoLog/widget/video_thumbnail_image.dart';
|
||||||
import 'package:star_lock/tools/EasyRefreshTool.dart';
|
import 'package:star_lock/tools/EasyRefreshTool.dart';
|
||||||
import 'package:star_lock/tools/advancedCalendar/src/widget.dart';
|
import 'package:star_lock/tools/advancedCalendar/src/widget.dart';
|
||||||
import 'package:star_lock/tools/commonDataManage.dart';
|
import 'package:star_lock/tools/commonDataManage.dart';
|
||||||
@ -399,7 +399,7 @@ class _DoorLockLogPageState extends State<DoorLockLogPage> with RouteAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_buildVideoItem(RecordListData recordData) {
|
_buildVideoItem(RecordListData recordData) {
|
||||||
return VideoThumbnail(videoUrl: recordData.videoUrl!);
|
return VideoThumbnailImage(videoUrl: recordData.videoUrl!);
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildImageItem(RecordListData recordData) {
|
_buildImageItem(RecordListData recordData) {
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import 'package:star_lock/appRouters.dart';
|
|||||||
import 'package:star_lock/main/lockDetail/videoLog/editVideoLog/editVideoLog_state.dart';
|
import 'package:star_lock/main/lockDetail/videoLog/editVideoLog/editVideoLog_state.dart';
|
||||||
import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart';
|
import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart';
|
||||||
import 'package:star_lock/main/lockDetail/videoLog/widget/full_screenImage_page.dart';
|
import 'package:star_lock/main/lockDetail/videoLog/widget/full_screenImage_page.dart';
|
||||||
import 'package:star_lock/main/lockDetail/videoLog/widget/video_thumbnail.dart';
|
import 'package:star_lock/main/lockDetail/videoLog/widget/video_thumbnail_image.dart';
|
||||||
import 'package:star_lock/tools/dateTool.dart';
|
import 'package:star_lock/tools/dateTool.dart';
|
||||||
|
|
||||||
import '../../../../app_settings/app_colors.dart';
|
import '../../../../app_settings/app_colors.dart';
|
||||||
@ -241,61 +241,8 @@ class _EditVideoLogPageState extends State<EditVideoLogPage> {
|
|||||||
|
|
||||||
Future<void> _onDelClick() async {
|
Future<void> _onDelClick() async {
|
||||||
if (state.selectVideoLogList.value.isNotEmpty) {
|
if (state.selectVideoLogList.value.isNotEmpty) {
|
||||||
// 弹出自定义确认对话框
|
await logic.deleteLockCloudStorageList();
|
||||||
bool confirmDelete = await showDialog(
|
setState(() {});
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return Dialog(
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'确认删除'.tr,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 16),
|
|
||||||
Text('确定要删除选中的视频吗?'.tr),
|
|
||||||
SizedBox(height: 24),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
// 用户点击取消,返回 false
|
|
||||||
Navigator.of(context).pop(false);
|
|
||||||
},
|
|
||||||
child: Text('取消'.tr),
|
|
||||||
),
|
|
||||||
SizedBox(width: 8),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
// 用户点击确认,返回 true
|
|
||||||
Navigator.of(context).pop(true);
|
|
||||||
},
|
|
||||||
child: Text('确认'.tr),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// 如果用户确认删除,执行删除逻辑
|
|
||||||
if (confirmDelete == true) {
|
|
||||||
await logic.deleteLockCloudStorageList();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
logic.showToast('请选择要删除的视频'.tr);
|
logic.showToast('请选择要删除的视频'.tr);
|
||||||
}
|
}
|
||||||
@ -412,7 +359,7 @@ class _EditVideoLogPageState extends State<EditVideoLogPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_buildVideoItem(RecordListData recordData) {
|
_buildVideoItem(RecordListData recordData) {
|
||||||
return VideoThumbnail(videoUrl: recordData.videoUrl!);
|
return VideoThumbnailImage(videoUrl: recordData.videoUrl!);
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildImageItem(RecordListData recordData) {
|
_buildImageItem(RecordListData recordData) {
|
||||||
|
|||||||
@ -22,38 +22,12 @@ class VideoLogLogic extends BaseGetXController {
|
|||||||
(record.imagesUrl != null && record.imagesUrl!.isNotEmpty) ||
|
(record.imagesUrl != null && record.imagesUrl!.isNotEmpty) ||
|
||||||
(record.videoUrl != null && record.videoUrl!.isNotEmpty))
|
(record.videoUrl != null && record.videoUrl!.isNotEmpty))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
// // 为 videoUrl 不为空的项设置封面
|
|
||||||
// for (var record in element.recordList!) {
|
|
||||||
// if (record.videoUrl != null && record.videoUrl!.isNotEmpty) {
|
|
||||||
// _setVideoThumbnail(record); // 设置视频封面
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
});
|
});
|
||||||
|
|
||||||
state.videoLogList.refresh();
|
state.videoLogList.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Future<void> _setVideoThumbnail(RecordListData record) async {
|
|
||||||
// try {
|
|
||||||
// final thumbnailData = await getVideoThumbnail(record.videoUrl!);
|
|
||||||
// record.thumbnailData = thumbnailData; // 设置视频封面数据
|
|
||||||
// } catch (e) {
|
|
||||||
// print('获取视频封面失败: $e');
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Future<Uint8List?> getVideoThumbnail(String videoUrl) async {
|
|
||||||
// final thumbnail = await VideoThumbnail.thumbnailData(
|
|
||||||
// video: videoUrl,
|
|
||||||
// imageFormat: ImageFormat.JPEG,
|
|
||||||
// maxWidth: 128, // 缩略图的最大宽度
|
|
||||||
// quality: 25, // 缩略图的质量(0-100)
|
|
||||||
// );
|
|
||||||
// return thumbnail;
|
|
||||||
// }
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
onReady() {
|
onReady() {
|
||||||
super.onReady();
|
super.onReady();
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import 'package:star_lock/flavors.dart';
|
|||||||
import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart';
|
import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart';
|
||||||
import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_state.dart';
|
import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_state.dart';
|
||||||
import 'package:star_lock/main/lockDetail/videoLog/widget/full_screenImage_page.dart';
|
import 'package:star_lock/main/lockDetail/videoLog/widget/full_screenImage_page.dart';
|
||||||
import 'package:star_lock/main/lockDetail/videoLog/widget/video_thumbnail.dart';
|
import 'package:star_lock/main/lockDetail/videoLog/widget/video_thumbnail_image.dart';
|
||||||
import 'package:star_lock/tools/dateTool.dart';
|
import 'package:star_lock/tools/dateTool.dart';
|
||||||
import 'package:star_lock/tools/noData.dart';
|
import 'package:star_lock/tools/noData.dart';
|
||||||
import 'package:video_player/video_player.dart';
|
import 'package:video_player/video_player.dart';
|
||||||
@ -341,7 +341,7 @@ class _VideoLogPageState extends State<VideoLogPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_buildVideoItem(RecordListData recordData) {
|
_buildVideoItem(RecordListData recordData) {
|
||||||
return VideoThumbnail(videoUrl: recordData.videoUrl!);
|
return VideoThumbnailImage(videoUrl: recordData.videoUrl!);
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildImageItem(RecordListData recordData) {
|
_buildImageItem(RecordListData recordData) {
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart
|
|||||||
import 'package:star_lock/main/lockDetail/videoLog/videoLogDetail/controlsOverlay_page.dart';
|
import 'package:star_lock/main/lockDetail/videoLog/videoLogDetail/controlsOverlay_page.dart';
|
||||||
import 'package:star_lock/main/lockDetail/videoLog/videoLogDetail/videoLogDetail_state.dart';
|
import 'package:star_lock/main/lockDetail/videoLog/videoLogDetail/videoLogDetail_state.dart';
|
||||||
import 'package:star_lock/main/lockDetail/videoLog/widget/full_screenImage_page.dart';
|
import 'package:star_lock/main/lockDetail/videoLog/widget/full_screenImage_page.dart';
|
||||||
import 'package:star_lock/main/lockDetail/videoLog/widget/video_thumbnail.dart';
|
import 'package:star_lock/main/lockDetail/videoLog/widget/video_thumbnail_image.dart';
|
||||||
import 'package:star_lock/tools/dateTool.dart';
|
import 'package:star_lock/tools/dateTool.dart';
|
||||||
import 'package:video_player/video_player.dart';
|
import 'package:video_player/video_player.dart';
|
||||||
|
|
||||||
@ -165,7 +165,7 @@ class _VideoLogDetailPageState extends State<VideoLogDetailPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_buildVideoItem(RecordListData recordData) {
|
_buildVideoItem(RecordListData recordData) {
|
||||||
return VideoThumbnail(videoUrl: recordData.videoUrl!);
|
return VideoThumbnailImage(videoUrl: recordData.videoUrl!);
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildImageItem(RecordListData recordData) {
|
_buildImageItem(RecordListData recordData) {
|
||||||
|
|||||||
@ -1,76 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:video_player/video_player.dart';
|
|
||||||
|
|
||||||
class VideoThumbnail extends StatefulWidget {
|
|
||||||
final String videoUrl;
|
|
||||||
|
|
||||||
VideoThumbnail({required this.videoUrl});
|
|
||||||
|
|
||||||
@override
|
|
||||||
_VideoThumbnailState createState() => _VideoThumbnailState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _VideoThumbnailState extends State<VideoThumbnail> {
|
|
||||||
late VideoPlayerController _controller;
|
|
||||||
late Future<void> _initializeVideoPlayerFuture;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
// 创建并初始化 VideoPlayerController
|
|
||||||
_controller = VideoPlayerController.network(widget.videoUrl)
|
|
||||||
..initialize().then((_) {
|
|
||||||
// 当视频控制器初始化完成后,更新UI以便显示第一帧
|
|
||||||
setState(() {});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
// 清理资源
|
|
||||||
_controller.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return GestureDetector(
|
|
||||||
// onTap: () {
|
|
||||||
// setState(() {
|
|
||||||
// if (_controller.value.isPlaying) {
|
|
||||||
// _controller.pause();
|
|
||||||
// } else {
|
|
||||||
// _controller.play();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
child: Stack(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
// 如果视频已经初始化,则显示视频玩家
|
|
||||||
_controller.value.isInitialized
|
|
||||||
? RotatedBox(
|
|
||||||
quarterTurns: -1,
|
|
||||||
child: AspectRatio(
|
|
||||||
aspectRatio: 1 / 1, // 强制正方形比例
|
|
||||||
child: FittedBox(
|
|
||||||
fit: BoxFit.cover, // 确保视频封面铺满空间
|
|
||||||
child: SizedBox(
|
|
||||||
width: _controller.value.size.width,
|
|
||||||
height: _controller.value.size.height,
|
|
||||||
child: VideoPlayer(_controller),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Center(
|
|
||||||
child: CircularProgressIndicator(), // 加载中显示转圈
|
|
||||||
),
|
|
||||||
if (!_controller.value.isPlaying && _controller.value.isInitialized)
|
|
||||||
Icon(Icons.play_arrow_rounded,
|
|
||||||
size: 80, color: Colors.white.withOpacity(0.8)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,97 @@
|
|||||||
|
import 'dart:io'; // 导入 dart:io 以使用 File 类
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart'; // 导入 path_provider
|
||||||
|
import 'package:video_thumbnail/video_thumbnail.dart'; // 导入 video_thumbnail
|
||||||
|
|
||||||
|
class VideoThumbnailImage extends StatefulWidget {
|
||||||
|
final String videoUrl;
|
||||||
|
|
||||||
|
VideoThumbnailImage({required this.videoUrl});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_VideoThumbnailState createState() => _VideoThumbnailState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _VideoThumbnailState extends State<VideoThumbnailImage> {
|
||||||
|
final Map<String, String> _thumbnailCache = {}; // 缩略图缓存
|
||||||
|
late Future<String?> _thumbnailFuture; // 用于存储缩略图生成的 Future
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_thumbnailFuture = _generateThumbnail(); // 在 initState 中初始化 Future
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成缩略图
|
||||||
|
Future<String?> _generateThumbnail() async {
|
||||||
|
try {
|
||||||
|
// 检查缓存中是否已有缩略图
|
||||||
|
if (_thumbnailCache.containsKey(widget.videoUrl)) {
|
||||||
|
return _thumbnailCache[widget.videoUrl];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取临时目录路径
|
||||||
|
final tempDir = await getTemporaryDirectory();
|
||||||
|
final thumbnailPath = await VideoThumbnail.thumbnailFile(
|
||||||
|
video: widget.videoUrl,
|
||||||
|
// 视频 URL
|
||||||
|
thumbnailPath: tempDir.path,
|
||||||
|
// 缩略图保存路径
|
||||||
|
imageFormat: ImageFormat.JPEG,
|
||||||
|
// 缩略图格式
|
||||||
|
maxHeight: 200,
|
||||||
|
// 缩略图最大高度
|
||||||
|
quality: 100, // 缩略图质量 (0-100)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 更新缓存
|
||||||
|
_thumbnailCache[widget.videoUrl] = thumbnailPath!;
|
||||||
|
return thumbnailPath;
|
||||||
|
} catch (e) {
|
||||||
|
print('Failed to generate thumbnail: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FutureBuilder<String?>(
|
||||||
|
future: _thumbnailFuture, // 生成缩略图的 Future
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
|
// 加载中显示转圈
|
||||||
|
return Center(child: CircularProgressIndicator());
|
||||||
|
} else if (snapshot.hasError || !snapshot.hasData) {
|
||||||
|
// 加载失败或没有数据时显示提示
|
||||||
|
return Image.asset(
|
||||||
|
'images/icon_unHaveData.png', // 错误图片路径
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// 加载成功,显示缩略图
|
||||||
|
final thumbnailPath = snapshot.data!;
|
||||||
|
return Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
RotatedBox(
|
||||||
|
quarterTurns: -1,
|
||||||
|
child: Image.file(
|
||||||
|
File(thumbnailPath), // 显示生成的缩略图
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
Icons.play_arrow_rounded,
|
||||||
|
size: 80,
|
||||||
|
color: Colors.white.withOpacity(0.8),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user