fix(shopinbit): ShopInBit confirm/send navigation and display fixes
What changed, and why it matters
This commit fixes two user-interface bugs in the Stack Wallet app's ShopInBit payment flow. First, it prevents the app from crashing or showing a confusing error dialog if the user navigates away while a transaction is being broadcast. Second, it correctly displays the token symbol (for example, a stablecoin ticker) on the desktop send-confirmation screen. There is no indication these changes address a security vulnerability.
No security action required. Treat as a normal bug-fix/UI-hardening commit. Reviewers may optionally verify that the context.mounted guard pattern is applied consistently elsewhere in async post-frame callbacks.
Security signals we found
No security-relevant keywords or patterns in commit title/message
Changes are purely UI/navigation hardening
No input validation, cryptography, authentication, or authorization changes
No network or transaction-logic changes
Evidence from the diff
The patch makes three small Flutter/Dart UI fixes in the ShopInBit send flow: (1) wraps a post-broadcast Navigator.pop() and error dialog in a context.mounted guard so async error handling does not use a stale BuildContext; (2) moves the bool wasCancelled declaration outside the try block so its value is available in the catch block; and (3) adds a tokenTicker parameter to DesktopAuthSend so the confirmation view can display the correct token contract symbol. The catch block is also guarded by !wasCancelled to avoid showing a failure dialog when the user simply cancelled the operation.
Changed components
lib/pages/shopinbit/shopinbit_confirm_send_view.dartlib/pages/shopinbit/shopinbit_send_from_view.dartInspect captured patch +36 / −31
diff --git a/lib/pages/shopinbit/shopinbit_confirm_send_view.dart b/lib/pages/shopinbit/shopinbit_confirm_send_view.dart
index 6a512fd..17a900e 100644
--- a/lib/pages/shopinbit/shopinbit_confirm_send_view.dart
+++ b/lib/pages/shopinbit/shopinbit_confirm_send_view.dart
@@ -146,36 +146,38 @@ class _ShopInBitConfirmSendViewState
stackTrace: s,
);
- // pop sending dialog (pushed via showDialog which uses root navigator)
- Navigator.of(context, rootNavigator: true).pop();
+ if (context.mounted) {
+ // pop sending dialog (pushed via showDialog which uses root navigator)
+ Navigator.of(context, rootNavigator: true).pop();
- await showDialog<dynamic>(
- context: context,
- useSafeArea: false,
- barrierDismissible: true,
- builder: (context) {
- return StackDialog(
- title: "Broadcast transaction failed",
- message: e.toString(),
- rightButton: TextButton(
- style: Theme.of(context)
- .extension<StackColors>()!
- .getSecondaryEnabledButtonStyle(context),
- child: Text(
- "Ok",
- style: STextStyles.button(context).copyWith(
- color: Theme.of(
- context,
- ).extension<StackColors>()!.buttonTextSecondary,
+ await showDialog<dynamic>(
+ context: context,
+ useSafeArea: false,
+ barrierDismissible: true,
+ builder: (context) {
+ return StackDialog(
+ title: "Broadcast transaction failed",
+ message: e.toString(),
+ rightButton: TextButton(
+ style: Theme.of(context)
+ .extension<StackColors>()!
+ .getSecondaryEnabledButtonStyle(context),
+ child: Text(
+ "Ok",
+ style: STextStyles.button(context).copyWith(
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.buttonTextSecondary,
+ ),
),
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
),
- onPressed: () {
- Navigator.of(context).pop();
- },
- ),
- );
- },
- );
+ );
+ },
+ );
+ }
}
}
@@ -199,7 +201,10 @@ class _ShopInBitConfirmSendViewState
),
Padding(
padding: const EdgeInsets.only(left: 32, right: 32, bottom: 32),
- child: DesktopAuthSend(coin: coin),
+ child: DesktopAuthSend(
+ coin: coin,
+ tokenTicker: widget.tokenContract?.symbol,
+ ),
),
],
),
diff --git a/lib/pages/shopinbit/shopinbit_send_from_view.dart b/lib/pages/shopinbit/shopinbit_send_from_view.dart
index 6ed9bdf..716b006 100644
--- a/lib/pages/shopinbit/shopinbit_send_from_view.dart
+++ b/lib/pages/shopinbit/shopinbit_send_from_view.dart
@@ -279,9 +279,9 @@ class _ShopInBitSendFromCardState extends ConsumerState<ShopInBitSendFromCard> {
}
}
- try {
- bool wasCancelled = false;
+ bool wasCancelled = false;
+ try {
final parentWallet = ref.read(pWallets).getWallet(walletId);
unawaited(
@@ -384,7 +384,7 @@ class _ShopInBitSendFromCardState extends ConsumerState<ShopInBitSendFromCard> {
}
} catch (e, s) {
Logging.instance.e("$e\n$s", error: e, stackTrace: s);
- if (mounted) {
+ if (mounted && !wasCancelled) {
Navigator.of(context, rootNavigator: true).pop();
await showDialog<dynamic>(
Why this scored 16/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.