Reason Pun

视频录制页面增加了计时器

import 'dart:async';
import 'dart:io';
import 'package:camera/camera.dart';
......@@ -6,6 +7,7 @@ import 'package:flutter/services.dart';
import 'package:one_poem/util/toast_utils.dart';
import 'package:one_poem/widgets/my_app_bar.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pausable_timer/pausable_timer.dart';
import 'package:video_player/video_player.dart';
import 'package:one_poem/extension/int_extension.dart';
......@@ -21,8 +23,7 @@ class _PoemRecordVideoPageState extends State<PoemRecordVideoPage>
CameraController? controller;
VideoPlayerController? videoController;
File? _imageFile;
File? _videoFile;
File? _videoFile; // 保存的视频文件位置,用于上传视频的时候用!
final bool _isVideoCameraSelected = true;
bool _isCameraInitialized = false;
......@@ -36,6 +37,15 @@ class _PoemRecordVideoPageState extends State<PoemRecordVideoPage>
ResolutionPreset currentResolutionPreset = ResolutionPreset.high;
List<CameraDescription>? cameras = [];
///声明变量
late final PausableTimer _timer;
///记录当前的时间
int currentTimer = 0;
int duration = 60 * 1000;
@override
void initState() {
// Hide the status bar in Android
......@@ -49,6 +59,20 @@ class _PoemRecordVideoPageState extends State<PoemRecordVideoPage>
Toast.show("摄像头出错啦~");
}
});
_timer = PausableTimer(
const Duration(milliseconds: 100),
() {
currentTimer += 100;
_timer
..reset()
..start();
if (currentTimer >= duration) {
stopRecordVideo();
}
setState(() {});
},
);
super.initState();
}
......@@ -76,22 +100,6 @@ class _PoemRecordVideoPageState extends State<PoemRecordVideoPage>
fileNames.add({0: int.parse(name), 1: file.path.split('/').last});
}
}
if (fileNames.isNotEmpty) {
final recentFile =
fileNames.reduce((curr, next) => curr[0] > next[0] ? curr : next);
String recentFileName = recentFile[1];
if (recentFileName.contains('.mp4')) {
_videoFile = File('${directory.path}/$recentFileName');
_imageFile = null;
_startVideoPlayer();
} else {
_imageFile = File('${directory.path}/$recentFileName');
_videoFile = null;
}
setState(() {});
}
}
Future<XFile?> takePicture() async {
......@@ -110,19 +118,6 @@ class _PoemRecordVideoPageState extends State<PoemRecordVideoPage>
}
}
Future<void> _startVideoPlayer() async {
if (_videoFile != null) {
videoController = VideoPlayerController.file(_videoFile!);
await videoController!.initialize().then((_) {
// Ensure the first frame is shown after the video is initialized,
// even before the play button has been pressed.
setState(() {});
});
await videoController!.setLooping(true);
await videoController!.play();
}
}
Future<void> startVideoRecording() async {
final CameraController? cameraController = controller;
......@@ -239,9 +234,34 @@ class _PoemRecordVideoPageState extends State<PoemRecordVideoPage>
void dispose() {
controller?.dispose();
videoController?.dispose();
///取消计时器
_timer.cancel();
super.dispose();
}
Future<void> stopRecordVideo() async {
try {
XFile? rawVideo = await stopVideoRecording();
File videoFile = File(rawVideo!.path);
int currentUnix = DateTime.now().millisecondsSinceEpoch;
final directory = await getApplicationDocumentsDirectory();
String fileFormat = videoFile.path.split('.').last;
_videoFile = await videoFile.copy(
'${directory.path}/$currentUnix.$fileFormat',
);
// TODO why pause!直接使用cancel()会出现问题,暂时这么解决
_timer.pause();
} catch (e) {
// print(e);
}
}
@override
Widget build(BuildContext context) {
return SafeArea(
......@@ -266,11 +286,11 @@ class _PoemRecordVideoPageState extends State<PoemRecordVideoPage>
),
),
Padding(
padding: const EdgeInsets.fromLTRB(
16.0,
8.0,
16.0,
8.0,
padding: EdgeInsets.fromLTRB(
16.px,
8.px,
16.px,
8.px,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
......@@ -281,14 +301,17 @@ class _PoemRecordVideoPageState extends State<PoemRecordVideoPage>
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
InkWell(
onTap: _isRecordingInProgress
? () async {
if (controller!.value.isRecordingPaused) {
await resumeVideoRecording();
_timer.start();
} else {
await pauseVideoRecording();
_timer.pause();
}
}
: () {
......@@ -305,29 +328,29 @@ class _PoemRecordVideoPageState extends State<PoemRecordVideoPage>
child: Stack(
alignment: Alignment.center,
children: [
const Icon(
Icon(
Icons.circle,
color: Colors.black38,
size: 60,
size: 60.px,
),
_isRecordingInProgress
? controller!.value.isRecordingPaused
? const Icon(
? Icon(
Icons.play_arrow,
color: Colors.white,
size: 30,
size: 30.px,
)
: const Icon(
: Icon(
Icons.pause,
color: Colors.white,
size: 30,
size: 30.px,
)
: Icon(
_isRearCameraSelected
? Icons.camera_front
: Icons.camera_rear,
color: Colors.white,
size: 30,
size: 30.px,
),
],
),
......@@ -335,24 +358,12 @@ class _PoemRecordVideoPageState extends State<PoemRecordVideoPage>
InkWell(
onTap: () async {
if (_isRecordingInProgress) {
XFile? rawVideo = await stopVideoRecording();
File videoFile = File(rawVideo!.path);
int currentUnix =
DateTime.now().millisecondsSinceEpoch;
final directory =
await getApplicationDocumentsDirectory();
String fileFormat =
videoFile.path.split('.').last;
_videoFile = await videoFile.copy(
'${directory.path}/$currentUnix.$fileFormat',
);
_startVideoPlayer();
stopRecordVideo();
} else {
currentTimer = 0;
_timer
..reset()
..start();
await startVideoRecording();
}
},
......@@ -364,21 +375,40 @@ class _PoemRecordVideoPageState extends State<PoemRecordVideoPage>
color: _isVideoCameraSelected
? Colors.white
: Colors.white38,
size: 80,
size: 80.px,
),
_isRecordingInProgress
? SizedBox(
width: 60.px,
height: 60.px,
child: CircularProgressIndicator(
strokeWidth: 5.px,
value: currentTimer / duration,
),
)
: Container(),
Icon(
Icons.circle,
color: _isVideoCameraSelected
? Colors.red
: Colors.white,
size: 65,
size: 65.px,
),
_isRecordingInProgress
? Container()
: Text(
"60s",
style: TextStyle(
fontSize: 12.px,
color: Colors.white,
),
),
_isVideoCameraSelected &&
_isRecordingInProgress
? const Icon(
? Icon(
Icons.stop_rounded,
color: Colors.white,
size: 32,
size: 32.px,
)
: Container(),
],
......@@ -388,16 +418,16 @@ class _PoemRecordVideoPageState extends State<PoemRecordVideoPage>
onTap: () {},
child: Stack(
alignment: Alignment.center,
children: const [
children: [
Icon(
Icons.circle,
color: Colors.black38,
size: 60,
size: 60.px,
),
Icon(
Icons.photo_album,
color: Colors.white,
size: 30,
size: 30.px,
),
],
),
......
......@@ -701,6 +701,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
pausable_timer:
dependency: "direct main"
description:
name: pausable_timer
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0+3"
pedantic:
dependency: transitive
description:
......
......@@ -99,6 +99,9 @@ dependencies:
camera: ^0.9.4+5
path_provider: ^2.0.8
# A Dart timer that can be paused, resumed and reset.
pausable_timer: ^1.0.0+3
dependency_overrides:
decimal: 1.5.0
......