diff --git a/lib/ui/screens/account_receive_screen.dart b/lib/ui/screens/account_receive_screen.dart index c67a263..c1fd035 100644 --- a/lib/ui/screens/account_receive_screen.dart +++ b/lib/ui/screens/account_receive_screen.dart @@ -37,6 +37,9 @@ class _AccountReceiveScreenState extends ConsumerState { bool _scannerActive = false; MobileScannerController? _scannerController; + // True when the scanner plugin fails to initialise at runtime (e.g. + // MissingPluginException on some Android builds). + bool _scannerFailed = false; @override void initState() { @@ -76,8 +79,35 @@ class _AccountReceiveScreenState extends ConsumerState { setState(() { _step = _Step.scanning; _scannerActive = true; - _scannerController = MobileScannerController(); }); + if (_cameraScanSupported()) { + unawaited(_initScanner()); + } + } + + // Pre-flight: start + stop the scanner to verify the plugin is available. + // Falls back to text entry on any exception (including MissingPluginException). + Future _initScanner() async { + MobileScannerController? ctrl; + bool available = false; + try { + ctrl = MobileScannerController(); + await ctrl.start(); + await ctrl.stop(); + available = true; + } catch (_) { + // Plugin not available on this device; text fallback will be shown. + } finally { + try { + await ctrl?.dispose(); + } catch (_) {} + } + if (!mounted) return; + if (available) { + setState(() => _scannerController = MobileScannerController()); + } else { + setState(() => _scannerFailed = true); + } } Future _onScanned(String rawValue) async { @@ -266,11 +296,14 @@ class _AccountReceiveScreenState extends ConsumerState { } Widget _buildScannerView(BuildContext context) { - // On platforms where the camera scanner is not available (Linux desktop), - // fall back to a text-input field. - if (!_cameraScanSupported()) { + // Fall back to text input when the platform has no camera support or when + // the scanner plugin fails to initialise at runtime (MissingPluginException). + if (!_cameraScanSupported() || _scannerFailed) { return _buildTextFallbackView(context); } + if (_scannerController == null) { + return const Center(child: CircularProgressIndicator()); + } return Stack( children: [ diff --git a/lib/ui/screens/account_send_screen.dart b/lib/ui/screens/account_send_screen.dart index 34c8620..59e3548 100644 --- a/lib/ui/screens/account_send_screen.dart +++ b/lib/ui/screens/account_send_screen.dart @@ -45,12 +45,40 @@ class _AccountSendScreenState extends ConsumerState { bool _scannerActive = true; MobileScannerController? _scannerController; + // True when the scanner plugin fails to initialise at runtime (e.g. + // MissingPluginException on some Android builds). + bool _scannerFailed = false; @override void initState() { super.initState(); if (_cameraScanSupported()) { - _scannerController = MobileScannerController(); + unawaited(_initScanner()); + } + } + + // Pre-flight: start + stop the scanner to verify the plugin is available. + // Falls back to text entry on any exception (including MissingPluginException). + Future _initScanner() async { + MobileScannerController? ctrl; + bool available = false; + try { + ctrl = MobileScannerController(); + await ctrl.start(); + await ctrl.stop(); + available = true; + } catch (_) { + // Plugin not available on this device; text fallback will be shown. + } finally { + try { + await ctrl?.dispose(); + } catch (_) {} + } + if (!mounted) return; + if (available) { + setState(() => _scannerController = MobileScannerController()); + } else { + setState(() => _scannerFailed = true); } } @@ -178,9 +206,12 @@ class _AccountSendScreenState extends ConsumerState { } Widget _buildScanStep(BuildContext context) { - if (!_cameraScanSupported()) { + if (!_cameraScanSupported() || _scannerFailed) { return _buildTextFallbackView(context); } + if (_scannerController == null) { + return const Center(child: CircularProgressIndicator()); + } return Stack( children: [