371 lines
11 KiB
Dart
Executable File
371 lines
11 KiB
Dart
Executable File
import 'dart:io';
|
|
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter/widgets.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:star_lock/appRouters.dart';
|
|
import 'package:star_lock/app_settings/app_settings.dart';
|
|
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_image.dart';
|
|
import 'package:star_lock/tools/dateTool.dart';
|
|
import 'package:video_player/video_player.dart';
|
|
|
|
import '../../../../app_settings/app_colors.dart';
|
|
import '../../../../tools/titleAppBar.dart';
|
|
import 'videoLogDetail_logic.dart';
|
|
|
|
class VideoLogDetailPage extends StatefulWidget {
|
|
const VideoLogDetailPage({Key? key}) : super(key: key);
|
|
|
|
@override
|
|
State<VideoLogDetailPage> createState() => _VideoLogDetailPageState();
|
|
}
|
|
|
|
class _VideoLogDetailPageState extends State<VideoLogDetailPage> {
|
|
final VideoLogDetailLogic logic = Get.put(VideoLogDetailLogic());
|
|
final VideoLogDetailState state = Get.find<VideoLogDetailLogic>().state;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
AppLog.log(
|
|
'state.recordData.value.videoUrl!' + state.recordData.value.videoUrl!);
|
|
|
|
state.videoController =
|
|
createVideoController(state.recordData.value.videoUrl!);
|
|
|
|
state.videoController.addListener(() {
|
|
setState(() {});
|
|
});
|
|
state.videoController.setLooping(false);
|
|
state.videoController.initialize();
|
|
}
|
|
|
|
void _initializeVideoPlayer(String videoUrl) async {
|
|
if (state.videoController != null) {
|
|
await state.videoController.dispose(); // 释放旧资源
|
|
}
|
|
state.videoController =
|
|
createVideoController(state.recordData.value.videoUrl!);
|
|
|
|
// 初始化完成后通知框架重新构建界面
|
|
await state.videoController.initialize();
|
|
state.videoController.addListener(() {
|
|
setState(() {});
|
|
});
|
|
setState(() {});
|
|
}
|
|
|
|
VideoPlayerController createVideoController(String url) {
|
|
if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
return VideoPlayerController.networkUrl(
|
|
Uri.parse(url),
|
|
videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true),
|
|
);
|
|
} else {
|
|
final file = File(
|
|
url.startsWith('file://') ? url.replaceFirst('file://', '') : url);
|
|
return VideoPlayerController.file(
|
|
file,
|
|
videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true),
|
|
);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: Colors.white,
|
|
appBar: TitleAppBar(
|
|
barTitle: '视频播放'.tr,
|
|
haveBack: true,
|
|
backgroundColor: AppColors.mainColor,
|
|
),
|
|
body: state.videoController.value.isInitialized
|
|
? Column(
|
|
children: <Widget>[
|
|
Container(
|
|
color: Colors.black,
|
|
height: 500.h,
|
|
width: 1.sw,
|
|
child: Stack(
|
|
alignment: Alignment.bottomCenter,
|
|
children: <Widget>[
|
|
RotatedBox(
|
|
quarterTurns: -1,
|
|
child: AspectRatio(
|
|
aspectRatio: state.videoController.value.size.width /
|
|
state.videoController.value.size.height,
|
|
child: VideoPlayer(state.videoController),
|
|
),
|
|
),
|
|
ControlsOverlay(
|
|
controller: state.videoController,
|
|
recordData: state.recordData.value,
|
|
),
|
|
if (state.videoController.value.isPlaying ||
|
|
state.videoController.value.isBuffering)
|
|
Container()
|
|
else
|
|
VideoProgressIndicator(
|
|
state.videoController,
|
|
colors: VideoProgressColors(
|
|
playedColor: AppColors.mainColor),
|
|
allowScrubbing: true,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
// _buildTitleRow(),
|
|
_buildOther(),
|
|
],
|
|
)
|
|
: Center(
|
|
child: CircularProgressIndicator(),
|
|
),
|
|
);
|
|
}
|
|
|
|
double itemW = (1.sw - 15.w * 4) / 3;
|
|
double itemH = (1.sw - 15.w * 4) / 3 + 40.h;
|
|
|
|
Widget videoItem(RecordListData recordData) {
|
|
return GestureDetector(
|
|
onTap: () {
|
|
if (recordData.videoUrl != null && recordData.videoUrl!.isNotEmpty) {
|
|
state.recordData.value = recordData;
|
|
_initializeVideoPlayer(recordData.videoUrl!);
|
|
setState(() {});
|
|
} else if (recordData.imagesUrl != null &&
|
|
recordData.imagesUrl!.isNotEmpty) {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => FullScreenImagePage(
|
|
imageUrl: recordData.imagesUrl!,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
},
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(horizontal: 20.w),
|
|
margin: EdgeInsets.only(
|
|
bottom: 20.h,
|
|
left: 18.w,
|
|
right: 18.w,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(10.w),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.grey.withOpacity(0.2),
|
|
spreadRadius: 1,
|
|
blurRadius: 5,
|
|
offset: const Offset(0, 3), // changes position of shadow
|
|
),
|
|
],
|
|
),
|
|
width: 1.sw,
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: <Widget>[
|
|
Row(
|
|
children: [
|
|
Container(
|
|
padding: EdgeInsets.all(10.w),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(58.w),
|
|
color: AppColors.mainColor,
|
|
),
|
|
child: Icon(
|
|
_buildIconByType(recordData),
|
|
size: 48.sp,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
SizedBox(
|
|
width: 14.w,
|
|
),
|
|
Container(
|
|
height: itemW,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Text(
|
|
_buildTitleByType(recordData),
|
|
style: TextStyle(
|
|
fontSize: 24.sp,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
SizedBox(
|
|
height: 8.h,
|
|
),
|
|
Text(
|
|
DateTool()
|
|
.dateToHNString(recordData.operateDate.toString()),
|
|
textAlign: TextAlign.center,
|
|
style: TextStyle(
|
|
fontSize: 20.sp,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
Container(
|
|
width: 118.w,
|
|
height: 118.w,
|
|
margin: const EdgeInsets.all(0),
|
|
color: Colors.white,
|
|
child: ClipRRect(
|
|
borderRadius: BorderRadius.circular(10.w),
|
|
child: _buildImageOrVideoItem(recordData),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
_buildImageOrVideoItem(RecordListData recordData) {
|
|
if (recordData.videoUrl != null && recordData.videoUrl!.isNotEmpty) {
|
|
return _buildVideoItem(recordData);
|
|
} else {
|
|
return _buildImageItem(recordData);
|
|
}
|
|
}
|
|
|
|
_buildVideoItem(RecordListData recordData) {
|
|
return VideoThumbnailImage(videoUrl: recordData.videoUrl!);
|
|
}
|
|
|
|
_buildImageItem(RecordListData recordData) {
|
|
return RotatedBox(
|
|
quarterTurns: -1,
|
|
child: Image.network(
|
|
recordData.imagesUrl!,
|
|
fit: BoxFit.cover,
|
|
errorBuilder:
|
|
(BuildContext context, Object error, StackTrace? stackTrace) {
|
|
// 图片加载失败时显示错误图片
|
|
return RotatedBox(
|
|
quarterTurns: -1,
|
|
child: Image.asset(
|
|
'images/icon_unHaveData.png', // 错误图片路径
|
|
fit: BoxFit.cover,
|
|
),
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
String formatString(time) {
|
|
String shortName = time.toString().substring(2, 7);
|
|
return shortName;
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
super.dispose();
|
|
state.videoController.dispose();
|
|
}
|
|
|
|
_buildOther() {
|
|
return Expanded(
|
|
child: ListView.builder(
|
|
itemCount: state.videoLogList.length,
|
|
itemBuilder: (BuildContext c, int index) {
|
|
CloudStorageData item = state.videoLogList[index];
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: <Widget>[
|
|
Container(
|
|
margin: EdgeInsets.only(left: 20.w, top: 15.w, bottom: 15.w),
|
|
child: Row(
|
|
children: <Widget>[
|
|
Text(item.date ?? '',
|
|
style: TextStyle(
|
|
fontSize: 24.sp, fontWeight: FontWeight.w600)),
|
|
],
|
|
),
|
|
),
|
|
...mainListView(index, item),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
// 云存列表
|
|
List<Widget> mainListView(int index, CloudStorageData itemData) {
|
|
return itemData.recordList!.map((e) => videoItem(e)).toList();
|
|
}
|
|
|
|
String _buildTitleByType(RecordListData item) {
|
|
final recordType = item.recordType;
|
|
switch (recordType) {
|
|
case 130:
|
|
return '防拆报警'.tr;
|
|
case 160:
|
|
return '人脸'.tr + '开锁'.tr;
|
|
case 220:
|
|
return '逗留警告'.tr;
|
|
default:
|
|
return '';
|
|
}
|
|
}
|
|
|
|
IconData _buildIconByType(RecordListData item) {
|
|
final recordType = item.recordType;
|
|
switch (recordType) {
|
|
case 130:
|
|
return Icons.fmd_bad_outlined;
|
|
case 160:
|
|
return Icons.tag_faces_outlined;
|
|
case 220:
|
|
return Icons.wifi_tethering_error_rounded_outlined;
|
|
default:
|
|
return Icons.priority_high_rounded;
|
|
}
|
|
}
|
|
|
|
_buildItem(itemData) {
|
|
return videoItem(itemData);
|
|
}
|
|
|
|
_buildTitleRow() {
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
),
|
|
padding: EdgeInsets.only(left: 15.w, top: 24.w, bottom: 24.w),
|
|
child: Row(
|
|
children: [
|
|
Text(
|
|
_buildTitleByType(state.recordData.value) ?? '',
|
|
style: TextStyle(
|
|
fontSize: 24.sp,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|