Give ConfirmSwiper an accessible-navigation button fallback (#3464)
Seth For Privacy committed
Aug 10, 2026 at 21:15 UTC
8ed08fb10e88463c745fd00a8349a4d4d0a4fbec
5 files changed
+38
-3
lib/new-ui/pages/bridge/bridge_confirm_sheet.dart
+1
@@ -218,6 +218,7 @@ class _BridgeConfirmSheetState extends State<BridgeConfirmSheet> {
218
bridgeViewModel.executeBridge();
219
},
220
swiperText: S.of(context).swipe_to_bridge,
221
+ accessibleNavigationModeButtonText: S.of(context).confirm,
222
);
223
},
224
),
lib/new-ui/widgets/confirm_swiper.dart
+33
-2
@@ -1,12 +1,23 @@
1
import 'dart:math';
2
3
+import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
4
import 'package:flutter/material.dart';
5
6
class ConfirmSwiper extends StatefulWidget {
7
final VoidCallback onConfirmed;
8
final String swiperText;
9
9
- const ConfirmSwiper({super.key, required this.onConfirmed, required this.swiperText});
10
+ /// Label of the plain button rendered instead of the swiper when the platform
11
+ /// reports accessible navigation (VoiceOver / TalkBack), which intercepts the
12
+ /// horizontal drag the swiper depends on. Falls back to [swiperText].
13
+ final String? accessibleNavigationModeButtonText;
14
+
15
+ const ConfirmSwiper({
16
+ super.key,
17
+ required this.onConfirmed,
18
+ required this.swiperText,
19
+ this.accessibleNavigationModeButtonText,
20
+ });
21
22
@override
23
State<ConfirmSwiper> createState() => _ConfirmSwiperState();
@@ -30,6 +41,15 @@ class _ConfirmSwiperState extends State<ConfirmSwiper> {
41
42
@override
43
Widget build(BuildContext context) {
44
+ if (MediaQuery.of(context).accessibleNavigation) {
45
+ return NewPrimaryButton(
46
+ onPressed: widget.onConfirmed,
47
+ text: widget.accessibleNavigationModeButtonText ?? widget.swiperText,
48
+ color: Theme.of(context).colorScheme.primary,
49
+ textColor: Theme.of(context).colorScheme.onPrimary,
50
+ );
51
+ }
52
+
53
final radius = (pillSize + pillHorizontalPadding * 2) / 2;
54
55
return LayoutBuilder(
@@ -41,7 +61,7 @@ class _ConfirmSwiperState extends State<ConfirmSwiper> {
61
final maxDrag = areaWidth - pillSize - pillHorizontalPadding;
62
final triggerAt = maxDrag - _triggerThreshold;
63
44
- return GestureDetector(
64
+ final swiper = GestureDetector(
65
onHorizontalDragUpdate: (d) {
66
setState(() {
67
drag = max(pillHorizontalPadding, min(maxDrag, drag + d.delta.dx));
@@ -98,6 +118,17 @@ class _ConfirmSwiperState extends State<ConfirmSwiper> {
118
),
119
),
120
);
121
+
122
+ // A single accessibility node for the whole control: the flowing label and
123
+ // the arrow knob are decorative and must not become separate stops. No
124
+ // semantics action is exposed here; a screen reader gets the button
125
+ // variant above instead.
126
+ return Semantics(
127
+ container: true,
128
+ excludeSemantics: true,
129
+ label: widget.swiperText,
130
+ child: swiper,
131
+ );
132
},
133
);
134
}
lib/new-ui/widgets/send_page/send_confirm_bottom_widget.dart
+2
-1
@@ -47,7 +47,8 @@ class SendConfirmBottomWidget extends StatelessWidget {
47
onConfirmed: () {
48
sendViewModel.commitTransaction(context);
49
},
50
- swiperText: "${S.of(context).swipe_to_send}");
50
+ swiperText: "${S.of(context).swipe_to_send}",
51
+ accessibleNavigationModeButtonText: S.of(context).send);
52
case IsExecutingState:
53
return LoadingBottomWidget(
54
text: "${S.of(context).generating_transaction}...",
lib/src/screens/wallet_connect/widgets/wc_connection_request_sheet.dart
+1
@@ -80,6 +80,7 @@ class WCConnectionRequestSheet extends StatelessWidget {
80
padding: const EdgeInsets.symmetric(horizontal: 24),
81
child: ConfirmSwiper(
82
swiperText: S.of(context).wc_swipe_to_approve,
83
+ accessibleNavigationModeButtonText: S.of(context).wc_action_approve,
84
onConfirmed: () {
85
if (Navigator.canPop(context)) {
86
Navigator.of(context).pop(WCBottomSheetResult.one);
lib/src/screens/wallet_connect/widgets/wc_signing_request_sheet.dart
+1
@@ -82,6 +82,7 @@ class WCSigningRequestSheet extends StatelessWidget {
82
padding: const EdgeInsets.symmetric(horizontal: 24),
83
child: ConfirmSwiper(
84
swiperText: swipeLabel,
85
+ accessibleNavigationModeButtonText: S.of(context).confirm,
86
onConfirmed: () {
87
if (Navigator.canPop(context)) {
88
Navigator.of(context).pop(WCBottomSheetResult.one);