feat: enable QR scanner for NWC wallet import#516
Conversation
Replace the 'coming soon' placeholder with a functional QR code scanner on the Connect Wallet screen. Changes: - Add mobile_scanner v7.0.1 dependency for camera-based QR scanning - Create reusable QrScannerScreen widget with URI prefix filtering, torch toggle, camera switch, and viewfinder overlay - Update ConnectWalletScreen to launch scanner with 'nostr+walletconnect://' prefix filter - Add CAMERA permission to Android manifest (iOS already had it) - Add cameraPermissionDenied localization string (EN, ES, FR, IT) - Change scan icon color from textSecondary to activeColor Closes #513
- Replace deprecated torchState ValueListenable with local state tracking (mobile_scanner v7 API change) - Fix use_build_context_synchronously warnings by capturing ScaffoldMessenger and Navigator before async gaps
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThis PR enables QR code scanning for the NWC wallet import flow. It adds a new reusable QrScannerScreen widget using the mobile_scanner package, integrates it into the Connect Wallet screen, requests Android camera permissions, and includes camera permission denial messages across English, Spanish, French, and Italian locales. Implementation documentation is provided. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant ConnectWallet as ConnectWalletScreen
participant PermHandler as PermissionHandler
participant QRScanner as QrScannerScreen
participant Camera as MobileScannerController
User->>ConnectWallet: Tap QR scan icon
ConnectWallet->>PermHandler: Request CAMERA permission
alt Permission Denied
PermHandler-->>ConnectWallet: Permission denied
ConnectWallet-->>User: Show SnackBar (cameraPermissionDenied)
else Permission Granted
PermHandler-->>ConnectWallet: Permission granted
ConnectWallet->>QRScanner: Navigate with uriPrefix filter
activate QRScanner
User->>Camera: Aim at QR code
Camera->>QRScanner: onDetect callback (barcode data)
QRScanner->>QRScanner: Apply prefix filter & validation
QRScanner-->>ConnectWallet: Pop with scanned URI
deactivate QRScanner
ConnectWallet->>ConnectWallet: Populate _uriController with result
ConnectWallet->>ConnectWallet: Clear _validationError
ConnectWallet-->>User: Display URI in text field
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
lib/features/wallet/screens/connect_wallet_screen.dart (1)
255-262: Defer the permission-deniedSnackBarto a post-frame callback.This branch fires a UI side effect directly after the async permission request. The repo guideline asks SnackBars/dialogs to be scheduled with a post-frame callback instead.
Suggested change
if (mounted) { - messenger.showSnackBar( - SnackBar( - content: Text(permDeniedMsg), - backgroundColor: AppTheme.backgroundCard, - behavior: SnackBarBehavior.floating, - ), - ); + WidgetsBinding.instance.addPostFrameCallback((_) { + if (!mounted) return; + messenger.showSnackBar( + SnackBar( + content: Text(permDeniedMsg), + backgroundColor: AppTheme.backgroundCard, + behavior: SnackBarBehavior.floating, + ), + ); + }); }As per coding guidelines, "Keep UI code declarative and side-effect free. Use post-frame callbacks for side effects like SnackBars/dialogs."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/features/wallet/screens/connect_wallet_screen.dart` around lines 255 - 262, The SnackBar is being shown immediately after an async permission request; move that UI side effect into a post-frame callback by wrapping the messenger.showSnackBar(...) call (the branch that uses permDeniedMsg and mounted) inside a WidgetsBinding.instance.addPostFrameCallback (or SchedulerBinding.instance.addPostFrameCallback) to ensure the SnackBar is scheduled after the current frame; keep the mounted check and same SnackBar content/behavior when invoking messenger.showSnackBar.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/shared/widgets/qr_scanner_screen.dart`:
- Around line 54-80: The three IconButton controls (the leading close HeroIcon,
the torch toggle IconButton using _torchOn and _controller.toggleTorch, and the
camera switch IconButton calling _controller.switchCamera) lack accessibility
labels; add localized tooltips or semantics labels by creating ARB entries (e.g.
"close", "toggleTorch", "switchCamera") and replace hard-coded strings with
S.of(context)!.close, S.of(context)!.toggleTorch, S.of(context)!.switchCamera,
then set each IconButton's tooltip: parameter (or wrap with Semantics(label:
...)) so TalkBack/VoiceOver announces the localized labels. Ensure you use the
same S.of(context) access pattern used elsewhere in this file.
---
Nitpick comments:
In `@lib/features/wallet/screens/connect_wallet_screen.dart`:
- Around line 255-262: The SnackBar is being shown immediately after an async
permission request; move that UI side effect into a post-frame callback by
wrapping the messenger.showSnackBar(...) call (the branch that uses
permDeniedMsg and mounted) inside a WidgetsBinding.instance.addPostFrameCallback
(or SchedulerBinding.instance.addPostFrameCallback) to ensure the SnackBar is
scheduled after the current frame; keep the mounted check and same SnackBar
content/behavior when invoking messenger.showSnackBar.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 430c7be5-1118-49d7-a841-27dff3bc27a3
📒 Files selected for processing (9)
android/app/src/main/AndroidManifest.xmldocs/QR_SCANNER_NWC_IMPLEMENTATION.mdlib/features/wallet/screens/connect_wallet_screen.dartlib/l10n/intl_en.arblib/l10n/intl_es.arblib/l10n/intl_fr.arblib/l10n/intl_it.arblib/shared/widgets/qr_scanner_screen.dartpubspec.yaml
| leading: IconButton( | ||
| icon: const HeroIcon(HeroIcons.xMark, color: Colors.white), | ||
| onPressed: () => context.pop(), | ||
| ), | ||
| title: Text( | ||
| S.of(context)!.scanQrCode, | ||
| style: const TextStyle( | ||
| color: Colors.white, | ||
| fontSize: 18, | ||
| fontWeight: FontWeight.w600, | ||
| ), | ||
| ), | ||
| actions: [ | ||
| IconButton( | ||
| icon: Icon( | ||
| _torchOn ? Icons.flash_on : Icons.flash_off, | ||
| color: _torchOn ? AppTheme.activeColor : Colors.white, | ||
| ), | ||
| onPressed: () async { | ||
| await _controller.toggleTorch(); | ||
| setState(() => _torchOn = !_torchOn); | ||
| }, | ||
| ), | ||
| IconButton( | ||
| icon: const Icon(Icons.cameraswitch, color: Colors.white), | ||
| onPressed: () => _controller.switchCamera(), | ||
| ), |
There was a problem hiding this comment.
Add localized labels to the scanner action buttons.
These three icon-only controls are currently unlabeled, so TalkBack/VoiceOver will expose them as generic buttons. Please add localized tooltips or explicit semantics labels for close, torch, and camera switch before shipping this screen.
As per coding guidelines, "Localize all user-facing strings via ARB files and access them with S.of(context) rather than hard-coded string literals."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/shared/widgets/qr_scanner_screen.dart` around lines 54 - 80, The three
IconButton controls (the leading close HeroIcon, the torch toggle IconButton
using _torchOn and _controller.toggleTorch, and the camera switch IconButton
calling _controller.switchCamera) lack accessibility labels; add localized
tooltips or semantics labels by creating ARB entries (e.g. "close",
"toggleTorch", "switchCamera") and replace hard-coded strings with
S.of(context)!.close, S.of(context)!.toggleTorch, S.of(context)!.switchCamera,
then set each IconButton's tooltip: parameter (or wrap with Semantics(label:
...)) so TalkBack/VoiceOver announces the localized labels. Ensure you use the
same S.of(context) access pattern used elsewhere in this file.
…timing - Add localized tooltips to QR scanner IconButtons (close, toggleTorch, switchCamera) for TalkBack/VoiceOver accessibility - Add toggleTorch and switchCamera localization strings (EN, ES, FR, IT) - Wrap permission-denied SnackBar in addPostFrameCallback to ensure it schedules after the current frame completes
Summary
Replaces the "coming soon" placeholder with a functional QR code scanner on the Connect Wallet screen, allowing users to scan
nostr+walletconnect://URIs from compatible wallets (Alby, Mutiny, etc.).Closes #513
Changes
New:
QrScannerScreen(lib/shared/widgets/qr_scanner_screen.dart)mobile_scannerv7.0.1uriPrefixparameter filters scanned codes (only NWC URIs accepted)Navigator.pop()Updated:
ConnectWalletScreen_showQrComingSoon()replaced with_openQrScanner()— requests camera permission, launches scanner, auto-fills URI field on successful scantextSecondary(gray) toactiveColor(green) to indicate it is now functionalPlatform Config
CAMERApermission toAndroidManifest.xmlNSCameraUsageDescriptionwas already presentLocalization
cameraPermissionDeniedstring in EN, ES, FR, IT ARB filesDependency
mobile_scanner: ^7.0.1— uses CameraX (Android) and AVFoundation (iOS)Documentation
docs/QR_SCANNER_NWC_IMPLEMENTATION.mdexplaining implementation decisionsUX Flow
Testing
Summary by CodeRabbit
New Features
Documentation
Localization