Skip to content

[camera_avfoundation][iOS] Crash when enableAudio = false due to incorrect guard condition #174702

@juliendelarbre

Description

@juliendelarbre

Steps to reproduce

1.	Create a Flutter project targeting iOS.
2.	Do not add NSMicrophoneUsageDescription to ios/Runner/Info.plist.
3.	Initialize a CameraController with enableAudio: false.
4.	Call startVideoRecording().
5.	The app crashes.

Expected results

•	With enableAudio = false, the iOS implementation should not set up audio.
•	Video recording should proceed normally without microphone permission.

Actual results

•	Even with enableAudio = false, the iOS implementation still attempts to set up audio.
•	On iOS, if NSMicrophoneUsageDescription is missing, the app crashes.

Code sample

Code sample

code reproduction example git

import 'package:camera/camera.dart';
import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final cams = await availableCameras();
  final cam = cams.first; // Use the first available camera
  runApp(MyApp(cameraDescription: cam));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key, required this.cameraDescription});
  final CameraDescription cameraDescription;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MRE: enableAudio=false crash (iOS)',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: RecordPage(cameraDescription: cameraDescription),
    );
  }
}

class RecordPage extends StatefulWidget {
  const RecordPage({super.key, required this.cameraDescription});
  final CameraDescription cameraDescription;

  @override
  State<RecordPage> createState() => _RecordPageState();
}

class _RecordPageState extends State<RecordPage> {
  late final CameraController _controller;
  bool _initializing = true;
  bool _recording = false;
  String? _lastVideoPath;
  String? _error;

  @override
  void initState() {
    super.initState();
    _controller = CameraController(
      widget.cameraDescription,
      ResolutionPreset.medium,
      // *** Key for repro: audio explicitly disabled ***
      enableAudio: false,
    );
    _init();
  }

