From 5ecde8a2c4d55eb3e5a17b836ca4397409d2f5bf Mon Sep 17 00:00:00 2001 From: Kishan Rathore <34465683+rkishan516@users.noreply.github.com> Date: Thu, 19 Jun 2025 00:08:40 +0530 Subject: [PATCH] Feat: Add mouse cursor for CupertinoDialogAction (#169051) This PR updates the mouse pointer behavior for CupertinoDialogAction to display a pointer cursor when hovering over the action button. Currently, when hovering over CupertinoDialogAction buttons on desktop platforms, the default cursor is shown instead of changing to a pointer cursor, which doesn't provide proper visual feedback to users that the element is clickable. ## Changes - Added mouse cursor property to CupertinoDialogAction to display SystemMouseCursors.click when hovering - Updated relevant tests to verify the cursor behavior ## Technical Details The implementation adds the appropriate cursor property to the CupertinoDialogAction widget, ensuring that when users hover over the action button on desktop platforms, the cursor changes to a pointer, indicating that the element is clickable. ## Related Issues fixes: #169037 (Mouse style doesn't change to pointer when hovering over CupertinoDialogAction on Desktop) Part of: #58192 (Add mouse cursor support to the remaining widgets) ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. --------- Co-authored-by: Tong Mu Co-authored-by: chunhtai <47866232+chunhtai@users.noreply.github.com> --- .../flutter/lib/src/cupertino/dialog.dart | 10 ++++- .../flutter/test/cupertino/dialog_test.dart | 40 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/packages/flutter/lib/src/cupertino/dialog.dart b/packages/flutter/lib/src/cupertino/dialog.dart index 3f025965178..7b525854f0f 100644 --- a/packages/flutter/lib/src/cupertino/dialog.dart +++ b/packages/flutter/lib/src/cupertino/dialog.dart @@ -2115,6 +2115,7 @@ class CupertinoDialogAction extends StatefulWidget { this.isDefaultAction = false, this.isDestructiveAction = false, this.textStyle, + this.mouseCursor, required this.child, }); @@ -2149,6 +2150,12 @@ class CupertinoDialogAction extends StatefulWidget { /// [_kCupertinoDialogActionStyle]. final TextStyle? textStyle; + /// The cursor that will be shown when hovering over the button. + /// + /// If null, defaults to [SystemMouseCursors.click] on web and + /// [MouseCursor.defer] on other platforms. + final MouseCursor? mouseCursor; + /// The widget below this widget in the tree. /// /// Typically a [Text] widget. @@ -2269,7 +2276,8 @@ class _CupertinoDialogActionState extends State implement ); return MouseRegion( - cursor: widget.onPressed != null && kIsWeb ? SystemMouseCursors.click : MouseCursor.defer, + cursor: + widget.mouseCursor ?? (enabled && kIsWeb ? SystemMouseCursors.click : MouseCursor.defer), child: MetaData( metaData: this, behavior: HitTestBehavior.opaque, diff --git a/packages/flutter/test/cupertino/dialog_test.dart b/packages/flutter/test/cupertino/dialog_test.dart index bbb00ae9343..f40302278e7 100644 --- a/packages/flutter/test/cupertino/dialog_test.dart +++ b/packages/flutter/test/cupertino/dialog_test.dart @@ -14,6 +14,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../widgets/semantics_tester.dart'; @@ -2117,6 +2118,45 @@ void main() { noButton = tester.getCenter(find.text('No')); expect(yesButton.dx > noButton.dx, false); }); + + testWidgets('CupertinoDialogAction.mouseCursor can customize the mouse cursor', ( + WidgetTester tester, + ) async { + const SystemMouseCursor customCursor = SystemMouseCursors.grab; + + await tester.pumpWidget( + CupertinoApp( + home: Directionality( + textDirection: TextDirection.ltr, + child: CupertinoAlertDialog( + actions: [ + CupertinoDialogAction( + mouseCursor: customCursor, + child: const Text('Yes'), + onPressed: () {}, + ), + ], + ), + ), + ), + ); + + final TestGesture gesture = await tester.createGesture( + kind: PointerDeviceKind.mouse, + pointer: 1, + ); + await gesture.addPointer(location: const Offset(10, 10)); + await tester.pumpAndSettle(); + expect( + RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), + SystemMouseCursors.basic, + ); + + final Offset actionSheetAction = tester.getCenter(find.text('Yes')); + await gesture.moveTo(actionSheetAction); + await tester.pumpAndSettle(); + expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), customCursor); + }); } RenderBox findActionButtonRenderBoxByTitle(WidgetTester tester, String title) {