start method

Future<void> start({
  1. Duration? countdown,
  2. dynamic onTick(
    1. int remainingSeconds
    )?,
})

Start recording the widget with optional countdown

countdown - Optional countdown duration before recording starts onTick - Optional callback called each second during countdown

Implementation

Future<void> start({
  Duration? countdown,
  Function(int remainingSeconds)? onTick,
}) async {
  if (_isRecording) {
    debugPrint('[WidgetRecorder] ⚠️ Already recording - ignoring start request');
    return;
  }

  // Handle countdown if specified
  if (countdown != null && countdown.inSeconds > 0) {
    debugPrint('[WidgetRecorder] ⏳ Starting countdown: ${countdown.inSeconds} seconds');

    for (int i = countdown.inSeconds; i > 0; i--) {
      debugPrint('[WidgetRecorder] ⏳   └─ Countdown: $i...');
      onTick?.call(i);
      await Future.delayed(const Duration(seconds: 1));
    }

    debugPrint('[WidgetRecorder] ✅ Countdown complete - starting recording');
  }

  // Handle permission automatically if audio recording is enabled
  if (recordAudio) {
    debugPrint('[WidgetRecorder] 🎤 Audio recording enabled - checking permissions');
    final hasPermission = await _handlePermission();
    if (!hasPermission) {
      debugPrint('[WidgetRecorder] ❌ Microphone permission denied');
      _handleError('Microphone permission denied');
      return;
    }
    debugPrint('[WidgetRecorder] ✅ Microphone permission granted');
  }

  _isRecording = true;

  try {
    debugPrint('[WidgetRecorder] 🎬 Starting recording...');

    // Get output directory
    final Directory dir;
    if (customSavePath != null) {
      debugPrint('[WidgetRecorder] 📁 Using custom save path: $customSavePath');
      dir = Directory(customSavePath!);
      // Create directory if it doesn't exist
      if (!await dir.exists()) {
        debugPrint('[WidgetRecorder] 📁   └─ Creating directory...');
        await dir.create(recursive: true);
        debugPrint('[WidgetRecorder] ✅   └─ Directory created');
      } else {
        debugPrint('[WidgetRecorder] ✅   └─ Directory exists');
      }
    } else {
      debugPrint('[WidgetRecorder] 📁 Using temporary directory');
      dir = await getTemporaryDirectory();
    }

    _outputPath = '${dir.path}/widget_rec_${DateTime.now().millisecondsSinceEpoch}.mp4';
    debugPrint('[WidgetRecorder] 📁 Output path: $_outputPath');

    final renderObject = _boundaryKey.currentContext?.findRenderObject();
    if (renderObject == null) {
      throw Exception('Widget not found. Ensure WidgetRecorder is built.');
    }

    _size = (renderObject as RenderRepaintBoundary).size;

    // Round dimensions down to the nearest multiple of 16 for perfect encoding
    final int validWidth = (_size!.width.toInt() ~/ 16) * 16;
    final int validHeight = (_size!.height.toInt() ~/ 16) * 16;

    debugPrint('[WidgetRecorder] 📐 Recording configuration:');
    debugPrint('[WidgetRecorder] 📐   ├─ Resolution: ${validWidth}x$validHeight');
    debugPrint('[WidgetRecorder] 📐   ├─ FPS: $_fps');
    debugPrint('[WidgetRecorder] 📐   ├─ Audio: ${recordAudio ? "enabled" : "disabled"}');
    if (_customBitrate != null) {
      debugPrint('[WidgetRecorder] 📐   └─ Custom bitrate: ${(_customBitrate! / 1000000).toStringAsFixed(1)} Mbps');
    }

    await _channel.invokeMethod('startRecording', {
      'width': validWidth,
      'height': validHeight,
      'fps': _fps,
      'outputPath': _outputPath,
      'recordAudio': recordAudio,
      if (_customBitrate != null) 'bitrate': _customBitrate,
    });

    _timer = Timer.periodic(
      Duration(milliseconds: 1000 ~/ _fps),
      (_) => _captureFrame(),
    );

    debugPrint('[WidgetRecorder] ✅ Recording started successfully');
  } catch (e) {
    debugPrint('[WidgetRecorder] ❌ Error starting recording: $e');
    _handleError(e.toString());
  }
}