Repository navigation
[camera] setFocusMode(FocusMode.auto) throws "StateError: Bad state: No element" when the focus-metering action has no AF points #187782
Description
What package does this bug report belong to?
camera
What target platforms are you seeing this bug on?
Android
Have you already upgraded your packages?
Yes
Dependency versions
pubspec.lock
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
url: "https://pub.dev"
source: hosted
version: "2.13.1"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
camera:
dependency: "direct main"
description:
name: camera
sha256: "034c38cb8014d29698dcae6d20276688a1bf74e6487dfeb274d70ea05d5f7777"
url: "https://pub.dev"
source: hosted
version: "0.12.0+1"
camera_android_camerax:
dependency: transitive
description:
name: camera_android_camerax
sha256: e20c1e92ce6797d9ae9b1db1e09a4c1039a04827d0b24985f5da3840b96948ac
url: "https://pub.dev"
source: hosted
version: "0.7.2+1"
camera_avfoundation:
dependency: transitive
description:
name: camera_avfoundation
sha256: "90e4cc3fde331581a3b2d35d83be41dbb7393af0ab857eb27b732174289cb96d"
url: "https://pub.dev"
source: hosted
version: "0.10.1"
camera_platform_interface:
dependency: transitive
description:
name: camera_platform_interface
sha256: "7ac852d77699acee79f0d438b793feee26721841e50973576419ff5c6d95e9b7"
url: "https://pub.dev"
source: hosted
version: "2.13.0"
camera_web:
dependency: transitive
description:
name: camera_web
sha256: "57f49a635c8bf249d07fb95eb693d7e4dda6796dedb3777f9127fb54847beba7"
url: "https://pub.dev"
source: hosted
version: "0.3.5+3"
characters:
dependency: transitive
description:
name: characters
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
url: "https://pub.dev"
source: hosted
version: "1.4.1"
clock:
dependency: transitive
description:
name: clock
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.dev"
source: hosted
version: "1.1.2"
collection:
dependency: transitive
description:
name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.dev"
source: hosted
version: "1.19.1"
cross_file:
dependency: transitive
description:
name: cross_file
sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937"
url: "https://pub.dev"
source: hosted
version: "0.3.5+2"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd"
url: "https://pub.dev"
source: hosted
version: "1.0.9"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
source: hosted
version: "1.3.3"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
url: "https://pub.dev"
source: hosted
version: "6.0.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: "3854fe5e3bff0b113c658f260b90c95dea17c92db0f2addeac2e343dd9969785"
url: "https://pub.dev"
source: hosted
version: "2.0.35"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev"
source: hosted
version: "11.0.2"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev"
source: hosted
version: "3.0.10"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
lints:
dependency: transitive
description:
name: lints
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
url: "https://pub.dev"
source: hosted
version: "6.1.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
url: "https://pub.dev"
source: hosted
version: "0.12.19"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
url: "https://pub.dev"
source: hosted
version: "0.13.0"
meta:
dependency: transitive
description:
name: meta
sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
url: "https://pub.dev"
source: hosted
version: "1.18.0"
path:
dependency: transitive
description:
name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev"
source: hosted
version: "1.9.1"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
version: "2.1.8"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
source_span:
dependency: transitive
description:
name: source_span
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
url: "https://pub.dev"
source: hosted
version: "1.10.2"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.dev"
source: hosted
version: "1.12.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
stream_transform:
dependency: transitive
description:
name: stream_transform
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
url: "https://pub.dev"
source: hosted
version: "2.1.1"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
url: "https://pub.dev"
source: hosted
version: "1.4.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
url: "https://pub.dev"
source: hosted
version: "1.2.2"
test_api:
dependency: transitive
description:
name: test_api
sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
url: "https://pub.dev"
source: hosted
version: "0.7.11"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev"
source: hosted
version: "2.2.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360"
url: "https://pub.dev"
source: hosted
version: "15.2.0"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
sdks:
dart: ">=3.12.0 <4.0.0"
flutter: ">=3.44.0"
Steps to reproduce
AndroidCameraCameraX.setFocusMode(FocusMode.auto) calls .first on the current focus-metering action's AF metering points without an isEmpty guard. When that list is empty, it throws StateError: Bad state: No element.
The attached minimal app reproduces it deterministically by driving a normal CameraController through five calls (same call path as a real app):
- Run the attached app on an Android device.
- Wait until the camera preview appears (camera initialized).
- Tap Reproduce (StateError). It runs, in order:
setExposurePoint(Offset(0.5, 0.5))— action has only an AE pointsetFocusPoint(Offset(0.5, 0.5))— action now also has an AF pointsetFocusMode(FocusMode.locked)— lock while an AF point existssetFocusPoint(null)— clear the AF point (AF list becomes empty, action stays non-null)setFocusMode(FocusMode.auto)— throwsStateError: Bad state: No element
- Observe the thrown
StateErroron step 5.
Two conditions matter and are easy to miss:
- Step 3 (
locked) is required. Without a priorlocked,setFocusMode(auto)early-returns atif (_currentFocusMode == mode) return;(the initial mode is alreadyauto) and never reaches the faulty line. - The faulty line is only reached when
_defaultFocusPointLocked == false, which is the case when the lock happened while an AF point existed (so the plugin did not fall back to its default center lock).
Expected results
setFocusMode(FocusMode.auto) should succeed even when the current focus-metering action has no AF points — exactly like the FocusMode.locked branch of the same method, which already guards this with isEmpty.
Actual results
Step 5, setFocusMode(FocusMode.auto), throws and never completes:
StateError: Bad state: No element
The stack trace (see Logs) points to AndroidCameraCameraX.setFocusMode, where the FocusMode.auto branch reads the AF metering points without an isEmpty guard:
case FocusMode.auto:
final MeteringPoint? unLockedFocusPoint = _defaultFocusPointLocked
? null
: currentFocusMeteringAction!.meteringPointsAf.first; // throws when the list is emptyThe FocusMode.locked branch just below already guards the same access with isEmpty; the auto branch is simply missing it.
Code sample
Code sample
import 'dart:developer' as developer;
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
// Minimal reproduction: switching the focus mode to FocusMode.auto throws
// "StateError: Bad state: No element" in camera_android_camerax when the current
// focus-metering action has no AF metering points. Tap "Reproduce (StateError)"
// and watch step 5. See the issue description for the root cause.
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'camera_focus_issue',
theme: ThemeData(colorSchemeSeed: Colors.indigo),
home: const ReproPage(),
);
}
}
class ReproPage extends StatefulWidget {
const ReproPage({super.key});
@override
State<ReproPage> createState() => _ReproPageState();
}
class _ReproPageState extends State<ReproPage> {
CameraController? _controller;
final List<String> _log = <String>[];
// Focus/exposure points use normalized coordinates in [0, 1].
static const Offset _center = Offset(0.5, 0.5);
@override
void initState() {
super.initState();
_setupCamera();
}
@override
void dispose() {
_controller?.dispose();
super.dispose();
}
void _addLog(String message) {
debugPrint('[repro] $message');
if (!mounted) return;
setState(() => _log.insert(0, message));
}
Future<void> _setupCamera() async {
try {
_addLog('Calling availableCameras()...');
final List<CameraDescription> cameras = await availableCameras();
if (cameras.isEmpty) {
_addLog('No cameras found');
return;
}
final CameraDescription description = cameras.firstWhere(
(CameraDescription c) => c.lensDirection == CameraLensDirection.back,
orElse: () => cameras.first,
);
final CameraController controller = CameraController(
description,
ResolutionPreset.medium,
enableAudio: false,
);
await controller.initialize();
if (!mounted) {
await controller.dispose();
return;
}
setState(() => _controller = controller);
_addLog(
'initialized: focusPointSupported=${controller.value.focusPointSupported}, '
'exposurePointSupported=${controller.value.exposurePointSupported}',
);
} catch (e, st) {
_addLog('Setup failed: $e');
developer.log('setup failed', name: 'repro', error: e, stackTrace: st);
}
}
/// Runs a single step (for manual inspection). Exceptions are caught and
/// logged so the rest of the UI keeps working.
Future<void> _step(String label, Future<void> Function() action) async {
final CameraController? c = _controller;
if (c == null || !c.value.isInitialized) {
_addLog('controller not initialized');
return;
}
try {
await action();
_addLog('OK: $label');
} catch (e, st) {
_addLog('${e.runtimeType}: $label -> $e');
developer.log(label, name: 'repro', error: e, stackTrace: st);
}
}
/// Runs the 5 steps in order to reproduce the StateError.
///
/// 1. setExposurePoint(center) - build an action with only an AE point.
/// 2. setFocusPoint(center) - now the action has both AE and AF points.
/// 3. setFocusMode(locked) - lock while an AF point exists, so
/// _defaultFocusPointLocked stays false and
/// _currentFocusMode becomes locked. Without
/// this, step 5 early-returns and does not crash.
/// 4. setFocusPoint(null) - clear the AF point, leaving only the AE point
/// (meteringPointsAf empty, action non-null).
/// 5. setFocusMode(auto) - the auto branch calls .first on the empty AF
/// list -> StateError: Bad state: No element.
Future<void> _reproduce() async {
final CameraController? c = _controller;
if (c == null || !c.value.isInitialized) {
_addLog('controller not initialized');
return;
}
_addLog('=== reproduction sequence start ===');
await _step('1) setExposurePoint(center)', () => c.setExposurePoint(_center));
await _step('2) setFocusPoint(center)', () => c.setFocusPoint(_center));
await _step('3) setFocusMode(locked)', () => c.setFocusMode(FocusMode.locked));
await _step('4) setFocusPoint(null) [clear AF]', () => c.setFocusPoint(null));
await _step('5) setFocusMode(auto) [StateError expected here]',
() => c.setFocusMode(FocusMode.auto));
_addLog('=== reproduction sequence end ===');
}
@override
Widget build(BuildContext context) {
final CameraController? c = _controller;
final bool ready = c != null && c.value.isInitialized;
return Scaffold(
appBar: AppBar(title: const Text('camera_focus_issue repro')),
body: Column(
children: <Widget>[
Expanded(
flex: 3,
child: Container(
color: Colors.black,
alignment: Alignment.center,
child: ready
? CameraPreview(c)
: const Text(
'Initializing camera...',
style: TextStyle(color: Colors.white),
),
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Wrap(
spacing: 8,
runSpacing: 4,
alignment: WrapAlignment.center,
children: <Widget>[
FilledButton(
onPressed: ready ? _reproduce : null,
child: const Text('Reproduce (StateError)'),
),
OutlinedButton(
onPressed: ready
? () => _step('setExposurePoint(center)',
() => c.setExposurePoint(_center))
: null,
child: const Text('setExposurePoint'),
),
OutlinedButton(
onPressed: ready
? () => _step('setFocusPoint(center)',
() => c.setFocusPoint(_center))
: null,
child: const Text('setFocusPoint'),
),
OutlinedButton(
onPressed: ready
? () => _step('setFocusPoint(null)',
() => c.setFocusPoint(null))
: null,
child: const Text('setFocusPoint(null)'),
),
OutlinedButton(
onPressed: ready
? () => _step('setFocusMode(locked)',
() => c.setFocusMode(FocusMode.locked))
: null,
child: const Text('setFocusMode(locked)'),
),
OutlinedButton(
onPressed: ready
? () => _step('setFocusMode(auto)',
() => c.setFocusMode(FocusMode.auto))
: null,
child: const Text('setFocusMode(auto)'),
),
],
),
),
const Divider(height: 1),
Expanded(
flex: 2,
child: ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: _log.length,
itemBuilder: (BuildContext context, int index) {
final String line = _log[index];
final bool isError = line.contains('Error') ||
line.contains('Exception') ||
line.contains('failed');
return Text(
line,
style: TextStyle(
fontFamily: 'monospace',
fontSize: 12,
color: isError ? Colors.red : null,
),
);
},
),
),
],
),
);
}
}
Screenshots or Videos
Screenshots / Video demonstration
2026-06-10.22.46.00.mov
Logs
Logs
I/flutter (15808): [repro] Calling availableCameras()...
I/flutter (15808): [repro] initialized: focusPointSupported=true, exposurePointSupported=true
I/flutter (15808): [repro] === reproduction sequence start ===
I/flutter (15808): [repro] OK: 1) setExposurePoint(center)
I/flutter (15808): [repro] OK: 2) setFocusPoint(center)
I/flutter (15808): [repro] OK: 3) setFocusMode(locked)
I/flutter (15808): [repro] OK: 4) setFocusPoint(null) [clear AF]
I/flutter (15808): [repro] StateError: 5) setFocusMode(auto) [StateError expected here] -> Bad state: No element
I/flutter (15808): [repro] === reproduction sequence end ===
[repro] 5) setFocusMode(auto) [StateError expected here]
Bad state: No element
#0 ListBase.first (dart:collection/list.dart:64:22)
#1 AndroidCameraCameraX.setFocusMode (package:camera_android_camerax/src/android_camera_camerax.dart:666:60)
#2 CameraController.setFocusMode (package:camera/src/camera_controller.dart:957:37)
#3 _ReproPageState._reproduce.<anonymous closure> (package:camera_focus_issue/main.dart:135:17)
#4 _ReproPageState._step (package:camera_focus_issue/main.dart:103:19)
#5 _ReproPageState._reproduce (package:camera_focus_issue/main.dart:134:11)
<asynchronous suspension>Flutter Doctor output
Doctor output
[✓] Flutter (Channel stable, 3.44.1, on macOS 26.5.1 25F80 darwin-arm64, locale ja-JP) [692ms]
• Flutter version 3.44.1 on channel stable at /Users/osaki/fvm/versions/3.44.1
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 924134a44c (12 days ago), 2026-05-29 12:13:22 -0400
• Engine revision c416acfeb8
• Dart version 3.12.1
• DevTools version 2.57.0
• Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations, enable-native-assets,
enable-swift-package-manager, omit-legacy-version-file, enable-lldb-debugging, enable-uiscene-migration
[✓] Android toolchain - develop for Android devices (Android SDK version 36.1.0) [2.7s]
• Android SDK at /Users/osaki/Library/Android/sdk
• Emulator version 36.6.11.0 (build_id 15507667) (CL:N/A)
• Platform android-36, build-tools 36.1.0
• Java binary at: /Users/osaki/Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
This is the JDK bundled with the latest Android Studio installation on this machine.
To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
• Java version OpenJDK Runtime Environment (build 21.0.10+-117844308-b1163.108)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 26.5) [1,756ms]
• Xcode at /Applications/Xcode_26.5.app/Contents/Developer
• Build 17F42
• CocoaPods version 1.16.2
[✓] Chrome - develop for the web [12ms]
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Connected device (6 available) [6.9s]
• Pixel 10 (mobile) • 55161FDCR002T4 • android-arm64 • Android 17 (API 37)
• motucraft (wireless) (mobile) • 00008150-001C305A3440401C • ios • iOS 26.5.1 23F81
• iPhone 17 Pro Max (mobile) • FD600137-FDF2-41C6-8D0A-1CAAB8720772 • ios • com.apple.CoreSimulator.SimRuntime.iOS-26-5 (simulator)
• iPhone SE (3rd generation) (mobile) • 5B9AD46D-1C1B-4DE3-A8AE-01B91BD8E0F6 • ios • com.apple.CoreSimulator.SimRuntime.iOS-26-5 (simulator)
• macOS (desktop) • macos • darwin-arm64 • macOS 26.5.1 25F80 darwin-arm64
• Chrome (web) • chrome • web-javascript • Google Chrome 149.0.7827.103
[✓] Network resources [1,602ms]
• All expected network resources are available.
• No issues found!
Activity
mbcorona commentedon Jun 10, 2026
Thanks for the report — minimal repro, stack trace, and root cause all pinned down. Routing to team-android.
[camera_android_camerax] Fix setFocusMode(auto) crash when there is n…
[camera_android_camerax] Fix setFocusMode(auto) crash when there is n…
github-actions commentedon Jul 21, 2026
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.