  Future<void> _init() async {
    try {
      await _controller.initialize();

      setState(() => _initializing = false);
    } catch (e) {
      setState(() {
        _initializing = false;
        _error = 'Initialization error: $e';
      });
    }
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  Future<void> _toggleRecord() async {
    setState(() => _error = null);
    try {
      if (!_recording) {
        await _controller
            .startVideoRecording(); // no path; plugin returns XFile on stop
        setState(() => _recording = true);
      } else {
        final file = await _controller.stopVideoRecording();
        setState(() {
          _recording = false;
          _lastVideoPath = file.path;
        });
      }
    } catch (e) {
      setState(() => _error = 'Record error: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    if (_initializing) {
      return const Scaffold(body: Center(child: CircularProgressIndicator()));
    }
    if (_error != null) {
      return Scaffold(
        appBar: AppBar(title: const Text('MRE: enableAudio=false crash (iOS)')),
        body: Center(child: Text(_error!, textAlign: TextAlign.center)),
      );
    }

    return Scaffold(
      appBar: AppBar(title: const Text('MRE: enableAudio=false crash (iOS)')),
      body: Column(
        children: [
          Expanded(
            child:
                _controller.value.isInitialized
                    ? CameraPreview(_controller)
                    : const Center(child: Text('Camera not initialized')),
          ),
          if (_lastVideoPath != null)
            Padding(
              padding: const EdgeInsets.all(12),
              child: Text('Last video: $_lastVideoPath'),
            ),
          const SizedBox(height: 12),
        ],
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: _toggleRecord,
        label: Text(_recording ? 'Stop' : 'Record'),
        icon: Icon(_recording ? Icons.stop : Icons.fiber_manual_record),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
    );
  }
}

Screenshots or Video

Logs

Logs
[  +24 ms] Caching compiled dill
[  +54 ms] Connecting to service protocol: http://192.168.1.38:59585/Z2bm7s8zemY=/
[ +109 ms] Launching a Dart Developer Service (DDS) instance at http://127.0.0.1:0, connecting to VM service at http://192.168.1.38:59585/Z2bm7s8zemY=/.
[ +366 ms] Successfully connected to service protocol: http://192.168.1.38:59585/Z2bm7s8zemY=/
[  +40 ms] DevFS: Creating new filesystem on the device (null)
[  +29 ms] DevFS: Created new filesystem on the device (file:///private/var/mobile/Containers/Data/Application/E4080DDB-AF56-4437-A366-30DA9A50D938/tmp/bugJLvP3B/bug/)
[   +1 ms] Updating assets
[  +61 ms] Syncing files to device iPh0ne (wireless)...
[        ] Compiling dart to kernel with 0 updated files
[        ] Processing bundle.
[        ] <- recompile package:bug/main.dart 5402615f-0015-4378-bdd7-f85a3b5d95e4
[        ] <- 5402615f-0015-4378-bdd7-f85a3b5d95e4
[        ] Bundle processing done.
[  +45 ms] Updating files.
[        ] Pending asset builds completed. Writing dirty entries.
[        ] DevFS: Sync finished
[        ] Syncing files to device iPh0ne (wireless)... (completed in 47ms)
[        ] Synced 0.0MB.
[        ] <- accept
[  +12 ms] Connected to _flutterView/0x1020fd420.
[   +1 ms] Flutter run key commands.
[        ] r Hot reload. 🔥🔥🔥
[        ] R Hot restart.
[        ] h List all available interactive commands.
[        ] d Detach (terminate "flutter run" but leave application running).
[        ] c Clear the screen
[        ] q Quit (terminate the application on the device).
[        ] A Dart VM Service on iPh0ne (wireless) is available at: http://127.0.0.1:59770/o9DTLyvR2wY=/
[  +37 ms] The Flutter DevTools debugger and profiler on iPh0ne (wireless) is available at: http://127.0.0.1:9101?uri=http://127.0.0.1:59770/o9DTLyvR2wY=/


App is being debugged, do not track this hang
Hang detected: 0.46s (debugger attached, not reporting)
App is being debugged, do not track this hang
Hang detected: 0.50s (debugger attached, not reporting)
This app has crashed because it attempted to access privacy-sensitive data without a usage description.  The app's Info.plist must contain an NSMicrophoneUsageDescription key with a string value explaining to the user how the app uses this data.

Flutter Doctor output

Doctor output
[✓] Flutter (Channel beta, 3.36.0-0.1.pre, on macOS 15.6 24G84 darwin-arm64, locale en-US) [319ms]
    • Flutter version 3.36.0-0.1.pre on channel beta at /Users/juliendelarbre/fvm/versions/3.36.0-0.1.pre
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision c84cdd0d0b (2 weeks ago), 2025-08-13 17:21:27 -0700
    • Engine revision 877970cbc9
    • Dart version 3.10.0 (build 3.10.0-75.1.beta)
    • DevTools version 2.49.0
    • Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations, enable-native-assets, enable-lldb-debugging

[!] Android toolchain - develop for Android devices (Android SDK version 35.0.1) [3.2s]
    • Android SDK at /Users/juliendelarbre/Library/Android/sdk
    • Emulator version 35.3.11.0 (build_id 12836668) (CL:N/A)
    ✗ Flutter requires Android SDK 36 and the Android BuildTools 28.0.3
      To update the Android SDK visit https://flutter.dev/to/macos-android-setup for detailed instructions.
    ! Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses

[✓] Xcode - develop for iOS and macOS (Xcode 16.4) [1,236ms]
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 16F6
    • CocoaPods version 1.16.2

[✓] Chrome - develop for the web [5ms]
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Connected device (3 available) [5.8s]
    • iPh0ne (wireless) (mobile) • 00008120-0016612C11C0C01E • ios            • iOS 18.5 22F76
    • macOS (desktop)            • macos                     • darwin-arm64   • macOS 15.6 24G84 darwin-arm64
    • Chrome (web)               • chrome                    • web-javascript • Google Chrome 139.0.7258.155

[✓] Network resources [520ms]
    • All expected network resources are available.

! Doctor found issues in 1 category.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listc: crashStack traces logged to the consolefound in release: 3.35Found to occur in 3.35found in release: 3.36Found to occur in 3.36has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: cameraThe camera pluginpackageflutter/packages repository. See also p: labels.platform-iosiOS applications specificallyteam-iosOwned by iOS platform teamtriaged-iosTriaged by iOS platform team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions