fix:增加视频日志中的视频缩略图
This commit is contained in:
parent
4f6781a0b0
commit
de6d7bca58
@ -2,5 +2,5 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
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/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/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/advancedCalendar/src/widget.dart';
|
||||
import 'package:star_lock/tools/commonDataManage.dart';
|
||||
@ -399,7 +399,7 @@ class _DoorLockLogPageState extends State<DoorLockLogPage> with RouteAware {
|
||||
}
|
||||
|
||||
_buildVideoItem(RecordListData recordData) {
|
||||
return VideoThumbnail(videoUrl: recordData.videoUrl!);
|
||||
return VideoThumbnailImage(videoUrl: recordData.videoUrl!);
|
||||
}
|
||||
|
||||
_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/videoLog/videoLog_entity.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 '../../../../app_settings/app_colors.dart';
|
||||
@ -241,61 +241,8 @@ class _EditVideoLogPageState extends State<EditVideoLogPage> {
|
||||
|
||||
Future<void> _onDelClick() async {
|
||||
if (state.selectVideoLogList.value.isNotEmpty) {
|
||||
// 弹出自定义确认对话框
|
||||
bool confirmDelete = await showDialog(
|
||||
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();
|
||||
}
|
||||
await logic.deleteLockCloudStorageList();
|
||||
setState(() {});
|
||||
} else {
|
||||
logic.showToast('请选择要删除的视频'.tr);
|
||||
}
|
||||
@ -412,7 +359,7 @@ class _EditVideoLogPageState extends State<EditVideoLogPage> {
|
||||
}
|
||||
|
||||
_buildVideoItem(RecordListData recordData) {
|
||||
return VideoThumbnail(videoUrl: recordData.videoUrl!);
|
||||
return VideoThumbnailImage(videoUrl: recordData.videoUrl!);
|
||||
}
|
||||
|
||||
_buildImageItem(RecordListData recordData) {
|
||||
|
||||
@ -22,38 +22,12 @@ class VideoLogLogic extends BaseGetXController {
|
||||
(record.imagesUrl != null && record.imagesUrl!.isNotEmpty) ||
|
||||
(record.videoUrl != null && record.videoUrl!.isNotEmpty))
|
||||
.toList();
|
||||
|
||||
// // 为 videoUrl 不为空的项设置封面
|
||||
// for (var record in element.recordList!) {
|
||||
// if (record.videoUrl != null && record.videoUrl!.isNotEmpty) {
|
||||
// _setVideoThumbnail(record); // 设置视频封面
|
||||
// }
|
||||
// }
|
||||
});
|
||||
|
||||
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
|
||||
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_state.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/noData.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
@ -341,7 +341,7 @@ class _VideoLogPageState extends State<VideoLogPage> {
|
||||
}
|
||||
|
||||
_buildVideoItem(RecordListData recordData) {
|
||||
return VideoThumbnail(videoUrl: recordData.videoUrl!);
|
||||
return VideoThumbnailImage(videoUrl: recordData.videoUrl!);
|
||||
}
|
||||
|
||||
_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/videoLogDetail_state.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:video_player/video_player.dart';
|
||||
|
||||
@ -165,7 +165,7 @@ class _VideoLogDetailPageState extends State<VideoLogDetailPage> {
|
||||
}
|
||||
|
||||
_buildVideoItem(RecordListData recordData) {
|
||||
return VideoThumbnail(videoUrl: recordData.videoUrl!);
|
||||
return VideoThumbnailImage(videoUrl: recordData.videoUrl!);
|
||||
}
|
||||
|
||||
_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