From f6566e3bafcd8d6cc29caf072eb3db98bce1273d Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Sun, 2 Aug 2026 09:43:46 +0200 Subject: [PATCH] Enforce passphrase confirmation match on the restore and create wallet flows (#3474) * fix: enforce passphrase confirmation in restore passphrase bottom sheet The confirm-passphrase field in AddPassphraseBottomSheet already had a validator comparing it to the first field, but the fields were not inside a Form and nothing ever called validate(), so the validator never ran. Tapping "Restore" restored the wallet with the first field's value even when the confirmation did not match, defeating the typo check. Wrap both fields in a Form and validate it before completing the restore, matching the existing pattern in advanced_privacy_settings_page.dart. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_019DtLNfYw1H81p7zZrkNDXM * Validate passphrase confirmation when the passphrase field is empty --------- Co-authored-by: Claude --- .../advanced_privacy_settings_page.dart | 8 +- .../add_passphrase_bottom_sheet_widget.dart | 103 ++++++++++-------- 2 files changed, 62 insertions(+), 49 deletions(-) diff --git a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart index 76683bb3b..2f190c857 100644 --- a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart +++ b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart @@ -360,11 +360,9 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo widget.nodeViewModel.save(); } - if (passphraseController.text.isNotEmpty) { - if (_passphraseFormKey.currentState != null && - !_passphraseFormKey.currentState!.validate()) { - return; - } + if (_passphraseFormKey.currentState != null && + !_passphraseFormKey.currentState!.validate()) { + return; } widget.seedTypeViewModel.setPassphrase(passphraseController.text); diff --git a/lib/src/widgets/bottom_sheet/add_passphrase_bottom_sheet_widget.dart b/lib/src/widgets/bottom_sheet/add_passphrase_bottom_sheet_widget.dart index dde80d139..c1d1f1dd4 100644 --- a/lib/src/widgets/bottom_sheet/add_passphrase_bottom_sheet_widget.dart +++ b/lib/src/widgets/bottom_sheet/add_passphrase_bottom_sheet_widget.dart @@ -20,6 +20,7 @@ class AddPassphraseBottomSheet extends StatefulWidget { class _AddPassphraseBottomSheetState extends State { late final TextEditingController passphraseController; late final TextEditingController confirmPassphraseController; + final _passphraseFormKey = GlobalKey(); @override void initState() { @@ -112,51 +113,60 @@ class _AddPassphraseBottomSheetState extends State { ), ), SizedBox(height: 24), - BaseTextFormField( - key: ValueKey('add_passphrase_bottom_sheet_widget_passphrase_textfield_key'), - controller: passphraseController, - obscureText: obscurePassphrase, - contentPadding: EdgeInsets.symmetric(horizontal: 12), - hintText: S.of(context).required_passphrase, - suffixIcon: GestureDetector( - onTap: () { - setState(() { - obscurePassphrase = !obscurePassphrase; - }); - }, - child: Icon( - obscurePassphrase ? Icons.visibility_off : Icons.visibility, - size: 24, - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), - ), - ), - ), - SizedBox(height: 8), - BaseTextFormField( - key: ValueKey('add_passphrase_bottom_sheet_widget_confirm_passphrase_textfield_key'), - controller: confirmPassphraseController, - obscureText: obscurePassphrase, - contentPadding: EdgeInsets.symmetric(horizontal: 12), - hintText: S.of(context).confirm_passphrase, - suffixIcon: GestureDetector( - onTap: () { - setState(() { - obscurePassphrase = !obscurePassphrase; - }); - }, - child: Icon( - obscurePassphrase ? Icons.visibility_off : Icons.visibility, - size: 24, - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), - ), - ), - validator: (text) { - if (text == passphraseController.text) { - return null; - } + Form( + key: _passphraseFormKey, + child: Column( + children: [ + BaseTextFormField( + key: ValueKey('add_passphrase_bottom_sheet_widget_passphrase_textfield_key'), + controller: passphraseController, + obscureText: obscurePassphrase, + contentPadding: EdgeInsets.symmetric(horizontal: 12), + hintText: S.of(context).required_passphrase, + suffixIcon: GestureDetector( + onTap: () { + setState(() { + obscurePassphrase = !obscurePassphrase; + }); + }, + child: Icon( + obscurePassphrase ? Icons.visibility_off : Icons.visibility, + size: 24, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), + ), + ), + ), + SizedBox(height: 8), + BaseTextFormField( + key: ValueKey( + 'add_passphrase_bottom_sheet_widget_confirm_passphrase_textfield_key', + ), + controller: confirmPassphraseController, + obscureText: obscurePassphrase, + contentPadding: EdgeInsets.symmetric(horizontal: 12), + hintText: S.of(context).confirm_passphrase, + suffixIcon: GestureDetector( + onTap: () { + setState(() { + obscurePassphrase = !obscurePassphrase; + }); + }, + child: Icon( + obscurePassphrase ? Icons.visibility_off : Icons.visibility, + size: 24, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), + ), + ), + validator: (text) { + if (text == passphraseController.text) { + return null; + } - return S.of(context).passphrases_doesnt_match; - }, + return S.of(context).passphrases_doesnt_match; + }, + ), + ], + ), ), SizedBox(height: 16), Padding( @@ -184,6 +194,11 @@ class _AddPassphraseBottomSheetState extends State { child: PrimaryButton( key: ValueKey('add_passphrase_bottom_sheet_widget_restore_button_key'), onPressed: () { + if (_passphraseFormKey.currentState != null && + !_passphraseFormKey.currentState!.validate()) { + return; + } + Navigator.pop(context); widget.onRestoreButtonPressed(passphraseController.text); },