add save as dialog for seed on desktop (#2753)
What changed, and why it matters
This commit changes how users save their wallet recovery seed on desktop computers. Previously, the app used a generic share feature. Now it opens a 'Save As' dialog and writes the seed to a plain text file named after the wallet. The seed is still shown on screen and handled by the app, so this is mainly a usability change, not a fix for a known security flaw. However, saving a seed as a plain text file on a desktop can increase the risk that the seed is accidentally left in an easy-to-find location.
Treat this as a defensive review note, not an urgent vulnerability. If the project wants to reduce risk, consider warning users that the saved seed is unencrypted, prompting for a password to encrypt the file, or defaulting to a secure app-private storage location. No immediate patch is required unless the project decides to harden the desktop seed export flow.
Security signals we found
Wallet recovery seed written to disk in plaintext
No encryption or access-control applied to saved seed file
File name includes wallet name, making seed files easy to identify
User-chosen save location may include cloud-synced or shared directories
Evidence from the diff
The patch adds platform-specific handling in WalletSeedPage._shareSeed. On Android/iOS it keeps the existing ShareUtil.share behavior. On macOS/Windows/Linux it uses file_picker’s saveFile dialog to let the user choose a path, then writes walletSeedViewModel.seed to a file named ‘
Changed components
lib/src/screens/seed/wallet_seed_page.dartDesktop seed save flow (macOS/Windows/Linux)Inspect captured patch +30 / −4
diff --git a/lib/src/screens/seed/wallet_seed_page.dart b/lib/src/screens/seed/wallet_seed_page.dart
index 1101097..b73c13c 100644
--- a/lib/src/screens/seed/wallet_seed_page.dart
+++ b/lib/src/screens/seed/wallet_seed_page.dart
@@ -1,11 +1,15 @@
+import 'dart:io';
+
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/seedphrase_grid_widget.dart';
import 'package:cake_wallet/src/widgets/warning_box_widget.dart';
import 'package:cake_wallet/utils/clipboard_util.dart';
import 'package:cake_wallet/utils/share_util.dart';
import 'package:cake_wallet/utils/show_bar.dart';
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
+import "package:file_picker/file_picker.dart";
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
@@ -104,10 +108,7 @@ class WalletSeedPage extends BasePage {
child: PrimaryButton(
key: ValueKey('wallet_seed_page_save_seeds_button_key'),
onPressed: () {
- ShareUtil.share(
- text: walletSeedViewModel.seed,
- context: context,
- );
+ _shareSeed(context);
},
text: S.of(context).save,
color: Theme.of(context).colorScheme.surfaceContainer,
@@ -139,4 +140,29 @@ class WalletSeedPage extends BasePage {
),
);
}
+
+ void _shareSeed(BuildContext context) async {
+ switch(defaultTargetPlatform) {
+ case TargetPlatform.android:
+ case TargetPlatform.iOS:
+ ShareUtil.share(
+ text: walletSeedViewModel.seed,
+ context: context,
+ );
+ break;
+ case TargetPlatform.macOS:
+ case TargetPlatform.windows:
+ case TargetPlatform.linux:
+ final path = await FilePicker.platform.saveFile(
+ dialogTitle: "Save seed as",
+ fileName: "${walletSeedViewModel.name}-seed.txt",
+ );
+ if(path != null) {
+ final file = File(path);
+ await file.writeAsString(walletSeedViewModel.seed);
+ }
+ default:
+break;
+ }
+ }
}
Why this scored 26/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.