diff --git a/lib/new-ui/pages/bridge/bridge_confirm_sheet.dart b/lib/new-ui/pages/bridge/bridge_confirm_sheet.dart index 4ccbfb4e7..96a8c6467 100644 --- a/lib/new-ui/pages/bridge/bridge_confirm_sheet.dart +++ b/lib/new-ui/pages/bridge/bridge_confirm_sheet.dart @@ -218,6 +218,7 @@ class _BridgeConfirmSheetState extends State { bridgeViewModel.executeBridge(); }, swiperText: S.of(context).swipe_to_bridge, + accessibleNavigationModeButtonText: S.of(context).confirm, ); }, ), diff --git a/lib/new-ui/widgets/confirm_swiper.dart b/lib/new-ui/widgets/confirm_swiper.dart index c023c546a..05ebd8b9c 100644 --- a/lib/new-ui/widgets/confirm_swiper.dart +++ b/lib/new-ui/widgets/confirm_swiper.dart @@ -1,12 +1,23 @@ import 'dart:math'; +import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart'; import 'package:flutter/material.dart'; class ConfirmSwiper extends StatefulWidget { final VoidCallback onConfirmed; final String swiperText; - const ConfirmSwiper({super.key, required this.onConfirmed, required this.swiperText}); + /// Label of the plain button rendered instead of the swiper when the platform + /// reports accessible navigation (VoiceOver / TalkBack), which intercepts the + /// horizontal drag the swiper depends on. Falls back to [swiperText]. + final String? accessibleNavigationModeButtonText; + + const ConfirmSwiper({ + super.key, + required this.onConfirmed, + required this.swiperText, + this.accessibleNavigationModeButtonText, + }); @override State createState() => _ConfirmSwiperState(); @@ -30,6 +41,15 @@ class _ConfirmSwiperState extends State { @override Widget build(BuildContext context) { + if (MediaQuery.of(context).accessibleNavigation) { + return NewPrimaryButton( + onPressed: widget.onConfirmed, + text: widget.accessibleNavigationModeButtonText ?? widget.swiperText, + color: Theme.of(context).colorScheme.primary, + textColor: Theme.of(context).colorScheme.onPrimary, + ); + } + final radius = (pillSize + pillHorizontalPadding * 2) / 2; return LayoutBuilder( @@ -41,7 +61,7 @@ class _ConfirmSwiperState extends State { final maxDrag = areaWidth - pillSize - pillHorizontalPadding; final triggerAt = maxDrag - _triggerThreshold; - return GestureDetector( + final swiper = GestureDetector( onHorizontalDragUpdate: (d) { setState(() { drag = max(pillHorizontalPadding, min(maxDrag, drag + d.delta.dx)); @@ -98,6 +118,17 @@ class _ConfirmSwiperState extends State { ), ), ); + + // A single accessibility node for the whole control: the flowing label and + // the arrow knob are decorative and must not become separate stops. No + // semantics action is exposed here; a screen reader gets the button + // variant above instead. + return Semantics( + container: true, + excludeSemantics: true, + label: widget.swiperText, + child: swiper, + ); }, ); } diff --git a/lib/new-ui/widgets/send_page/send_confirm_bottom_widget.dart b/lib/new-ui/widgets/send_page/send_confirm_bottom_widget.dart index 950932e9a..c052fceec 100644 --- a/lib/new-ui/widgets/send_page/send_confirm_bottom_widget.dart +++ b/lib/new-ui/widgets/send_page/send_confirm_bottom_widget.dart @@ -47,7 +47,8 @@ class SendConfirmBottomWidget extends StatelessWidget { onConfirmed: () { sendViewModel.commitTransaction(context); }, - swiperText: "${S.of(context).swipe_to_send}"); + swiperText: "${S.of(context).swipe_to_send}", + accessibleNavigationModeButtonText: S.of(context).send); case IsExecutingState: return LoadingBottomWidget( text: "${S.of(context).generating_transaction}...", diff --git a/lib/src/screens/wallet_connect/widgets/wc_connection_request_sheet.dart b/lib/src/screens/wallet_connect/widgets/wc_connection_request_sheet.dart index fb22e7e78..f63857dcf 100644 --- a/lib/src/screens/wallet_connect/widgets/wc_connection_request_sheet.dart +++ b/lib/src/screens/wallet_connect/widgets/wc_connection_request_sheet.dart @@ -80,6 +80,7 @@ class WCConnectionRequestSheet extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 24), child: ConfirmSwiper( swiperText: S.of(context).wc_swipe_to_approve, + accessibleNavigationModeButtonText: S.of(context).wc_action_approve, onConfirmed: () { if (Navigator.canPop(context)) { Navigator.of(context).pop(WCBottomSheetResult.one); diff --git a/lib/src/screens/wallet_connect/widgets/wc_signing_request_sheet.dart b/lib/src/screens/wallet_connect/widgets/wc_signing_request_sheet.dart index 3b083ba2d..d561f870d 100644 --- a/lib/src/screens/wallet_connect/widgets/wc_signing_request_sheet.dart +++ b/lib/src/screens/wallet_connect/widgets/wc_signing_request_sheet.dart @@ -82,6 +82,7 @@ class WCSigningRequestSheet extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 24), child: ConfirmSwiper( swiperText: swipeLabel, + accessibleNavigationModeButtonText: S.of(context).confirm, onConfirmed: () { if (Navigator.canPop(context)) { Navigator.of(context).pop(WCBottomSheetResult.one);