Flutter使用camera包录制视频时如何实时显示当前录制时长
针对Flutter camera包录制实时时长展示的方案
目前官方
camera插件没有内置录制时长实时回调、监听事件,也没有可直接查询的当前录制时长公开属性,所以目前和录制逻辑结合最紧密的方案是与CameraController的录制状态强绑定的计时器实现,完全避免计时器和实际录制状态不同步的问题,比独立写通用计时器更可靠。
实现核心要点
- 计时器必须和
startVideoRecording的成功回调同步启动,不能在调用方法的同时就启动,避免方法调用失败(比如权限问题、硬件占用)时计时器空跑 - 计时器必须和
stopVideoRecording、录制异常、录制中断(比如用户切后台、相机被其他应用占用)的时机同步停止/销毁,避免内存泄漏和时长统计错误 - 推荐用
Stopwatch类实现更精准的时长统计,不需要自己做时间累加,配合Timer.periodic实现界面更新即可
完整示例代码
import 'dart:async'; import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; class CameraRecordPage extends StatefulWidget { const CameraRecordPage({super.key}); @override State<CameraRecordPage> createState() => _CameraRecordPageState(); } class _CameraRecordPageState extends State<CameraRecordPage> { CameraController? _cameraController; // 系统Stopwatch计时精度远高于手动累加 final Stopwatch _recordStopwatch = Stopwatch(); Timer? _durationUpdateTimer; String _currentRecordDuration = "00:00"; // 相机初始化逻辑可参考camera包官方默认示例,此处省略 // ... // 开始录制方法 Future<void> startRecord() async { if (_cameraController == null || !_cameraController!.value.isInitialized) return; if (_cameraController!.value.isRecordingVideo) return; try { // 等待录制真正启动成功后再启动计时 await _cameraController!.startVideoRecording(); _recordStopwatch.reset(); _recordStopwatch.start(); // 每100毫秒更新一次时长,展示更丝滑 _durationUpdateTimer = Timer.periodic(const Duration(milliseconds: 100), (timer) { final duration = _recordStopwatch.elapsed; setState(() { _currentRecordDuration = "${duration.inMinutes.toString().padLeft(2, '0')}:${(duration.inSeconds % 60).toString().padLeft(2, '0')}"; }); }); } on CameraException catch (e) { // 录制启动失败时重置计时变量 _resetRecordTimer(); debugPrint("录制启动失败: $e"); } } // 停止录制方法 Future<XFile?> stopRecord() async { if (_cameraController == null || !_cameraController!.value.isRecordingVideo) return null; try { final file = await _cameraController!.stopVideoRecording(); _resetRecordTimer(); return file; } on CameraException catch (e) { _resetRecordTimer(); debugPrint("录制停止失败: $e"); return null; } } // 统一重置计时工具 void _resetRecordTimer() { _durationUpdateTimer?.cancel(); _durationUpdateTimer = null; _recordStopwatch.stop(); _recordStopwatch.reset(); setState(() { _currentRecordDuration = "00:00"; }); } // 监听相机状态变化,处理异常中断场景 @override void initState() { super.initState(); // 相机初始化完成后添加监听 _cameraController?.addListener(() { if (_cameraController?.value.isRecordingVideo == false && _recordStopwatch.isRunning) { // 非用户主动触发的录制中断,同步停止计时 _resetRecordTimer(); } }); } @override void dispose() { // 页面销毁时强制清理资源 _resetRecordTimer(); _cameraController?.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: Stack( children: [ // 相机预览层逻辑省略 // ... // 时长展示组件 Positioned( top: 50, left: 20, child: Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: Colors.black54, borderRadius: BorderRadius.circular(4), ), child: Text( _currentRecordDuration, style: const TextStyle(color: Colors.white, fontSize: 14), ), ), ) ], ), ); } }
额外优化点
- 如果需要实现最长录制时长限制,直接在时长更新的回调中判断当前时长是否达到阈值,达到后自动调用停止录制方法即可
- 如果需要展示毫秒级的时长精度,只需要修改
_currentRecordDuration的格式化逻辑,补充毫秒位展示即可
内容的提问来源于stack exchange,提问作者Darrell
相关产品推荐
相关产品推荐

