fix(ui): more navigation and layout/styling cleanup
What changed, and why it matters
This commit is a routine user-interface cleanup for a shopping feature inside the Stack Wallet app. It swaps some dialog widgets, adjusts button layouts, and makes desktop and mobile navigation behave the same way. There is no indication it fixes a security problem.
No security action needed; review as normal UI refactor.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors the ShopinBit desktop/mobile flow to use a shared SDialog and a nested navigator route generator instead of conditional showDialog vs. pushNamed branches. It adds route registrations for ShopInBitOfferView and ShopInBitShippingView, removes DesktopDialog usage, and tweaks Column/Spacer/ScrollView sizing. No security-sensitive logic, input handling, cryptography, or permissions are changed.
Changed components
lib/pages/shopinbit/shopinbit_offer_view.dartlib/pages/shopinbit/shopinbit_shipping_view.dartlib/pages/shopinbit/shopinbit_ticket_detail.dartlib/pages/shopinbit/shopinbit_tickets_view.dartlib/widgets/dialogs/nested_navigator_dialog/nested_navigator_dialog_route_generator.dartInspect captured patch +144 / −115
diff --git a/lib/pages/shopinbit/shopinbit_offer_view.dart b/lib/pages/shopinbit/shopinbit_offer_view.dart
index 98946c1..544a554 100644
--- a/lib/pages/shopinbit/shopinbit_offer_view.dart
+++ b/lib/pages/shopinbit/shopinbit_offer_view.dart
@@ -7,11 +7,12 @@ import '../../themes/stack_colors.dart';
import '../../utilities/text_styles.dart';
import '../../utilities/util.dart';
import '../../widgets/background.dart';
+import '../../widgets/conditional_parent.dart';
import '../../widgets/custom_buttons/app_bar_icon_button.dart';
-import '../../widgets/desktop/desktop_dialog.dart';
import '../../widgets/desktop/desktop_dialog_close_button.dart';
import '../../widgets/desktop/primary_button.dart';
import '../../widgets/desktop/secondary_button.dart';
+import '../../widgets/dialogs/s_dialog.dart';
import '../../widgets/loading_indicator.dart';
import '../../widgets/rounded_white_container.dart';
import 'shopinbit_shipping_view.dart';
@@ -65,6 +66,7 @@ class _ShopInBitOfferViewState extends ConsumerState<ShopInBitOfferView> {
final model = widget.model;
final content = Column(
+ mainAxisSize: .min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
@@ -124,73 +126,86 @@ class _ShopInBitOfferViewState extends ConsumerState<ShopInBitOfferView> {
],
),
),
- const Spacer(),
- PrimaryButton(
- label: "Accept offer",
- enabled: !_loading,
- onPressed: () {
- model.status = ShopInBitOrderStatus.accepted;
- if (isDesktop) {
- Navigator.of(context, rootNavigator: true).pop();
- showDialog<void>(
- context: context,
- builder: (_) => ShopInBitShippingView(model: model),
- );
- } else {
- Navigator.of(
- context,
- ).pushNamed(ShopInBitShippingView.routeName, arguments: model);
- }
- },
- ),
- SizedBox(height: isDesktop ? 16 : 12),
- SecondaryButton(
- label: "Decline",
- onPressed: () {
- if (isDesktop) {
- Navigator.of(context, rootNavigator: true).pop();
- } else {
- Navigator.of(context).pop();
- }
- },
+ isDesktop ? const SizedBox(height: 40) : const Spacer(),
+ BranchedParent(
+ condition: isDesktop,
+ conditionBranchBuilder: (children) => Row(
+ children: [
+ Expanded(child: children[1]),
+ const SizedBox(width: 16),
+ Expanded(child: children[0]),
+ ],
+ ),
+ otherBranchBuilder: (children) => Column(
+ mainAxisSize: .min,
+ crossAxisAlignment: .stretch,
+ children: [children[0], const SizedBox(height: 16), children[1]],
+ ),
+ children: [
+ PrimaryButton(
+ label: "Accept offer",
+ buttonHeight: Util.isDesktop ? ButtonHeight.l : null,
+ enabled: !_loading,
+ onPressed: () {
+ // TODO verify this is ok to stay set to accepted if the next route pops back and then decline is tapped
+ model.status = ShopInBitOrderStatus.accepted;
+
+ Navigator.of(
+ context,
+ ).pushNamed(ShopInBitShippingView.routeName, arguments: model);
+ },
+ ),
+ SecondaryButton(
+ label: "Decline",
+ buttonHeight: Util.isDesktop ? ButtonHeight.l : null,
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ ),
+ ],
),
],
);
if (isDesktop) {
- return DesktopDialog(
- maxWidth: 580,
- maxHeight: 600,
- child: Column(
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Padding(
- padding: const EdgeInsets.only(left: 32),
- child: Text(
- "ShopinBit",
- style: STextStyles.desktopH3(context),
+ return SDialog(
+ child: SizedBox(
+ width: 580,
+ child: Column(
+ mainAxisSize: .min,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(left: 32),
+ child: Text(
+ "ShopinBit",
+ style: STextStyles.desktopH3(context),
+ ),
+ ),
+ const DesktopDialogCloseButton(),
+ ],
+ ),
+ Flexible(
+ child: Padding(
+ padding: const .only(
+ left: 32,
+ right: 32,
+ bottom: 32,
+ top: 16,
+ ),
+ child: Stack(
+ children: [
+ content,
+ if (_loading)
+ const LoadingIndicator(width: 24, height: 24),
+ ],
),
- ),
- const DesktopDialogCloseButton(),
- ],
- ),
- Expanded(
- child: Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 32,
- vertical: 16,
- ),
- child: Stack(
- children: [
- content,
- if (_loading) const LoadingIndicator(width: 24, height: 24),
- ],
),
),
- ),
- ],
+ ],
+ ),
),
);
}
diff --git a/lib/pages/shopinbit/shopinbit_shipping_view.dart b/lib/pages/shopinbit/shopinbit_shipping_view.dart
index ccebcb3..298e293 100644
--- a/lib/pages/shopinbit/shopinbit_shipping_view.dart
+++ b/lib/pages/shopinbit/shopinbit_shipping_view.dart
@@ -15,9 +15,9 @@ import '../../utilities/text_styles.dart';
import '../../utilities/util.dart';
import '../../widgets/background.dart';
import '../../widgets/custom_buttons/app_bar_icon_button.dart';
-import '../../widgets/desktop/desktop_dialog.dart';
import '../../widgets/desktop/desktop_dialog_close_button.dart';
import '../../widgets/desktop/primary_button.dart';
+import '../../widgets/dialogs/s_dialog.dart';
import '../../widgets/textfields/adaptive_text_field.dart';
import 'shopinbit_payment_view.dart';
@@ -235,21 +235,12 @@ class _ShopInBitShippingViewState extends ConsumerState<ShopInBitShippingView> {
}
if (!mounted) return;
- if (Util.isDesktop) {
- Navigator.of(context, rootNavigator: true).pop();
- unawaited(
- showDialog<void>(
- context: context,
- builder: (_) => ShopInBitPaymentView(model: widget.model),
- ),
- );
- } else {
- unawaited(
- Navigator.of(
- context,
- ).pushNamed(ShopInBitPaymentView.routeName, arguments: widget.model),
- );
- }
+
+ unawaited(
+ Navigator.of(
+ context,
+ ).pushNamed(ShopInBitPaymentView.routeName, arguments: widget.model),
+ );
}
@override
@@ -258,6 +249,7 @@ class _ShopInBitShippingViewState extends ConsumerState<ShopInBitShippingView> {
final spacing = SizedBox(height: isDesktop ? 16 : 12);
final content = Column(
+ mainAxisSize: .min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
@@ -666,34 +658,38 @@ class _ShopInBitShippingViewState extends ConsumerState<ShopInBitShippingView> {
);
if (isDesktop) {
- return DesktopDialog(
- maxWidth: 580,
- maxHeight: 700,
- child: Column(
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Padding(
- padding: const EdgeInsets.only(left: 32),
- child: Text(
- "ShopinBit",
- style: STextStyles.desktopH3(context),
+ return SDialog(
+ child: SizedBox(
+ width: 580,
+ child: Column(
+ mainAxisSize: .min,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(left: 32),
+ child: Text(
+ "ShopinBit",
+ style: STextStyles.desktopH3(context),
+ ),
),
+ const DesktopDialogCloseButton(),
+ ],
+ ),
+ Flexible(
+ child: Padding(
+ padding: const .only(
+ left: 32,
+ right: 32,
+ bottom: 32,
+ top: 16,
+ ),
+ child: SingleChildScrollView(child: content),
),
- const DesktopDialogCloseButton(),
- ],
- ),
- Expanded(
- child: Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 32,
- vertical: 16,
- ),
- child: SingleChildScrollView(child: content),
),
- ),
- ],
+ ],
+ ),
),
);
}
diff --git a/lib/pages/shopinbit/shopinbit_ticket_detail.dart b/lib/pages/shopinbit/shopinbit_ticket_detail.dart
index dc5127c..6251f62 100644
--- a/lib/pages/shopinbit/shopinbit_ticket_detail.dart
+++ b/lib/pages/shopinbit/shopinbit_ticket_detail.dart
@@ -417,19 +417,10 @@ class _ShopInBitTicketDetailState extends ConsumerState<ShopInBitTicketDetail> {
PrimaryButton(
label: "Review offer",
onPressed: () {
- if (isDesktop) {
- Navigator.of(context, rootNavigator: true).pop();
- showDialog<void>(
- context: context,
-
- builder: (_) => ShopInBitOfferView(model: model),
- );
- } else {
- Navigator.of(context).pushNamed(
- ShopInBitOfferView.routeName,
- arguments: model,
- );
- }
+ Navigator.of(context).pushNamed(
+ ShopInBitOfferView.routeName,
+ arguments: model,
+ );
},
),
],
diff --git a/lib/pages/shopinbit/shopinbit_tickets_view.dart b/lib/pages/shopinbit/shopinbit_tickets_view.dart
index 71445ae..8226f81 100644
--- a/lib/pages/shopinbit/shopinbit_tickets_view.dart
+++ b/lib/pages/shopinbit/shopinbit_tickets_view.dart
@@ -78,7 +78,6 @@ class _ShopInBitTicketsViewState extends ConsumerState<ShopInBitTicketsView> {
final model = ShopInBitOrderModel.fromDriftRow(pending);
final expiresAt = pending.carResearchExpiresAt;
final linksJson = pending.carResearchPaymentLinks;
- final isDesktop = Util.isDesktop;
if (expiresAt != null &&
expiresAt.isAfter(DateTime.now()) &&
diff --git a/lib/widgets/dialogs/nested_navigator_dialog/nested_navigator_dialog_route_generator.dart b/lib/widgets/dialogs/nested_navigator_dialog/nested_navigator_dialog_route_generator.dart
index 235aca4..eb0fa8e 100644
--- a/lib/widgets/dialogs/nested_navigator_dialog/nested_navigator_dialog_route_generator.dart
+++ b/lib/widgets/dialogs/nested_navigator_dialog/nested_navigator_dialog_route_generator.dart
@@ -9,7 +9,9 @@ import '../../../pages/cakepay/cakepay_orders_view.dart';
import '../../../pages/cakepay/cakepay_vendors_view.dart';
import '../../../pages/shopinbit/shopinbit_car_fee_view.dart';
import '../../../pages/shopinbit/shopinbit_car_research_payment_view.dart';
+import '../../../pages/shopinbit/shopinbit_offer_view.dart';
import '../../../pages/shopinbit/shopinbit_order_created.dart';
+import '../../../pages/shopinbit/shopinbit_shipping_view.dart';
import '../../../pages/shopinbit/shopinbit_step_1.dart';
import '../../../pages/shopinbit/shopinbit_step_2.dart';
import '../../../pages/shopinbit/shopinbit_step_3.dart';
@@ -166,6 +168,32 @@ abstract final class NestedNavigatorDialogRouteGenerator {
"Expected ShopInBitOrderModel",
);
+ case ShopInBitOfferView.routeName:
+ if (args is ShopInBitOrderModel) {
+ return getRoute(
+ builder: (_) => ShopInBitOfferView(model: args),
+ settings: RouteSettings(name: settings.name),
+ );
+ }
+ return _routeError(
+ "${settings.name} invalid args\n"
+ "Got ${args.runtimeType}\n"
+ "Expected ShopInBitOrderModel",
+ );
+
+ case ShopInBitShippingView.routeName:
+ if (args is ShopInBitOrderModel) {
+ return getRoute(
+ builder: (_) => ShopInBitShippingView(model: args),
+ settings: RouteSettings(name: settings.name),
+ );
+ }
+ return _routeError(
+ "${settings.name} invalid args\n"
+ "Got ${args.runtimeType}\n"
+ "Expected ShopInBitOrderModel",
+ );
+
case CakePayVendorsView.routeName:
return getRoute(
builder: (_) => const CakePayVendorsView(),
Why this scored 15/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.