From 80d774d8dc705b92ea4134ad76aa74853a144eec Mon Sep 17 00:00:00 2001 From: Taha Tesser Date: Wed, 3 Apr 2024 19:31:06 +0300 Subject: [PATCH] Add `DropdownMenu` cursor behavior sample (#146133) fixes [Add `DropdownMenu` cursor behavior sample to `DropdownMenu.enabled` & `DropdownMenu.requestFocusOnTap` docs](https://github.com/flutter/flutter/issues/146131) ### Preview ![Screenshot 2024-04-02 at 17 12 43](https://github.com/flutter/flutter/assets/48603081/33865ca0-d48d-4651-9c83-9bdcd6369cb8) --- .../dropdown_menu/dropdown_menu.2.dart | 169 ++++++++++++++++++ .../dropdown_menu/dropdown_menu.2_test.dart | 43 +++++ .../lib/src/material/dropdown_menu.dart | 14 ++ 3 files changed, 226 insertions(+) create mode 100644 examples/api/lib/material/dropdown_menu/dropdown_menu.2.dart create mode 100644 examples/api/test/material/dropdown_menu/dropdown_menu.2_test.dart diff --git a/examples/api/lib/material/dropdown_menu/dropdown_menu.2.dart b/examples/api/lib/material/dropdown_menu/dropdown_menu.2.dart new file mode 100644 index 0000000000..4fbe137cb5 --- /dev/null +++ b/examples/api/lib/material/dropdown_menu/dropdown_menu.2.dart @@ -0,0 +1,169 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +/// Flutter code sample for [DropdownMenu]. + +const List list = ['One', 'Two', 'Three', 'Four']; + +void main() => runApp(const DropdownMenuApp()); + +class DropdownMenuApp extends StatelessWidget { + const DropdownMenuApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar(title: const Text('DropdownMenu Sample')), + body: const Center( + child: DropdownMenuExample(), + ), + ), + ); + } +} + +class DropdownMenuExample extends StatefulWidget { + const DropdownMenuExample({super.key}); + + @override + State createState() => _DropdownMenuExampleState(); +} + +class _DropdownMenuExampleState extends State { + String dropdownValue = list.first; + + @override + Widget build(BuildContext context) { + final ColorScheme colorScheme = Theme.of(context).colorScheme; + + return ListView( + children: [ + ListTile( + tileColor: colorScheme.primaryContainer, + title: const Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('enabled: true'), + Text('requestFocusOnTap: true'), + ], + ), + subtitle: Column( + children: [ + DropdownMenu( + requestFocusOnTap: true, + initialSelection: list.first, + expandedInsets: EdgeInsets.zero, + onSelected: (String? value) { + setState(() { + dropdownValue = value!; + }); + }, + dropdownMenuEntries: + list.map>((String value) { + return DropdownMenuEntry(value: value, label: value); + }).toList(), + ), + const Text('Text cursor is shown when hovering over the DropdownMenu.'), + ], + ), + ), + const SizedBox(height: 20), + ListTile( + tileColor: colorScheme.primaryContainer, + title: const Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('enabled: true'), + Text('requestFocusOnTap: false'), + ], + ), + subtitle: Column( + children: [ + DropdownMenu( + requestFocusOnTap: false, + initialSelection: list.first, + expandedInsets: EdgeInsets.zero, + onSelected: (String? value) { + setState(() { + dropdownValue = value!; + }); + }, + dropdownMenuEntries: + list.map>((String value) { + return DropdownMenuEntry(value: value, label: value); + }).toList(), + ), + const Text('Clickable cursor is shown when hovering over the DropdownMenu.'), + ], + ), + ), + const SizedBox(height: 20), + ListTile( + tileColor: colorScheme.onInverseSurface, + title: const Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('enabled: false'), + Text('requestFocusOnTap: true'), + ], + ), + subtitle: Column( + children: [ + DropdownMenu( + enabled: false, + requestFocusOnTap: true, + initialSelection: list.first, + expandedInsets: EdgeInsets.zero, + onSelected: (String? value) { + setState(() { + dropdownValue = value!; + }); + }, + dropdownMenuEntries: + list.map>((String value) { + return DropdownMenuEntry(value: value, label: value); + }).toList(), + ), + const Text('Default cursor is shown when hovering over the DropdownMenu.'), + ], + ), + ), + const SizedBox(height: 20), + ListTile( + tileColor: colorScheme.onInverseSurface, + title: const Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('enabled: false'), + Text('requestFocusOnTap: false'), + ], + ), + subtitle: Column( + children: [ + DropdownMenu( + enabled: false, + requestFocusOnTap: false, + initialSelection: list.first, + expandedInsets: EdgeInsets.zero, + onSelected: (String? value) { + setState(() { + dropdownValue = value!; + }); + }, + dropdownMenuEntries: + list.map>((String value) { + return DropdownMenuEntry(value: value, label: value); + }).toList(), + ), + const Text('Default cursor is shown when hovering over the DropdownMenu.'), + ], + ), + ), + ], + ); + } +} diff --git a/examples/api/test/material/dropdown_menu/dropdown_menu.2_test.dart b/examples/api/test/material/dropdown_menu/dropdown_menu.2_test.dart new file mode 100644 index 0000000000..e652b3f9ca --- /dev/null +++ b/examples/api/test/material/dropdown_menu/dropdown_menu.2_test.dart @@ -0,0 +1,43 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter_api_samples/material/dropdown_menu/dropdown_menu.2.dart' as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('DropdownMenu cursor behavoir', (WidgetTester tester) async { + await tester.pumpWidget( + const example.DropdownMenuApp(), + ); + + Finder textFieldFinder(int index) { + return find.byType(TextField).at(index); + } + + // Hover over the "enabled and requestFocusOnTap set to true" text field. + final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse, pointer: 1); + await gesture.moveTo(tester.getCenter(textFieldFinder(0))); + + expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text); + + // Hover over the "enabled and requestFocusOnTap set to false" text field. + await gesture.moveTo(tester.getCenter(textFieldFinder(1))); + + expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.click); + + // Hover over the "disabled and requestFocusOnTap set to true" text field. + await gesture.moveTo(tester.getCenter(textFieldFinder(2))); + + expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic); + + // Hover over the "disabled and requestFocusOnTap set to false" text field. + await gesture.moveTo(tester.getCenter(textFieldFinder(3))); + + expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic); + }); +} diff --git a/packages/flutter/lib/src/material/dropdown_menu.dart b/packages/flutter/lib/src/material/dropdown_menu.dart index 6ac09b52ee..be22497982 100644 --- a/packages/flutter/lib/src/material/dropdown_menu.dart +++ b/packages/flutter/lib/src/material/dropdown_menu.dart @@ -170,6 +170,13 @@ class DropdownMenu extends StatefulWidget { /// Determine if the [DropdownMenu] is enabled. /// /// Defaults to true. + /// + /// {@tool dartpad} + /// This sample demonstrates how the [enabled] and [requestFocusOnTap] properties + /// affect the textfield's hover cursor. + /// + /// ** See code in examples/api/lib/material/dropdown_menu/dropdown_menu.2.dart ** + /// {@end-tool} final bool enabled; /// Determine the width of the [DropdownMenu]. @@ -338,6 +345,13 @@ class DropdownMenu extends StatefulWidget { /// focus when activated. /// /// Set this to true or false explicitly to override the default behavior. + /// + /// {@tool dartpad} + /// This sample demonstrates how the [enabled] and [requestFocusOnTap] properties + /// affect the textfield's hover cursor. + /// + /// ** See code in examples/api/lib/material/dropdown_menu/dropdown_menu.2.dart ** + /// {@end-tool} final bool? requestFocusOnTap; /// Descriptions of the menu items in the [DropdownMenu].