You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 03:45:07