Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
5f792ba1
Unverified
Commit
5f792ba1
authored
Jul 26, 2021
by
Tong Mu
Committed by
GitHub
Jul 26, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Keyboard events (#83752)
parent
019f0337
Changes
38
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
38 changed files
with
2528 additions
and
283 deletions
+2528
-283
keyboard_key.tmpl
dev/tools/gen_keycodes/data/keyboard_key.tmpl
+6
-0
windows_logical_to_window_vk.json
...tools/gen_keycodes/data/windows_logical_to_window_vk.json
+1
-37
keyboard_maps_code_gen.dart
dev/tools/gen_keycodes/lib/keyboard_maps_code_gen.dart
+13
-1
services.dart
packages/flutter/lib/services.dart
+2
-0
binding.dart
packages/flutter/lib/src/services/binding.dart
+20
-0
debug.dart
packages/flutter/lib/src/services/debug.dart
+30
-0
hardware_keyboard.dart
packages/flutter/lib/src/services/hardware_keyboard.dart
+941
-0
keyboard_key.dart
packages/flutter/lib/src/services/keyboard_key.dart
+6
-0
keyboard_maps.dart
packages/flutter/lib/src/services/keyboard_maps.dart
+10
-0
raw_keyboard.dart
packages/flutter/lib/src/services/raw_keyboard.dart
+60
-58
raw_keyboard_web.dart
packages/flutter/lib/src/services/raw_keyboard_web.dart
+13
-8
focus_manager.dart
packages/flutter/lib/src/widgets/focus_manager.dart
+101
-30
focus_scope.dart
packages/flutter/lib/src/widgets/focus_scope.dart
+23
-2
keyboard_listener.dart
packages/flutter/lib/src/widgets/keyboard_listener.dart
+95
-0
raw_keyboard_listener.dart
packages/flutter/lib/src/widgets/raw_keyboard_listener.dart
+6
-0
shortcuts.dart
packages/flutter/lib/src/widgets/shortcuts.dart
+12
-16
widgets.dart
packages/flutter/lib/widgets.dart
+1
-0
text_field_test.dart
packages/flutter/test/cupertino/text_field_test.dart
+2
-2
text_field_test.dart
packages/flutter/test/material/text_field_test.dart
+15
-15
editable_test.dart
packages/flutter/test/rendering/editable_test.dart
+39
-2
hardware_keyboard_test.dart
packages/flutter/test/services/hardware_keyboard_test.dart
+165
-0
raw_keyboard_test.dart
packages/flutter/test/services/raw_keyboard_test.dart
+82
-17
app_test.dart
packages/flutter/test/widgets/app_test.dart
+1
-1
default_text_editing_actions_test.dart
...utter/test/widgets/default_text_editing_actions_test.dart
+1
-1
editable_text_cursor_test.dart
packages/flutter/test/widgets/editable_text_cursor_test.dart
+5
-1
editable_text_test.dart
packages/flutter/test/widgets/editable_text_test.dart
+17
-2
focus_manager_test.dart
packages/flutter/test/widgets/focus_manager_test.dart
+95
-1
focus_traversal_test.dart
packages/flutter/test/widgets/focus_traversal_test.dart
+7
-7
keyboard_listener_test.dart
packages/flutter/test/widgets/keyboard_listener_test.dart
+106
-0
scrollable_test.dart
packages/flutter/test/widgets/scrollable_test.dart
+7
-7
selectable_text_test.dart
packages/flutter/test/widgets/selectable_text_test.dart
+10
-10
shortcuts_test.dart
packages/flutter/test/widgets/shortcuts_test.dart
+48
-2
binding.dart
packages/flutter_test/lib/src/binding.dart
+7
-0
controller.dart
packages/flutter_test/lib/src/controller.dart
+44
-4
event_simulation.dart
packages/flutter_test/lib/src/event_simulation.dart
+261
-57
test_pointer.dart
packages/flutter_test/lib/src/test_pointer.dart
+2
-0
widget_tester.dart
packages/flutter_test/lib/src/widget_tester.dart
+1
-1
event_simulation_test.dart
packages/flutter_test/test/event_simulation_test.dart
+273
-1
No files found.
dev/tools/gen_keycodes/data/keyboard_key.tmpl
View file @
5f792ba1
...
@@ -327,6 +327,9 @@ class LogicalKeyboardKey extends KeyboardKey {
...
@@ -327,6 +327,9 @@ class LogicalKeyboardKey extends KeyboardKey {
@@@LOGICAL_KEY_DEFINITIONS@@@
@@@LOGICAL_KEY_DEFINITIONS@@@
/// A list of all predefined constant [LogicalKeyboardKey]s.
static Iterable<LogicalKeyboardKey> get knownLogicalKeys => _knownLogicalKeys.values;
// A list of all predefined constant LogicalKeyboardKeys so they can be
// A list of all predefined constant LogicalKeyboardKeys so they can be
// searched.
// searched.
static const Map<int, LogicalKeyboardKey> _knownLogicalKeys = <int, LogicalKeyboardKey>{
static const Map<int, LogicalKeyboardKey> _knownLogicalKeys = <int, LogicalKeyboardKey>{
...
@@ -489,6 +492,9 @@ class PhysicalKeyboardKey extends KeyboardKey {
...
@@ -489,6 +492,9 @@ class PhysicalKeyboardKey extends KeyboardKey {
@@@PHYSICAL_KEY_DEFINITIONS@@@
@@@PHYSICAL_KEY_DEFINITIONS@@@
/// A list of all predefined constant [PhysicalKeyboardKey]s.
static Iterable<PhysicalKeyboardKey> get knownPhysicalKeys => _knownPhysicalKeys.values;
// A list of all the predefined constant PhysicalKeyboardKeys so that they
// A list of all the predefined constant PhysicalKeyboardKeys so that they
// can be searched.
// can be searched.
static const Map<int, PhysicalKeyboardKey> _knownPhysicalKeys = <int, PhysicalKeyboardKey>{
static const Map<int, PhysicalKeyboardKey> _knownPhysicalKeys = <int, PhysicalKeyboardKey>{
...
...
dev/tools/gen_keycodes/data/windows_logical_to_window_vk.json
View file @
5f792ba1
...
@@ -176,41 +176,5 @@
...
@@ -176,41 +176,5 @@
"Zoom"
:
[
"ZOOM"
],
"Zoom"
:
[
"ZOOM"
],
"Noname"
:
[
"NONAME"
],
"Noname"
:
[
"NONAME"
],
"Pa1"
:
[
"PA1"
],
"Pa1"
:
[
"PA1"
],
"OemClear"
:
[
"OEM_CLEAR"
],
"OemClear"
:
[
"OEM_CLEAR"
]
"0"
:
[
"0"
],
"1"
:
[
"1"
],
"2"
:
[
"2"
],
"3"
:
[
"3"
],
"4"
:
[
"4"
],
"5"
:
[
"5"
],
"6"
:
[
"6"
],
"7"
:
[
"7"
],
"8"
:
[
"8"
],
"9"
:
[
"9"
],
"KeyA"
:
[
"A"
],
"KeyB"
:
[
"B"
],
"KeyC"
:
[
"C"
],
"KeyD"
:
[
"D"
],
"KeyE"
:
[
"E"
],
"KeyF"
:
[
"F"
],
"KeyG"
:
[
"G"
],
"KeyH"
:
[
"H"
],
"KeyI"
:
[
"I"
],
"KeyJ"
:
[
"J"
],
"KeyK"
:
[
"K"
],
"KeyL"
:
[
"L"
],
"KeyM"
:
[
"M"
],
"KeyN"
:
[
"N"
],
"KeyO"
:
[
"O"
],
"KeyP"
:
[
"P"
],
"KeyQ"
:
[
"Q"
],
"KeyR"
:
[
"R"
],
"KeyS"
:
[
"S"
],
"KeyT"
:
[
"T"
],
"KeyU"
:
[
"U"
],
"KeyV"
:
[
"V"
],
"KeyW"
:
[
"W"
],
"KeyX"
:
[
"X"
],
"KeyY"
:
[
"Y"
],
"KeyZ"
:
[
"Z"
]
}
}
dev/tools/gen_keycodes/lib/keyboard_maps_code_gen.dart
View file @
5f792ba1
...
@@ -24,6 +24,16 @@ bool _isAsciiLetter(String? char) {
...
@@ -24,6 +24,16 @@ bool _isAsciiLetter(String? char) {
||
(
charCode
>=
charLowerA
&&
charCode
<=
charLowerZ
);
||
(
charCode
>=
charLowerA
&&
charCode
<=
charLowerZ
);
}
}
bool
_isDigit
(
String
?
char
)
{
if
(
char
==
null
)
return
false
;
final
int
charDigit0
=
'0'
.
codeUnitAt
(
0
);
final
int
charDigit9
=
'9'
.
codeUnitAt
(
0
);
assert
(
char
.
length
==
1
);
final
int
charCode
=
char
.
codeUnitAt
(
0
);
return
charCode
>=
charDigit0
&&
charCode
<=
charDigit9
;
}
/// Generates the keyboard_maps.dart files, based on the information in the key
/// Generates the keyboard_maps.dart files, based on the information in the key
/// data structure given to it.
/// data structure given to it.
class
KeyboardMapsCodeGenerator
extends
BaseCodeGenerator
{
class
KeyboardMapsCodeGenerator
extends
BaseCodeGenerator
{
...
@@ -170,7 +180,9 @@ class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
...
@@ -170,7 +180,9 @@ class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
// because they are not used by the embedding. Add them manually.
// because they are not used by the embedding. Add them manually.
final
List
<
int
>?
keyCodes
=
entry
.
windowsValues
.
isNotEmpty
final
List
<
int
>?
keyCodes
=
entry
.
windowsValues
.
isNotEmpty
?
entry
.
windowsValues
?
entry
.
windowsValues
:
(
_isAsciiLetter
(
entry
.
keyLabel
)
?
<
int
>[
entry
.
keyLabel
!.
toUpperCase
().
codeUnitAt
(
0
)]
:
null
);
:
(
_isAsciiLetter
(
entry
.
keyLabel
)
?
<
int
>[
entry
.
keyLabel
!.
toUpperCase
().
codeUnitAt
(
0
)]
:
_isDigit
(
entry
.
keyLabel
)
?
<
int
>[
entry
.
keyLabel
!.
toUpperCase
().
codeUnitAt
(
0
)]
:
null
);
if
(
keyCodes
!=
null
)
{
if
(
keyCodes
!=
null
)
{
for
(
final
int
code
in
keyCodes
)
{
for
(
final
int
code
in
keyCodes
)
{
lines
.
add
(
code
,
'
$code
: LogicalKeyboardKey.
${entry.constantName}
,'
);
lines
.
add
(
code
,
'
$code
: LogicalKeyboardKey.
${entry.constantName}
,'
);
...
...
packages/flutter/lib/services.dart
View file @
5f792ba1
...
@@ -15,9 +15,11 @@ export 'src/services/autofill.dart';
...
@@ -15,9 +15,11 @@ export 'src/services/autofill.dart';
export
'src/services/binary_messenger.dart'
;
export
'src/services/binary_messenger.dart'
;
export
'src/services/binding.dart'
;
export
'src/services/binding.dart'
;
export
'src/services/clipboard.dart'
;
export
'src/services/clipboard.dart'
;
export
'src/services/debug.dart'
;
export
'src/services/deferred_component.dart'
;
export
'src/services/deferred_component.dart'
;
export
'src/services/font_loader.dart'
;
export
'src/services/font_loader.dart'
;
export
'src/services/haptic_feedback.dart'
;
export
'src/services/haptic_feedback.dart'
;
export
'src/services/hardware_keyboard.dart'
;
export
'src/services/keyboard_key.dart'
;
export
'src/services/keyboard_key.dart'
;
export
'src/services/keyboard_maps.dart'
;
export
'src/services/keyboard_maps.dart'
;
export
'src/services/message_codec.dart'
;
export
'src/services/message_codec.dart'
;
...
...
packages/flutter/lib/src/services/binding.dart
View file @
5f792ba1
...
@@ -14,7 +14,9 @@ import 'package:flutter/scheduler.dart';
...
@@ -14,7 +14,9 @@ import 'package:flutter/scheduler.dart';
import
'asset_bundle.dart'
;
import
'asset_bundle.dart'
;
import
'binary_messenger.dart'
;
import
'binary_messenger.dart'
;
import
'hardware_keyboard.dart'
;
import
'message_codec.dart'
;
import
'message_codec.dart'
;
import
'raw_keyboard.dart'
;
import
'restoration.dart'
;
import
'restoration.dart'
;
import
'system_channels.dart'
;
import
'system_channels.dart'
;
...
@@ -31,6 +33,7 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
...
@@ -31,6 +33,7 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
_instance
=
this
;
_instance
=
this
;
_defaultBinaryMessenger
=
createBinaryMessenger
();
_defaultBinaryMessenger
=
createBinaryMessenger
();
_restorationManager
=
createRestorationManager
();
_restorationManager
=
createRestorationManager
();
_initKeyboard
();
initLicenses
();
initLicenses
();
SystemChannels
.
system
.
setMessageHandler
((
dynamic
message
)
=>
handleSystemMessage
(
message
as
Object
));
SystemChannels
.
system
.
setMessageHandler
((
dynamic
message
)
=>
handleSystemMessage
(
message
as
Object
));
SystemChannels
.
lifecycle
.
setMessageHandler
(
_handleLifecycleMessage
);
SystemChannels
.
lifecycle
.
setMessageHandler
(
_handleLifecycleMessage
);
...
@@ -42,6 +45,23 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
...
@@ -42,6 +45,23 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
static
ServicesBinding
?
get
instance
=>
_instance
;
static
ServicesBinding
?
get
instance
=>
_instance
;
static
ServicesBinding
?
_instance
;
static
ServicesBinding
?
_instance
;
/// The global singleton instance of [HardwareKeyboard], which can be used to
/// query keyboard states.
HardwareKeyboard
get
keyboard
=>
_keyboard
;
late
final
HardwareKeyboard
_keyboard
;
/// The global singleton instance of [KeyEventManager], which is used
/// internally to dispatch key messages.
KeyEventManager
get
keyEventManager
=>
_keyEventManager
;
late
final
KeyEventManager
_keyEventManager
;
void
_initKeyboard
()
{
_keyboard
=
HardwareKeyboard
();
_keyEventManager
=
KeyEventManager
(
_keyboard
,
RawKeyboard
.
instance
);
window
.
onKeyData
=
_keyEventManager
.
handleKeyData
;
SystemChannels
.
keyEvent
.
setMessageHandler
(
_keyEventManager
.
handleRawKeyMessage
);
}
/// The default instance of [BinaryMessenger].
/// The default instance of [BinaryMessenger].
///
///
/// This is used to send messages from the application to the platform, and
/// This is used to send messages from the application to the platform, and
...
...
packages/flutter/lib/src/services/debug.dart
0 → 100644
View file @
5f792ba1
// 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/foundation.dart'
;
import
'hardware_keyboard.dart'
;
/// Override the transit mode with which key events are simulated.
///
/// Setting [debugKeyEventSimulatorTransitModeOverride] is a good way to make
/// certain tests simulate the behavior of different type of platforms in terms
/// of their extent of support for keyboard API.
KeyDataTransitMode
?
debugKeyEventSimulatorTransitModeOverride
;
/// Returns true if none of the widget library debug variables have been changed.
///
/// This function is used by the test framework to ensure that debug variables
/// haven't been inadvertently changed.
///
/// See [the services library](services/services-library.html) for a complete list.
bool
debugAssertAllServicesVarsUnset
(
String
reason
)
{
assert
(()
{
if
(
debugKeyEventSimulatorTransitModeOverride
!=
null
)
{
throw
FlutterError
(
reason
);
}
return
true
;
}());
return
true
;
}
packages/flutter/lib/src/services/hardware_keyboard.dart
0 → 100644
View file @
5f792ba1
This diff is collapsed.
Click to expand it.
packages/flutter/lib/src/services/keyboard_key.dart
View file @
5f792ba1
...
@@ -2624,6 +2624,9 @@ class LogicalKeyboardKey extends KeyboardKey {
...
@@ -2624,6 +2624,9 @@ class LogicalKeyboardKey extends KeyboardKey {
/// See the function [RawKeyEvent.logicalKey] for more information.
/// See the function [RawKeyEvent.logicalKey] for more information.
static
const
LogicalKeyboardKey
gameButtonZ
=
LogicalKeyboardKey
(
0x0020000031f
);
static
const
LogicalKeyboardKey
gameButtonZ
=
LogicalKeyboardKey
(
0x0020000031f
);
/// A list of all predefined constant [LogicalKeyboardKey]s.
static
Iterable
<
LogicalKeyboardKey
>
get
knownLogicalKeys
=>
_knownLogicalKeys
.
values
;
// A list of all predefined constant LogicalKeyboardKeys so they can be
// A list of all predefined constant LogicalKeyboardKeys so they can be
// searched.
// searched.
static
const
Map
<
int
,
LogicalKeyboardKey
>
_knownLogicalKeys
=
<
int
,
LogicalKeyboardKey
>{
static
const
Map
<
int
,
LogicalKeyboardKey
>
_knownLogicalKeys
=
<
int
,
LogicalKeyboardKey
>{
...
@@ -5136,6 +5139,9 @@ class PhysicalKeyboardKey extends KeyboardKey {
...
@@ -5136,6 +5139,9 @@ class PhysicalKeyboardKey extends KeyboardKey {
/// See the function [RawKeyEvent.physicalKey] for more information.
/// See the function [RawKeyEvent.physicalKey] for more information.
static
const
PhysicalKeyboardKey
showAllWindows
=
PhysicalKeyboardKey
(
0x000c029f
);
static
const
PhysicalKeyboardKey
showAllWindows
=
PhysicalKeyboardKey
(
0x000c029f
);
/// A list of all predefined constant [PhysicalKeyboardKey]s.
static
Iterable
<
PhysicalKeyboardKey
>
get
knownPhysicalKeys
=>
_knownPhysicalKeys
.
values
;
// A list of all the predefined constant PhysicalKeyboardKeys so that they
// A list of all the predefined constant PhysicalKeyboardKeys so that they
// can be searched.
// can be searched.
static
const
Map
<
int
,
PhysicalKeyboardKey
>
_knownPhysicalKeys
=
<
int
,
PhysicalKeyboardKey
>{
static
const
Map
<
int
,
PhysicalKeyboardKey
>
_knownPhysicalKeys
=
<
int
,
PhysicalKeyboardKey
>{
...
...
packages/flutter/lib/src/services/keyboard_maps.dart
View file @
5f792ba1
...
@@ -2953,6 +2953,16 @@ const Map<int, LogicalKeyboardKey> kWindowsToLogicalKey = <int, LogicalKeyboardK
...
@@ -2953,6 +2953,16 @@ const Map<int, LogicalKeyboardKey> kWindowsToLogicalKey = <int, LogicalKeyboardK
45
:
LogicalKeyboardKey
.
insert
,
45
:
LogicalKeyboardKey
.
insert
,
46
:
LogicalKeyboardKey
.
delete
,
46
:
LogicalKeyboardKey
.
delete
,
47
:
LogicalKeyboardKey
.
help
,
47
:
LogicalKeyboardKey
.
help
,
48
:
LogicalKeyboardKey
.
digit0
,
49
:
LogicalKeyboardKey
.
digit1
,
50
:
LogicalKeyboardKey
.
digit2
,
51
:
LogicalKeyboardKey
.
digit3
,
52
:
LogicalKeyboardKey
.
digit4
,
53
:
LogicalKeyboardKey
.
digit5
,
54
:
LogicalKeyboardKey
.
digit6
,
55
:
LogicalKeyboardKey
.
digit7
,
56
:
LogicalKeyboardKey
.
digit8
,
57
:
LogicalKeyboardKey
.
digit9
,
65
:
LogicalKeyboardKey
.
keyA
,
65
:
LogicalKeyboardKey
.
keyA
,
66
:
LogicalKeyboardKey
.
keyB
,
66
:
LogicalKeyboardKey
.
keyB
,
67
:
LogicalKeyboardKey
.
keyC
,
67
:
LogicalKeyboardKey
.
keyC
,
...
...
packages/flutter/lib/src/services/raw_keyboard.dart
View file @
5f792ba1
...
@@ -3,10 +3,12 @@
...
@@ -3,10 +3,12 @@
// found in the LICENSE file.
// found in the LICENSE file.
import
'dart:io'
;
import
'dart:io'
;
import
'dart:ui'
;
import
'dart:ui'
as
ui
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/foundation.dart'
;
import
'binding.dart'
;
import
'hardware_keyboard.dart'
;
import
'keyboard_key.dart'
;
import
'keyboard_key.dart'
;
import
'raw_keyboard_android.dart'
;
import
'raw_keyboard_android.dart'
;
import
'raw_keyboard_fuchsia.dart'
;
import
'raw_keyboard_fuchsia.dart'
;
...
@@ -286,16 +288,20 @@ abstract class RawKeyEvent with Diagnosticable {
...
@@ -286,16 +288,20 @@ abstract class RawKeyEvent with Diagnosticable {
final
RawKeyEventData
data
;
final
RawKeyEventData
data
;
String
?
character
;
String
?
character
;
if
(
kIsWeb
)
{
RawKeyEventData
_dataFromWeb
(
)
{
final
String
?
key
=
message
[
'key'
]
as
String
?;
final
String
?
key
=
message
[
'key'
]
as
String
?;
data
=
RawKeyEventDataWeb
(
if
(
key
!=
null
&&
key
.
isNotEmpty
)
{
character
=
key
;
}
return
RawKeyEventDataWeb
(
code:
message
[
'code'
]
as
String
?
??
''
,
code:
message
[
'code'
]
as
String
?
??
''
,
key:
key
??
''
,
key:
key
??
''
,
location:
message
[
'location'
]
as
int
?
??
0
,
metaState:
message
[
'metaState'
]
as
int
?
??
0
,
metaState:
message
[
'metaState'
]
as
int
?
??
0
,
);
);
if
(
key
!=
null
&&
key
.
isNotEmpty
)
{
}
character
=
key
;
if
(
kIsWeb
)
{
}
data
=
_dataFromWeb
();
}
else
{
}
else
{
final
String
keymap
=
message
[
'keymap'
]
as
String
;
final
String
keymap
=
message
[
'keymap'
]
as
String
;
switch
(
keymap
)
{
switch
(
keymap
)
{
...
@@ -372,15 +378,7 @@ abstract class RawKeyEvent with Diagnosticable {
...
@@ -372,15 +378,7 @@ abstract class RawKeyEvent with Diagnosticable {
}
}
break
;
break
;
case
'web'
:
case
'web'
:
final
String
?
key
=
message
[
'key'
]
as
String
?;
data
=
_dataFromWeb
();
data
=
RawKeyEventDataWeb
(
code:
message
[
'code'
]
as
String
?
??
''
,
key:
key
??
''
,
metaState:
message
[
'metaState'
]
as
int
?
??
0
,
);
if
(
key
!=
null
&&
key
.
isNotEmpty
)
{
character
=
key
;
}
break
;
break
;
default
:
default
:
/// This exception would only be hit on platforms that haven't yet
/// This exception would only be hit on platforms that haven't yet
...
@@ -571,9 +569,7 @@ typedef RawKeyEventHandler = bool Function(RawKeyEvent event);
...
@@ -571,9 +569,7 @@ typedef RawKeyEventHandler = bool Function(RawKeyEvent event);
/// * [SystemChannels.keyEvent], the low-level channel used for receiving
/// * [SystemChannels.keyEvent], the low-level channel used for receiving
/// events from the system.
/// events from the system.
class
RawKeyboard
{
class
RawKeyboard
{
RawKeyboard
.
_
()
{
RawKeyboard
.
_
();
SystemChannels
.
keyEvent
.
setMessageHandler
(
_handleKeyEvent
);
}
/// The shared instance of [RawKeyboard].
/// The shared instance of [RawKeyboard].
static
final
RawKeyboard
instance
=
RawKeyboard
.
_
();
static
final
RawKeyboard
instance
=
RawKeyboard
.
_
();
...
@@ -604,39 +600,45 @@ class RawKeyboard {
...
@@ -604,39 +600,45 @@ class RawKeyboard {
_listeners
.
remove
(
listener
);
_listeners
.
remove
(
listener
);
}
}
/// A handler for hardware keyboard events that will stop propagation if the
/// A handler for raw hardware keyboard events that will stop propagation if
/// handler returns true.
/// the handler returns true.
///
///
/// Key events on the platform are given to Flutter to be handled by the
/// This property is only a wrapper over [KeyEventManager.keyMessageHandler],
/// engine. If they are not handled, then the platform will continue to
/// and is kept only for backward compatibility. New code should use
/// distribute the keys (i.e. propagate them) to other (possibly non-Flutter)
/// [KeyEventManager.keyMessageHandler] to set custom global key event
/// components in the application. The return value from this handler tells
/// handler. Setting [keyEventHandler] will cause
/// the platform to either stop propagation (by returning true: "event
/// [KeyEventManager.keyMessageHandler] to be set with a converted handler.
/// handled"), or pass the event on to other controls (false: "event not
/// If [KeyEventManager.keyMessageHandler] is set by [FocusManager] (the most
/// handled").
/// common situation), then the exact value of [keyEventHandler] is a dummy
///
/// callback and must not be invoked.
/// This handler is normally set by the [FocusManager] so that it can control
RawKeyEventHandler
?
get
keyEventHandler
{
/// the key event propagation to focused widgets.
if
(
ServicesBinding
.
instance
!.
keyEventManager
.
keyMessageHandler
!=
_cachedKeyMessageHandler
)
{
///
_cachedKeyMessageHandler
=
ServicesBinding
.
instance
!.
keyEventManager
.
keyMessageHandler
;
/// Most applications can use the focus system (see [Focus] and
_cachedKeyEventHandler
=
_cachedKeyMessageHandler
==
null
?
/// [FocusManager]) to receive key events. If you are not using the
null
:
/// [FocusManager] to manage focus, then to be able to stop propagation of the
(
RawKeyEvent
event
)
{
/// event by indicating that the event was handled, set this attribute to a
assert
(
false
,
/// [RawKeyEventHandler]. Otherwise, key events will be assumed to not have
'The RawKeyboard.instance.keyEventHandler assigned by Flutter is a dummy '
/// been handled by Flutter, and will also be sent to other (possibly
'callback kept for compatibility and should not be directly called. Use '
/// non-Flutter) controls in the application.
'ServicesBinding.instance!.keyMessageHandler instead.'
);
///
return
true
;
/// See also:
};
///
}
/// * [Focus.onKey], a [Focus] callback attribute that will be given key
return
_cachedKeyEventHandler
;
/// events distributed by the [FocusManager] based on the current primary
}
/// focus.
RawKeyEventHandler
?
_cachedKeyEventHandler
;
/// * [addListener], to add passive key event listeners that do not stop event
KeyMessageHandler
?
_cachedKeyMessageHandler
;
/// propagation.
set
keyEventHandler
(
RawKeyEventHandler
?
handler
)
{
RawKeyEventHandler
?
keyEventHandler
;
_cachedKeyEventHandler
=
handler
;
_cachedKeyMessageHandler
=
handler
==
null
?
null
:
(
KeyMessage
message
)
=>
handler
(
message
.
rawEvent
);
ServicesBinding
.
instance
!.
keyEventManager
.
keyMessageHandler
=
_cachedKeyMessageHandler
;
}
Future
<
dynamic
>
_handleKeyEvent
(
dynamic
message
)
async
{
/// Process a new [RawKeyEvent] by recording the state changes and
final
RawKeyEvent
event
=
RawKeyEvent
.
fromMessage
(
message
as
Map
<
String
,
dynamic
>);
/// dispatching to listeners.
bool
handleRawKeyEvent
(
RawKeyEvent
event
)
{
bool
shouldDispatch
=
true
;
bool
shouldDispatch
=
true
;
if
(
event
is
RawKeyDownEvent
)
{
if
(
event
is
RawKeyDownEvent
)
{
if
(
event
.
data
.
shouldDispatchEvent
())
{
if
(
event
.
data
.
shouldDispatchEvent
())
{
...
@@ -657,7 +659,7 @@ class RawKeyboard {
...
@@ -657,7 +659,7 @@ class RawKeyboard {
}
}
}
}
if
(!
shouldDispatch
)
{
if
(!
shouldDispatch
)
{
return
<
String
,
dynamic
>{
'handled'
:
true
}
;
return
true
;
}
}
// Make sure that the modifiers reflect reality, in case a modifier key was
// Make sure that the modifiers reflect reality, in case a modifier key was
// pressed/released while the app didn't have focus.
// pressed/released while the app didn't have focus.
...
@@ -676,12 +678,7 @@ class RawKeyboard {
...
@@ -676,12 +678,7 @@ class RawKeyboard {
}
}
}
}
// Send the key event to the keyEventHandler, then send the appropriate
return
false
;
// response to the platform so that it can resolve the event's handling.
// Defaults to false if keyEventHandler is null.
final
bool
handled
=
keyEventHandler
!=
null
&&
keyEventHandler
!(
event
);
assert
(
handled
!=
null
,
'keyEventHandler returned null, which is not allowed'
);
return
<
String
,
dynamic
>{
'handled'
:
handled
};
}
}
static
final
Map
<
_ModifierSidePair
,
Set
<
PhysicalKeyboardKey
>>
_modifierKeyMap
=
<
_ModifierSidePair
,
Set
<
PhysicalKeyboardKey
>>{
static
final
Map
<
_ModifierSidePair
,
Set
<
PhysicalKeyboardKey
>>
_modifierKeyMap
=
<
_ModifierSidePair
,
Set
<
PhysicalKeyboardKey
>>{
...
@@ -806,6 +803,11 @@ class RawKeyboard {
...
@@ -806,6 +803,11 @@ class RawKeyboard {
/// Returns the set of physical keys currently pressed.
/// Returns the set of physical keys currently pressed.
Set
<
PhysicalKeyboardKey
>
get
physicalKeysPressed
=>
_keysPressed
.
keys
.
toSet
();
Set
<
PhysicalKeyboardKey
>
get
physicalKeysPressed
=>
_keysPressed
.
keys
.
toSet
();
/// Returns the logical key that corresponds to the given pressed physical key.
///
/// Returns null if the physical key is not currently pressed.
LogicalKeyboardKey
?
lookUpLayout
(
PhysicalKeyboardKey
physicalKey
)
=>
_keysPressed
[
physicalKey
];
/// Clears the list of keys returned from [keysPressed].
/// Clears the list of keys returned from [keysPressed].
///
///
/// This is used by the testing framework to make sure tests are hermetic.
/// This is used by the testing framework to make sure tests are hermetic.
...
@@ -830,5 +832,5 @@ class _ModifierSidePair {
...
@@ -830,5 +832,5 @@ class _ModifierSidePair {
}
}
@override
@override
int
get
hashCode
=>
hashValues
(
modifier
,
side
);
int
get
hashCode
=>
ui
.
hashValues
(
modifier
,
side
);
}
}
packages/flutter/lib/src/services/raw_keyboard_web.dart
View file @
5f792ba1
...
@@ -22,6 +22,7 @@ class RawKeyEventDataWeb extends RawKeyEventData {
...
@@ -22,6 +22,7 @@ class RawKeyEventDataWeb extends RawKeyEventData {
const
RawKeyEventDataWeb
({
const
RawKeyEventDataWeb
({
required
this
.
code
,
required
this
.
code
,
required
this
.
key
,
required
this
.
key
,
this
.
location
=
0
,
this
.
metaState
=
modifierNone
,
this
.
metaState
=
modifierNone
,
})
:
assert
(
code
!=
null
),
})
:
assert
(
code
!=
null
),
assert
(
metaState
!=
null
);
assert
(
metaState
!=
null
);
...
@@ -38,6 +39,12 @@ class RawKeyEventDataWeb extends RawKeyEventData {
...
@@ -38,6 +39,12 @@ class RawKeyEventDataWeb extends RawKeyEventData {
/// for more information.
/// for more information.
final
String
key
;
final
String
key
;
/// The `KeyboardEvent.location` corresponding to this event.
///
/// See <https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/location>
/// for more information.
final
int
location
;
/// The modifiers that were present when the key event occurred.
/// The modifiers that were present when the key event occurred.
///
///
/// See `lib/src/engine/keyboard.dart` in the web engine for the numerical
/// See `lib/src/engine/keyboard.dart` in the web engine for the numerical
...
@@ -65,13 +72,11 @@ class RawKeyEventDataWeb extends RawKeyEventData {
...
@@ -65,13 +72,11 @@ class RawKeyEventDataWeb extends RawKeyEventData {
@override
@override
LogicalKeyboardKey
get
logicalKey
{
LogicalKeyboardKey
get
logicalKey
{
// Look to see if the keyCode is a printable number pad key, so that a
// Look to see if the keyCode is a key based on location. Typically they are
// difference between regular keys (e.g. ".") and the number pad version
// numpad keys (versus main area keys) and left/right modifiers.
// (e.g. the "." on the number pad) can be determined.
final
LogicalKeyboardKey
?
maybeLocationKey
=
kWebLocationMap
[
key
]?[
location
];
final
LogicalKeyboardKey
?
numPadKey
=
kWebNumPadMap
[
code
];
if
(
maybeLocationKey
!=
null
)
if
(
numPadKey
!=
null
)
{
return
maybeLocationKey
;
return
numPadKey
;
}
// Look to see if the [code] is one we know about and have a mapping for.
// Look to see if the [code] is one we know about and have a mapping for.
final
LogicalKeyboardKey
?
newKey
=
kWebToLogicalKey
[
code
];
final
LogicalKeyboardKey
?
newKey
=
kWebToLogicalKey
[
code
];
...
@@ -189,6 +194,6 @@ class RawKeyEventDataWeb extends RawKeyEventData {
...
@@ -189,6 +194,6 @@ class RawKeyEventDataWeb extends RawKeyEventData {
@override
@override
String
toString
()
{
String
toString
()
{
return
'
${objectRuntimeType(this, 'RawKeyEventDataWeb')}
(keyLabel:
$keyLabel
, code:
$code
, '
return
'
${objectRuntimeType(this, 'RawKeyEventDataWeb')}
(keyLabel:
$keyLabel
, code:
$code
, '
'metaState:
$metaState
, modifiers down:
$modifiersPressed
)'
;
'
location:
$location
,
metaState:
$metaState
, modifiers down:
$modifiersPressed
)'
;
}
}
}
}
packages/flutter/lib/src/widgets/focus_manager.dart
View file @
5f792ba1
...
@@ -36,7 +36,7 @@ bool _focusDebug(String message, [Iterable<String>? details]) {
...
@@ -36,7 +36,7 @@ bool _focusDebug(String message, [Iterable<String>? details]) {
}
}
/// An enum that describes how to handle a key event handled by a
/// An enum that describes how to handle a key event handled by a
/// [FocusOnKeyCallback].
/// [FocusOnKeyCallback]
or [FocusOnKeyEventCallback]
.
enum
KeyEventResult
{
enum
KeyEventResult
{
/// The key event has been handled, and the event should not be propagated to
/// The key event has been handled, and the event should not be propagated to
/// other key event handlers.
/// other key event handlers.
...
@@ -52,6 +52,32 @@ enum KeyEventResult {
...
@@ -52,6 +52,32 @@ enum KeyEventResult {
skipRemainingHandlers
,
skipRemainingHandlers
,
}
}
/// Combine the results returned by multiple [FocusOnKeyCallback]s or
/// [FocusOnKeyEventCallback]s.
///
/// If any callback returns [KeyEventResult.handled], the node considers the
/// message handled; otherwise, if any callback returns
/// [KeyEventResult.skipRemainingHandlers], the node skips the remaining
/// handlers without preventing the platform to handle; otherwise the node is
/// ignored.
KeyEventResult
combineKeyEventResults
(
Iterable
<
KeyEventResult
>
results
)
{
bool
hasSkipRemainingHandlers
=
false
;
for
(
final
KeyEventResult
result
in
results
)
{
switch
(
result
)
{
case
KeyEventResult
.
handled
:
return
KeyEventResult
.
handled
;
case
KeyEventResult
.
skipRemainingHandlers
:
hasSkipRemainingHandlers
=
true
;
break
;
default
:
break
;
}
}
return
hasSkipRemainingHandlers
?
KeyEventResult
.
skipRemainingHandlers
:
KeyEventResult
.
ignored
;
}
/// Signature of a callback used by [Focus.onKey] and [FocusScope.onKey]
/// Signature of a callback used by [Focus.onKey] and [FocusScope.onKey]
/// to receive key events.
/// to receive key events.
///
///
...
@@ -61,6 +87,15 @@ enum KeyEventResult {
...
@@ -61,6 +87,15 @@ enum KeyEventResult {
/// was handled.
/// was handled.
typedef
FocusOnKeyCallback
=
KeyEventResult
Function
(
FocusNode
node
,
RawKeyEvent
event
);
typedef
FocusOnKeyCallback
=
KeyEventResult
Function
(
FocusNode
node
,
RawKeyEvent
event
);
/// Signature of a callback used by [Focus.onKeyEvent] and [FocusScope.onKeyEvent]
/// to receive key events.
///
/// The [node] is the node that received the event.
///
/// Returns a [KeyEventResult] that describes how, and whether, the key event
/// was handled.
typedef
FocusOnKeyEventCallback
=
KeyEventResult
Function
(
FocusNode
node
,
KeyEvent
event
);
/// An attachment point for a [FocusNode].
/// An attachment point for a [FocusNode].
///
///
/// Using a [FocusAttachment] is rarely needed, unless you are building
/// Using a [FocusAttachment] is rarely needed, unless you are building
...
@@ -271,12 +306,13 @@ enum UnfocusDisposition {
...
@@ -271,12 +306,13 @@ enum UnfocusDisposition {
/// {@template flutter.widgets.FocusNode.keyEvents}
/// {@template flutter.widgets.FocusNode.keyEvents}
/// ## Key Event Propagation
/// ## Key Event Propagation
///
///
/// The [FocusManager] receives key events from [RawKeyboard] and will pass them
/// The [FocusManager] receives key events from [RawKeyboard] and
/// to the focused nodes. It starts with the node with the primary focus, and
/// [HardwareKeyboard] and will pass them to the focused nodes. It starts with
/// will call the [onKey] callback for that node. If the callback returns false,
/// the node with the primary focus, and will call the [onKey] or [onKeyEvent]
/// indicating that it did not handle the event, the [FocusManager] will move to
/// callback for that node. If the callback returns false, indicating that it did
/// the parent of that node and call its [onKey]. If that [onKey] returns true,
/// not handle the event, the [FocusManager] will move to the parent of that node
/// then it will stop propagating the event. If it reaches the root
/// and call its [onKey] or [onKeyEvent]. If that [onKey] or [onKeyEvent] returns
/// true, then it will stop propagating the event. If it reaches the root
/// [FocusScopeNode], [FocusManager.rootScope], the event is discarded.
/// [FocusScopeNode], [FocusManager.rootScope], the event is discarded.
/// {@endtemplate}
/// {@endtemplate}
///
///
...
@@ -433,9 +469,14 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
...
@@ -433,9 +469,14 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
///
///
/// The [skipTraversal], [descendantsAreFocusable], and [canRequestFocus]
/// The [skipTraversal], [descendantsAreFocusable], and [canRequestFocus]
/// arguments must not be null.
/// arguments must not be null.
///
/// To receive key events that focuses on this node, pass a listener to `onKeyEvent`.
/// The `onKey` is a legacy API based on [RawKeyEvent] and will be deprecated
/// in the future.
FocusNode
({
FocusNode
({
String
?
debugLabel
,
String
?
debugLabel
,
this
.
onKey
,
this
.
onKey
,
this
.
onKeyEvent
,
bool
skipTraversal
=
false
,
bool
skipTraversal
=
false
,
bool
canRequestFocus
=
true
,
bool
canRequestFocus
=
true
,
bool
descendantsAreFocusable
=
true
,
bool
descendantsAreFocusable
=
true
,
...
@@ -574,9 +615,18 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
...
@@ -574,9 +615,18 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
/// Called if this focus node receives a key event while focused (i.e. when
/// Called if this focus node receives a key event while focused (i.e. when
/// [hasFocus] returns true).
/// [hasFocus] returns true).
///
///
/// This is a legacy API based on [RawKeyEvent] and will be deprecated in the
/// future. Prefer [onKeyEvent] instead.
///
/// {@macro flutter.widgets.FocusNode.keyEvents}
/// {@macro flutter.widgets.FocusNode.keyEvents}
FocusOnKeyCallback
?
onKey
;
FocusOnKeyCallback
?
onKey
;
/// Called if this focus node receives a key event while focused (i.e. when
/// [hasFocus] returns true).
///
/// {@macro flutter.widgets.FocusNode.keyEvents}
FocusOnKeyEventCallback
?
onKeyEvent
;
FocusManager
?
_manager
;
FocusManager
?
_manager
;
List
<
FocusNode
>?
_ancestors
;
List
<
FocusNode
>?
_ancestors
;
List
<
FocusNode
>?
_descendants
;
List
<
FocusNode
>?
_descendants
;
...
@@ -1028,10 +1078,19 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
...
@@ -1028,10 +1078,19 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
/// need to be attached. [FocusAttachment.detach] should be called on the old
/// need to be attached. [FocusAttachment.detach] should be called on the old
/// node, and then [attach] called on the new node. This typically happens in
/// node, and then [attach] called on the new node. This typically happens in
/// the [State.didUpdateWidget] method.
/// the [State.didUpdateWidget] method.
///
/// To receive key events that focuses on this node, pass a listener to `onKeyEvent`.
/// The `onKey` is a legacy API based on [RawKeyEvent] and will be deprecated
/// in the future.
@mustCallSuper
@mustCallSuper
FocusAttachment
attach
(
BuildContext
?
context
,
{
FocusOnKeyCallback
?
onKey
})
{
FocusAttachment
attach
(
BuildContext
?
context
,
{
FocusOnKeyEventCallback
?
onKeyEvent
,
FocusOnKeyCallback
?
onKey
,
})
{
_context
=
context
;
_context
=
context
;
this
.
onKey
=
onKey
??
this
.
onKey
;
this
.
onKey
=
onKey
??
this
.
onKey
;
this
.
onKeyEvent
=
onKeyEvent
??
this
.
onKeyEvent
;
_attachment
=
FocusAttachment
.
_
(
this
);
_attachment
=
FocusAttachment
.
_
(
this
);
return
_attachment
!;
return
_attachment
!;
}
}
...
@@ -1225,6 +1284,7 @@ class FocusScopeNode extends FocusNode {
...
@@ -1225,6 +1284,7 @@ class FocusScopeNode extends FocusNode {
/// All parameters are optional.
/// All parameters are optional.
FocusScopeNode
({
FocusScopeNode
({
String
?
debugLabel
,
String
?
debugLabel
,
FocusOnKeyEventCallback
?
onKeyEvent
,
FocusOnKeyCallback
?
onKey
,
FocusOnKeyCallback
?
onKey
,
bool
skipTraversal
=
false
,
bool
skipTraversal
=
false
,
bool
canRequestFocus
=
true
,
bool
canRequestFocus
=
true
,
...
@@ -1232,6 +1292,7 @@ class FocusScopeNode extends FocusNode {
...
@@ -1232,6 +1292,7 @@ class FocusScopeNode extends FocusNode {
assert
(
canRequestFocus
!=
null
),
assert
(
canRequestFocus
!=
null
),
super
(
super
(
debugLabel:
debugLabel
,
debugLabel:
debugLabel
,
onKeyEvent:
onKeyEvent
,
onKey:
onKey
,
onKey:
onKey
,
canRequestFocus:
canRequestFocus
,
canRequestFocus:
canRequestFocus
,
descendantsAreFocusable:
true
,
descendantsAreFocusable:
true
,
...
@@ -1463,15 +1524,15 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
...
@@ -1463,15 +1524,15 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
/// When this focus manager is no longer needed, calling [dispose] on it will
/// When this focus manager is no longer needed, calling [dispose] on it will
/// unregister these handlers.
/// unregister these handlers.
void
registerGlobalHandlers
()
{
void
registerGlobalHandlers
()
{
assert
(
RawKeyboard
.
instance
.
keyEvent
Handler
==
null
);
assert
(
ServicesBinding
.
instance
!.
keyEventManager
.
keyMessage
Handler
==
null
);
RawKeyboard
.
instance
.
keyEventHandler
=
_handleRawKeyEvent
;
ServicesBinding
.
instance
!.
keyEventManager
.
keyMessageHandler
=
_handleKeyMessage
;
GestureBinding
.
instance
!.
pointerRouter
.
addGlobalRoute
(
_handlePointerEvent
);
GestureBinding
.
instance
!.
pointerRouter
.
addGlobalRoute
(
_handlePointerEvent
);
}
}
@override
@override
void
dispose
()
{
void
dispose
()
{
if
(
RawKeyboard
.
instance
.
keyEventHandler
==
_handleRawKeyEvent
)
{
if
(
ServicesBinding
.
instance
!.
keyEventManager
.
keyMessageHandler
==
_handleKeyMessage
)
{
RawKeyboard
.
instance
.
keyEvent
Handler
=
null
;
ServicesBinding
.
instance
!.
keyEventManager
.
keyMessage
Handler
=
null
;
GestureBinding
.
instance
!.
pointerRouter
.
removeGlobalRoute
(
_handlePointerEvent
);
GestureBinding
.
instance
!.
pointerRouter
.
removeGlobalRoute
(
_handlePointerEvent
);
}
}
super
.
dispose
();
super
.
dispose
();
...
@@ -1660,15 +1721,15 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
...
@@ -1660,15 +1721,15 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
}
}
}
}
bool
_handle
RawKeyEvent
(
RawKeyEvent
event
)
{
bool
_handle
KeyMessage
(
KeyMessage
message
)
{
// Update highlightMode first, since things responding to the keys might
// Update highlightMode first, since things responding to the keys might
// look at the highlight mode, and it should be accurate.
// look at the highlight mode, and it should be accurate.
_lastInteractionWasTouch
=
false
;
_lastInteractionWasTouch
=
false
;
_updateHighlightMode
();
_updateHighlightMode
();
assert
(
_focusDebug
(
'Received key event
$
{event.logicalKey}
'
));
assert
(
_focusDebug
(
'Received key event
$
message
'
));
if
(
_primaryFocus
==
null
)
{
if
(
_primaryFocus
==
null
)
{
assert
(
_focusDebug
(
'No primary focus for key event, ignored:
$
event
'
));
assert
(
_focusDebug
(
'No primary focus for key event, ignored:
$
message
'
));
return
false
;
return
false
;
}
}
...
@@ -1677,25 +1738,35 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
...
@@ -1677,25 +1738,35 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
// stop propagation, stop.
// stop propagation, stop.
bool
handled
=
false
;
bool
handled
=
false
;
for
(
final
FocusNode
node
in
<
FocusNode
>[
_primaryFocus
!,
...
_primaryFocus
!.
ancestors
])
{
for
(
final
FocusNode
node
in
<
FocusNode
>[
_primaryFocus
!,
...
_primaryFocus
!.
ancestors
])
{
if
(
node
.
onKey
!=
null
)
{
final
List
<
KeyEventResult
>
results
=
<
KeyEventResult
>[];
final
KeyEventResult
result
=
node
.
onKey
!(
node
,
event
);
if
(
node
.
onKeyEvent
!=
null
)
{
switch
(
result
)
{
for
(
final
KeyEvent
event
in
message
.
events
)
{
case
KeyEventResult
.
handled
:
results
.
add
(
node
.
onKeyEvent
!(
node
,
event
));
assert
(
_focusDebug
(
'Node
$node
handled key event
$event
.'
));
handled
=
true
;
break
;
case
KeyEventResult
.
skipRemainingHandlers
:
assert
(
_focusDebug
(
'Node
$node
stopped key event propagation:
$event
.'
));
handled
=
false
;
break
;
case
KeyEventResult
.
ignored
:
continue
;
}
}
break
;
}
}
if
(
node
.
onKey
!=
null
)
{
results
.
add
(
node
.
onKey
!(
node
,
message
.
rawEvent
));
}
final
KeyEventResult
result
=
combineKeyEventResults
(
results
);
switch
(
result
)
{
case
KeyEventResult
.
ignored
:
continue
;
case
KeyEventResult
.
handled
:
assert
(
_focusDebug
(
'Node
$node
handled key event
$message
.'
));
handled
=
true
;
break
;
case
KeyEventResult
.
skipRemainingHandlers
:
assert
(
_focusDebug
(
'Node
$node
stopped key event propagation:
$message
.'
));
handled
=
false
;
break
;
}
// Only KeyEventResult.ignored will continue the for loop. All other
// options will stop the event propagation.
assert
(
result
!=
KeyEventResult
.
ignored
);
break
;
}
}
if
(!
handled
)
{
if
(!
handled
)
{
assert
(
_focusDebug
(
'Key event not handled by anyone:
$
event
.'
));
assert
(
_focusDebug
(
'Key event not handled by anyone:
$
message
.'
));
}
}
return
handled
;
return
handled
;
}
}
...
...
packages/flutter/lib/src/widgets/focus_scope.dart
View file @
5f792ba1
...
@@ -283,6 +283,7 @@ class Focus extends StatefulWidget {
...
@@ -283,6 +283,7 @@ class Focus extends StatefulWidget {
this
.
autofocus
=
false
,
this
.
autofocus
=
false
,
this
.
onFocusChange
,
this
.
onFocusChange
,
this
.
onKey
,
this
.
onKey
,
this
.
onKeyEvent
,
this
.
debugLabel
,
this
.
debugLabel
,
this
.
canRequestFocus
,
this
.
canRequestFocus
,
this
.
descendantsAreFocusable
=
true
,
this
.
descendantsAreFocusable
=
true
,
...
@@ -315,6 +316,24 @@ class Focus extends StatefulWidget {
...
@@ -315,6 +316,24 @@ class Focus extends StatefulWidget {
/// focus.
/// focus.
///
///
/// Key events are first given to the [FocusNode] that has primary focus, and
/// Key events are first given to the [FocusNode] that has primary focus, and
/// if its [onKeyEvent] method return false, then they are given to each
/// ancestor node up the focus hierarchy in turn. If an event reaches the root
/// of the hierarchy, it is discarded.
///
/// This is not the way to get text input in the manner of a text field: it
/// leaves out support for input method editors, and doesn't support soft
/// keyboards in general. For text input, consider [TextField],
/// [EditableText], or [CupertinoTextField] instead, which do support these
/// things.
final
FocusOnKeyEventCallback
?
onKeyEvent
;
/// Handler for keys pressed when this object or one of its children has
/// focus.
///
/// This is a legacy API based on [RawKeyEvent] and will be deprecated in the
/// future. Prefer [onKeyEvent] instead.
///
/// Key events are first given to the [FocusNode] that has primary focus, and
/// if its [onKey] method return false, then they are given to each ancestor
/// if its [onKey] method return false, then they are given to each ancestor
/// node up the focus hierarchy in turn. If an event reaches the root of the
/// node up the focus hierarchy in turn. If an event reaches the root of the
/// hierarchy, it is discarded.
/// hierarchy, it is discarded.
...
@@ -540,7 +559,7 @@ class Focus extends StatefulWidget {
...
@@ -540,7 +559,7 @@ class Focus extends StatefulWidget {
}
}
@override
@override
State
<
Focus
>
createState
()
=>
_FocusState
();
State
<
Focus
>
createState
()
=>
_FocusState
();
}
}
class
_FocusState
extends
State
<
Focus
>
{
class
_FocusState
extends
State
<
Focus
>
{
...
@@ -575,7 +594,7 @@ class _FocusState extends State<Focus> {
...
@@ -575,7 +594,7 @@ class _FocusState extends State<Focus> {
_canRequestFocus
=
focusNode
.
canRequestFocus
;
_canRequestFocus
=
focusNode
.
canRequestFocus
;
_descendantsAreFocusable
=
focusNode
.
descendantsAreFocusable
;
_descendantsAreFocusable
=
focusNode
.
descendantsAreFocusable
;
_hasPrimaryFocus
=
focusNode
.
hasPrimaryFocus
;
_hasPrimaryFocus
=
focusNode
.
hasPrimaryFocus
;
_focusAttachment
=
focusNode
.
attach
(
context
,
onKey:
widget
.
onKey
);
_focusAttachment
=
focusNode
.
attach
(
context
,
onKey
Event:
widget
.
onKeyEvent
,
onKey
:
widget
.
onKey
);
// Add listener even if the _internalNode existed before, since it should
// Add listener even if the _internalNode existed before, since it should
// not be listening now if we're re-using a previous one because it should
// not be listening now if we're re-using a previous one because it should
...
@@ -914,6 +933,7 @@ class FocusScope extends Focus {
...
@@ -914,6 +933,7 @@ class FocusScope extends Focus {
ValueChanged
<
bool
>?
onFocusChange
,
ValueChanged
<
bool
>?
onFocusChange
,
bool
?
canRequestFocus
,
bool
?
canRequestFocus
,
bool
?
skipTraversal
,
bool
?
skipTraversal
,
FocusOnKeyEventCallback
?
onKeyEvent
,
FocusOnKeyCallback
?
onKey
,
FocusOnKeyCallback
?
onKey
,
String
?
debugLabel
,
String
?
debugLabel
,
})
:
assert
(
child
!=
null
),
})
:
assert
(
child
!=
null
),
...
@@ -926,6 +946,7 @@ class FocusScope extends Focus {
...
@@ -926,6 +946,7 @@ class FocusScope extends Focus {
onFocusChange:
onFocusChange
,
onFocusChange:
onFocusChange
,
canRequestFocus:
canRequestFocus
,
canRequestFocus:
canRequestFocus
,
skipTraversal:
skipTraversal
,
skipTraversal:
skipTraversal
,
onKeyEvent:
onKeyEvent
,
onKey:
onKey
,
onKey:
onKey
,
debugLabel:
debugLabel
,
debugLabel:
debugLabel
,
);
);
...
...
packages/flutter/lib/src/widgets/keyboard_listener.dart
0 → 100644
View file @
5f792ba1
// 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/foundation.dart'
;
import
'package:flutter/services.dart'
;
import
'focus_manager.dart'
;
import
'focus_scope.dart'
;
import
'framework.dart'
;
export
'package:flutter/services.dart'
show
KeyEvent
;
/// A widget that calls a callback whenever the user presses or releases a key
/// on a keyboard.
///
/// A [KeyboardListener] is useful for listening to key events and
/// hardware buttons that are represented as keys. Typically used by games and
/// other apps that use keyboards for purposes other than text entry.
///
/// For text entry, consider using a [EditableText], which integrates with
/// on-screen keyboards and input method editors (IMEs).
///
/// The [KeyboardListener] is different from [RawKeyboardListener] in that
/// [KeyboardListener] uses the newer [HardwareKeyboard] API, which is
/// preferrable.
///
/// See also:
///
/// * [EditableText], which should be used instead of this widget for text
/// entry.
/// * [RawKeyboardListener], a similar widget based on the old [RawKeyboard]
/// API.
class
KeyboardListener
extends
StatelessWidget
{
/// Creates a widget that receives keyboard events.
///
/// For text entry, consider using a [EditableText], which integrates with
/// on-screen keyboards and input method editors (IMEs).
///
/// The [focusNode] and [child] arguments are required and must not be null.
///
/// The [autofocus] argument must not be null.
///
/// The `key` is an identifier for widgets, and is unrelated to keyboards.
/// See [Widget.key].
const
KeyboardListener
({
Key
?
key
,
required
this
.
focusNode
,
this
.
autofocus
=
false
,
this
.
includeSemantics
=
true
,
this
.
onKeyEvent
,
required
this
.
child
,
})
:
assert
(
focusNode
!=
null
),
assert
(
autofocus
!=
null
),
assert
(
includeSemantics
!=
null
),
assert
(
child
!=
null
),
super
(
key:
key
);
/// Controls whether this widget has keyboard focus.
final
FocusNode
focusNode
;
/// {@macro flutter.widgets.Focus.autofocus}
final
bool
autofocus
;
/// {@macro flutter.widgets.Focus.includeSemantics}
final
bool
includeSemantics
;
/// Called whenever this widget receives a keyboard event.
final
ValueChanged
<
KeyEvent
>?
onKeyEvent
;
/// The widget below this widget in the tree.
///
/// {@macro flutter.widgets.ProxyWidget.child}
final
Widget
child
;
@override
Widget
build
(
BuildContext
context
)
{
return
Focus
(
focusNode:
focusNode
,
autofocus:
autofocus
,
includeSemantics:
includeSemantics
,
onKeyEvent:
(
FocusNode
node
,
KeyEvent
event
)
{
onKeyEvent
?.
call
(
event
);
return
KeyEventResult
.
ignored
;
},
child:
child
,
);
}
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
super
.
debugFillProperties
(
properties
);
properties
.
add
(
DiagnosticsProperty
<
FocusNode
>(
'focusNode'
,
focusNode
));
}
}
packages/flutter/lib/src/widgets/raw_keyboard_listener.dart
View file @
5f792ba1
...
@@ -21,10 +21,16 @@ export 'package:flutter/services.dart' show RawKeyEvent;
...
@@ -21,10 +21,16 @@ export 'package:flutter/services.dart' show RawKeyEvent;
/// For text entry, consider using a [EditableText], which integrates with
/// For text entry, consider using a [EditableText], which integrates with
/// on-screen keyboards and input method editors (IMEs).
/// on-screen keyboards and input method editors (IMEs).
///
///
/// The [RawKeyboardListener] is different from [KeyboardListener] in that
/// [RawKeyboardListener] uses the legacy [RawKeyboard] API. Use
/// [KeyboardListener] if possible.
///
/// See also:
/// See also:
///
///
/// * [EditableText], which should be used instead of this widget for text
/// * [EditableText], which should be used instead of this widget for text
/// entry.
/// entry.
/// * [KeyboardListener], a similar widget based on the newer
/// [HardwareKeyboard] API.
class
RawKeyboardListener
extends
StatefulWidget
{
class
RawKeyboardListener
extends
StatefulWidget
{
/// Creates a widget that receives raw keyboard events.
/// Creates a widget that receives raw keyboard events.
///
///
...
...
packages/flutter/lib/src/widgets/shortcuts.dart
View file @
5f792ba1
...
@@ -194,13 +194,13 @@ abstract class ShortcutActivator {
...
@@ -194,13 +194,13 @@ abstract class ShortcutActivator {
/// event.
/// event.
///
///
/// For example, for `Ctrl-A`, it has to check if the event is a
/// For example, for `Ctrl-A`, it has to check if the event is a
/// [
Raw
KeyDownEvent], if either side of the Ctrl key is pressed, and none of
/// [KeyDownEvent], if either side of the Ctrl key is pressed, and none of
/// the Shift keys, Alt keys, or Meta keys are pressed; it doesn't have to
/// the Shift keys, Alt keys, or Meta keys are pressed; it doesn't have to
/// check if KeyA is pressed, since it's already guaranteed.
/// check if KeyA is pressed, since it's already guaranteed.
///
///
/// This method must not cause any side effects for the `state`. Typically
/// This method must not cause any side effects for the `state`. Typically
/// this is only used to query whether [
RawKeyboard.keysPressed] contains
/// this is only used to query whether [
HardwareKeyboard.logicalKeysPressed]
/// a key.
///
contains
a key.
///
///
/// Since [ShortcutActivator] accepts all event types, subclasses might want
/// Since [ShortcutActivator] accepts all event types, subclasses might want
/// to check the event type in [accepts].
/// to check the event type in [accepts].
...
@@ -314,11 +314,13 @@ class LogicalKeySet extends KeySet<LogicalKeyboardKey> with Diagnosticable
...
@@ -314,11 +314,13 @@ class LogicalKeySet extends KeySet<LogicalKeyboardKey> with Diagnosticable
@override
@override
bool
accepts
(
RawKeyEvent
event
,
RawKeyboard
state
)
{
bool
accepts
(
RawKeyEvent
event
,
RawKeyboard
state
)
{
if
(
event
is
!
RawKeyDownEvent
)
return
false
;
final
Set
<
LogicalKeyboardKey
>
collapsedRequired
=
LogicalKeyboardKey
.
collapseSynonyms
(
keys
);
final
Set
<
LogicalKeyboardKey
>
collapsedRequired
=
LogicalKeyboardKey
.
collapseSynonyms
(
keys
);
final
Set
<
LogicalKeyboardKey
>
collapsedPressed
=
LogicalKeyboardKey
.
collapseSynonyms
(
state
.
keysPressed
);
final
Set
<
LogicalKeyboardKey
>
collapsedPressed
=
LogicalKeyboardKey
.
collapseSynonyms
(
state
.
keysPressed
);
final
bool
keysEqual
=
collapsedRequired
.
difference
(
collapsedPressed
).
isEmpty
final
bool
keysEqual
=
collapsedRequired
.
difference
(
collapsedPressed
).
isEmpty
&&
collapsedRequired
.
length
==
collapsedPressed
.
length
;
&&
collapsedRequired
.
length
==
collapsedPressed
.
length
;
return
event
is
RawKeyDownEvent
&&
keysEqual
;
return
keysEqual
;
}
}
static
final
Set
<
LogicalKeyboardKey
>
_modifiers
=
<
LogicalKeyboardKey
>{
static
final
Set
<
LogicalKeyboardKey
>
_modifiers
=
<
LogicalKeyboardKey
>{
...
@@ -425,7 +427,8 @@ class ShortcutMapProperty extends DiagnosticsProperty<Map<ShortcutActivator, Int
...
@@ -425,7 +427,8 @@ class ShortcutMapProperty extends DiagnosticsProperty<Map<ShortcutActivator, Int
/// * [CharacterActivator], an activator that represents key combinations
/// * [CharacterActivator], an activator that represents key combinations
/// that result in the specified character, such as question mark.
/// that result in the specified character, such as question mark.
class SingleActivator with Diagnosticable implements ShortcutActivator {
class SingleActivator with Diagnosticable implements ShortcutActivator {
/// Create an activator of a trigger key and modifiers.
/// Triggered when the [trigger] key is pressed or repeated when the
/// modifiers are held.
///
///
/// The `trigger` should be the non-modifier key that is pressed after all the
/// The `trigger` should be the non-modifier key that is pressed after all the
/// modifiers, such as [LogicalKeyboardKey.keyC] as in `Ctrl+C`. It must not be
/// modifiers, such as [LogicalKeyboardKey.keyC] as in `Ctrl+C`. It must not be
...
@@ -434,6 +437,9 @@ class SingleActivator with Diagnosticable implements ShortcutActivator {
...
@@ -434,6 +437,9 @@ class SingleActivator with Diagnosticable implements ShortcutActivator {
/// The `control`, `shift`, `alt`, and `meta` flags represent whether
/// The `control`, `shift`, `alt`, and `meta` flags represent whether
/// the respect modifier keys should be held (true) or released (false)
/// the respect modifier keys should be held (true) or released (false)
///
///
/// On each [RawKeyDownEvent] of the [trigger] key, this activator checks
/// whether the specified modifier conditions are met.
///
/// {@tool dartpad --template=stateful_widget_scaffold_center}
/// {@tool dartpad --template=stateful_widget_scaffold_center}
/// In the following example, the shortcut `Control + C` increases the counter:
/// In the following example, the shortcut `Control + C` increases the counter:
///
///
...
@@ -811,17 +817,7 @@ class ShortcutManager extends ChangeNotifier with Diagnosticable {
...
@@ -811,17 +817,7 @@ class ShortcutManager extends ChangeNotifier with Diagnosticable {
/// must be mapped to an [Action], and the [Action] must be enabled.
/// must be mapped to an [Action], and the [Action] must be enabled.
@protected
@protected
KeyEventResult
handleKeypress
(
BuildContext
context
,
RawKeyEvent
event
)
{
KeyEventResult
handleKeypress
(
BuildContext
context
,
RawKeyEvent
event
)
{
if
(
event
is
!
RawKeyDownEvent
)
{
return
KeyEventResult
.
ignored
;
}
assert
(
context
!=
null
);
assert
(
context
!=
null
);
assert
(
RawKeyboard
.
instance
.
keysPressed
.
isNotEmpty
,
'Received a key down event when no keys are in keysPressed. '
"This state can occur if the key event being sent doesn't properly "
'set its modifier flags. This was the event:
$event
and its data: '
'
${event.data}
'
,
);
final
Intent
?
matchedIntent
=
_find
(
event
,
RawKeyboard
.
instance
);
final
Intent
?
matchedIntent
=
_find
(
event
,
RawKeyboard
.
instance
);
if
(
matchedIntent
!=
null
)
{
if
(
matchedIntent
!=
null
)
{
final
BuildContext
?
primaryContext
=
primaryFocus
?.
context
;
final
BuildContext
?
primaryContext
=
primaryFocus
?.
context
;
...
@@ -1285,4 +1281,4 @@ class CallbackShortcuts extends StatelessWidget {
...
@@ -1285,4 +1281,4 @@ class CallbackShortcuts extends StatelessWidget {
child:
child
,
child:
child
,
);
);
}
}
}
}
\ No newline at end of file
packages/flutter/lib/widgets.dart
View file @
5f792ba1
...
@@ -63,6 +63,7 @@ export 'src/widgets/inherited_model.dart';
...
@@ -63,6 +63,7 @@ export 'src/widgets/inherited_model.dart';
export
'src/widgets/inherited_notifier.dart'
;
export
'src/widgets/inherited_notifier.dart'
;
export
'src/widgets/inherited_theme.dart'
;
export
'src/widgets/inherited_theme.dart'
;
export
'src/widgets/interactive_viewer.dart'
;
export
'src/widgets/interactive_viewer.dart'
;
export
'src/widgets/keyboard_listener.dart'
;
export
'src/widgets/layout_builder.dart'
;
export
'src/widgets/layout_builder.dart'
;
export
'src/widgets/list_wheel_scroll_view.dart'
;
export
'src/widgets/list_wheel_scroll_view.dart'
;
export
'src/widgets/localizations.dart'
;
export
'src/widgets/localizations.dart'
;
...
...
packages/flutter/test/cupertino/text_field_test.dart
View file @
5f792ba1
...
@@ -4342,7 +4342,7 @@ void main() {
...
@@ -4342,7 +4342,7 @@ void main() {
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
pump
();
await
tester
.
pump
();
expect
(
focusNode3
.
hasPrimaryFocus
,
isTrue
);
expect
(
focusNode3
.
hasPrimaryFocus
,
isTrue
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Scrolling shortcuts are disabled in text fields'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Scrolling shortcuts are disabled in text fields'
,
(
WidgetTester
tester
)
async
{
bool
scrollInvoked
=
false
;
bool
scrollInvoked
=
false
;
...
@@ -4375,7 +4375,7 @@ void main() {
...
@@ -4375,7 +4375,7 @@ void main() {
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowDown
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowDown
);
expect
(
scrollInvoked
,
isFalse
);
expect
(
scrollInvoked
,
isFalse
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Cupertino text field semantics'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Cupertino text field semantics'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
...
...
packages/flutter/test/material/text_field_test.dart
View file @
5f792ba1
...
@@ -4725,7 +4725,7 @@ void main() {
...
@@ -4725,7 +4725,7 @@ void main() {
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowLeft
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowLeft
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
shift
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
shift
);
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
-
1
);
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
-
1
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Shift test 2'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Shift test 2'
,
(
WidgetTester
tester
)
async
{
await
setupWidget
(
tester
);
await
setupWidget
(
tester
);
...
@@ -4743,7 +4743,7 @@ void main() {
...
@@ -4743,7 +4743,7 @@ void main() {
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
1
);
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
1
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Control Shift test'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Control Shift test'
,
(
WidgetTester
tester
)
async
{
await
setupWidget
(
tester
);
await
setupWidget
(
tester
);
...
@@ -4760,7 +4760,7 @@ void main() {
...
@@ -4760,7 +4760,7 @@ void main() {
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
5
);
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
5
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Down and up test'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Down and up test'
,
(
WidgetTester
tester
)
async
{
await
setupWidget
(
tester
);
await
setupWidget
(
tester
);
...
@@ -4787,7 +4787,7 @@ void main() {
...
@@ -4787,7 +4787,7 @@ void main() {
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
0
);
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
0
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Down and up test 2'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Down and up test 2'
,
(
WidgetTester
tester
)
async
{
await
setupWidget
(
tester
);
await
setupWidget
(
tester
);
...
@@ -4843,7 +4843,7 @@ void main() {
...
@@ -4843,7 +4843,7 @@ void main() {
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
-
5
);
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
-
5
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Read only keyboard selection test'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Read only keyboard selection test'
,
(
WidgetTester
tester
)
async
{
final
TextEditingController
controller
=
TextEditingController
(
text:
'readonly'
);
final
TextEditingController
controller
=
TextEditingController
(
text:
'readonly'
);
...
@@ -4863,7 +4863,7 @@ void main() {
...
@@ -4863,7 +4863,7 @@ void main() {
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
shift
);
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
shift
);
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
arrowLeft
);
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
arrowLeft
);
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
-
1
);
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
-
1
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
},
skip:
areKeyEventsHandledByPlatform
);
},
skip:
areKeyEventsHandledByPlatform
);
testWidgets
(
'Copy paste test'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Copy paste test'
,
(
WidgetTester
tester
)
async
{
...
@@ -4938,7 +4938,7 @@ void main() {
...
@@ -4938,7 +4938,7 @@ void main() {
const
String
expected
=
'a biga big house
\n
jumped over a mouse'
;
const
String
expected
=
'a biga big house
\n
jumped over a mouse'
;
expect
(
find
.
text
(
expected
),
findsOneWidget
,
reason:
'Because text contains
${controller.text}
'
);
expect
(
find
.
text
(
expected
),
findsOneWidget
,
reason:
'Because text contains
${controller.text}
'
);
},
skip:
areKeyEventsHandledByPlatform
);
},
skip:
areKeyEventsHandledByPlatform
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Copy paste obscured text test'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Copy paste obscured text test'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
();
final
FocusNode
focusNode
=
FocusNode
();
...
@@ -5012,7 +5012,7 @@ void main() {
...
@@ -5012,7 +5012,7 @@ void main() {
const
String
expected
=
'a biga big house jumped over a mouse'
;
const
String
expected
=
'a biga big house jumped over a mouse'
;
expect
(
find
.
text
(
expected
),
findsOneWidget
,
reason:
'Because text contains
${controller.text}
'
);
expect
(
find
.
text
(
expected
),
findsOneWidget
,
reason:
'Because text contains
${controller.text}
'
);
},
skip:
areKeyEventsHandledByPlatform
);
},
skip:
areKeyEventsHandledByPlatform
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
// Regressing test for https://github.com/flutter/flutter/issues/78219
// Regressing test for https://github.com/flutter/flutter/issues/78219
testWidgets
(
'Paste does not crash when the section is inValid'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Paste does not crash when the section is inValid'
,
(
WidgetTester
tester
)
async
{
...
@@ -5063,7 +5063,7 @@ void main() {
...
@@ -5063,7 +5063,7 @@ void main() {
// Do nothing.
// Do nothing.
expect
(
find
.
text
(
clipboardContent
),
findsNothing
);
expect
(
find
.
text
(
clipboardContent
),
findsNothing
);
expect
(
controller
.
selection
,
const
TextSelection
.
collapsed
(
offset:
-
1
));
expect
(
controller
.
selection
,
const
TextSelection
.
collapsed
(
offset:
-
1
));
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Cut test'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Cut test'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
();
final
FocusNode
focusNode
=
FocusNode
();
...
@@ -5139,7 +5139,7 @@ void main() {
...
@@ -5139,7 +5139,7 @@ void main() {
const
String
expected
=
' housa bige
\n
jumped over a mouse'
;
const
String
expected
=
' housa bige
\n
jumped over a mouse'
;
expect
(
find
.
text
(
expected
),
findsOneWidget
);
expect
(
find
.
text
(
expected
),
findsOneWidget
);
},
skip:
areKeyEventsHandledByPlatform
);
},
skip:
areKeyEventsHandledByPlatform
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Cut obscured text test'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Cut obscured text test'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
();
final
FocusNode
focusNode
=
FocusNode
();
...
@@ -5214,7 +5214,7 @@ void main() {
...
@@ -5214,7 +5214,7 @@ void main() {
const
String
expected
=
' housa bige jumped over a mouse'
;
const
String
expected
=
' housa bige jumped over a mouse'
;
expect
(
find
.
text
(
expected
),
findsOneWidget
);
expect
(
find
.
text
(
expected
),
findsOneWidget
);
},
skip:
areKeyEventsHandledByPlatform
);
},
skip:
areKeyEventsHandledByPlatform
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Select all test'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Select all test'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
();
final
FocusNode
focusNode
=
FocusNode
();
...
@@ -5263,7 +5263,7 @@ void main() {
...
@@ -5263,7 +5263,7 @@ void main() {
const
String
expected
=
''
;
const
String
expected
=
''
;
expect
(
find
.
text
(
expected
),
findsOneWidget
);
expect
(
find
.
text
(
expected
),
findsOneWidget
);
},
skip:
areKeyEventsHandledByPlatform
);
},
skip:
areKeyEventsHandledByPlatform
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Delete test'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Delete test'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
();
final
FocusNode
focusNode
=
FocusNode
();
...
@@ -5315,7 +5315,7 @@ void main() {
...
@@ -5315,7 +5315,7 @@ void main() {
const
String
expected2
=
''
;
const
String
expected2
=
''
;
expect
(
find
.
text
(
expected2
),
findsOneWidget
);
expect
(
find
.
text
(
expected2
),
findsOneWidget
);
},
skip:
areKeyEventsHandledByPlatform
);
},
skip:
areKeyEventsHandledByPlatform
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Changing positions of text fields'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Changing positions of text fields'
,
(
WidgetTester
tester
)
async
{
...
@@ -5407,7 +5407,7 @@ void main() {
...
@@ -5407,7 +5407,7 @@ void main() {
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
shift
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
shift
);
expect
(
c1
.
selection
.
extentOffset
-
c1
.
selection
.
baseOffset
,
-
10
);
expect
(
c1
.
selection
.
extentOffset
-
c1
.
selection
.
baseOffset
,
-
10
);
},
skip:
areKeyEventsHandledByPlatform
);
},
skip:
areKeyEventsHandledByPlatform
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Changing focus test'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Changing focus test'
,
(
WidgetTester
tester
)
async
{
...
@@ -5482,7 +5482,7 @@ void main() {
...
@@ -5482,7 +5482,7 @@ void main() {
expect
(
c1
.
selection
.
extentOffset
-
c1
.
selection
.
baseOffset
,
0
);
expect
(
c1
.
selection
.
extentOffset
-
c1
.
selection
.
baseOffset
,
0
);
expect
(
c2
.
selection
.
extentOffset
-
c2
.
selection
.
baseOffset
,
-
5
);
expect
(
c2
.
selection
.
extentOffset
-
c2
.
selection
.
baseOffset
,
-
5
);
},
skip:
areKeyEventsHandledByPlatform
);
},
skip:
areKeyEventsHandledByPlatform
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Caret works when maxLines is null'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Caret works when maxLines is null'
,
(
WidgetTester
tester
)
async
{
final
TextEditingController
controller
=
TextEditingController
();
final
TextEditingController
controller
=
TextEditingController
();
...
...
packages/flutter/test/rendering/editable_test.dart
View file @
5f792ba1
...
@@ -10,6 +10,11 @@ import 'package:flutter/material.dart';
...
@@ -10,6 +10,11 @@ import 'package:flutter/material.dart';
import
'package:flutter/rendering.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:meta/meta.dart'
;
// The test_api package is not for general use... it's literally for our use.
// ignore: deprecated_member_use
import
'package:test_api/test_api.dart'
as
test_package
;
import
'../rendering/mock_canvas.dart'
;
import
'../rendering/mock_canvas.dart'
;
import
'../rendering/recording_canvas.dart'
;
import
'../rendering/recording_canvas.dart'
;
...
@@ -29,6 +34,38 @@ class FakeEditableTextState with TextSelectionDelegate {
...
@@ -29,6 +34,38 @@ class FakeEditableTextState with TextSelectionDelegate {
void
bringIntoView
(
TextPosition
position
)
{
}
void
bringIntoView
(
TextPosition
position
)
{
}
}
}
@isTest
void
testVariants
(
String
description
,
AsyncValueGetter
<
void
>
callback
,
{
bool
?
skip
,
test_package
.
Timeout
?
timeout
,
TestVariant
<
Object
?>
variant
=
const
DefaultTestVariant
(),
dynamic
tags
,
})
{
assert
(
variant
!=
null
);
assert
(
variant
.
values
.
isNotEmpty
,
'There must be at least one value to test in the testing variant.'
);
for
(
final
dynamic
value
in
variant
.
values
)
{
final
String
variationDescription
=
variant
.
describeValue
(
value
);
final
String
combinedDescription
=
variationDescription
.
isNotEmpty
?
'
$description
(
$variationDescription
)'
:
description
;
test
(
combinedDescription
,
()
async
{
Object
?
memento
;
try
{
memento
=
await
variant
.
setUp
(
value
);
await
callback
();
}
finally
{
await
variant
.
tearDown
(
value
,
memento
);
}
},
skip:
skip
,
timeout:
timeout
,
tags:
tags
,
);
}
}
void
main
(
)
{
void
main
(
)
{
test
(
'RenderEditable respects clipBehavior'
,
()
{
test
(
'RenderEditable respects clipBehavior'
,
()
{
const
BoxConstraints
viewport
=
BoxConstraints
(
maxHeight:
100.0
,
maxWidth:
100.0
);
const
BoxConstraints
viewport
=
BoxConstraints
(
maxHeight:
100.0
,
maxWidth:
100.0
);
...
@@ -1337,7 +1374,7 @@ void main() {
...
@@ -1337,7 +1374,7 @@ void main() {
expect
(
currentSelection
.
extentOffset
,
1
);
expect
(
currentSelection
.
extentOffset
,
1
);
},
skip:
isBrowser
);
// https://github.com/flutter/flutter/issues/58068
},
skip:
isBrowser
);
// https://github.com/flutter/flutter/issues/58068
test
(
'respects enableInteractiveSelection'
,
()
async
{
test
Variants
(
'respects enableInteractiveSelection'
,
()
async
{
const
String
text
=
'012345'
;
const
String
text
=
'012345'
;
final
TextSelectionDelegate
delegate
=
FakeEditableTextState
()
final
TextSelectionDelegate
delegate
=
FakeEditableTextState
()
..
textEditingValue
=
const
TextEditingValue
(
text:
text
);
..
textEditingValue
=
const
TextEditingValue
(
text:
text
);
...
@@ -1397,7 +1434,7 @@ void main() {
...
@@ -1397,7 +1434,7 @@ void main() {
await
simulateKeyUpEvent
(
wordModifier
);
await
simulateKeyUpEvent
(
wordModifier
);
await
simulateKeyUpEvent
(
LogicalKeyboardKey
.
shift
);
await
simulateKeyUpEvent
(
LogicalKeyboardKey
.
shift
);
},
skip:
isBrowser
);
// https://github.com/flutter/flutter/issues/58068
},
skip:
isBrowser
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
// https://github.com/flutter/flutter/issues/58068
group
(
'delete'
,
()
{
group
(
'delete'
,
()
{
test
(
'when as a non-collapsed selection, it should delete a selection'
,
()
async
{
test
(
'when as a non-collapsed selection, it should delete a selection'
,
()
async
{
...
...
packages/flutter/test/services/hardware_keyboard_test.dart
0 → 100644
View file @
5f792ba1
// 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/services.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
testWidgets
(
'HardwareKeyboard records pressed keys and enabled locks'
,
(
WidgetTester
tester
)
async
{
await
simulateKeyDownEvent
(
LogicalKeyboardKey
.
numLock
,
platform:
'windows'
);
expect
(
HardwareKeyboard
.
instance
.
physicalKeysPressed
,
equals
(<
PhysicalKeyboardKey
>{
PhysicalKeyboardKey
.
numLock
}));
expect
(
HardwareKeyboard
.
instance
.
logicalKeysPressed
,
equals
(<
LogicalKeyboardKey
>{
LogicalKeyboardKey
.
numLock
}));
expect
(
HardwareKeyboard
.
instance
.
lockModesEnabled
,
equals
(<
KeyboardLockMode
>{
KeyboardLockMode
.
numLock
}));
await
simulateKeyDownEvent
(
LogicalKeyboardKey
.
numpad1
,
platform:
'windows'
);
expect
(
HardwareKeyboard
.
instance
.
physicalKeysPressed
,
equals
(<
PhysicalKeyboardKey
>{
PhysicalKeyboardKey
.
numLock
,
PhysicalKeyboardKey
.
numpad1
}));
expect
(
HardwareKeyboard
.
instance
.
logicalKeysPressed
,
equals
(<
LogicalKeyboardKey
>{
LogicalKeyboardKey
.
numLock
,
LogicalKeyboardKey
.
numpad1
}));
expect
(
HardwareKeyboard
.
instance
.
lockModesEnabled
,
equals
(<
KeyboardLockMode
>{
KeyboardLockMode
.
numLock
}));
await
simulateKeyRepeatEvent
(
LogicalKeyboardKey
.
numpad1
,
platform:
'windows'
);
expect
(
HardwareKeyboard
.
instance
.
physicalKeysPressed
,
equals
(<
PhysicalKeyboardKey
>{
PhysicalKeyboardKey
.
numLock
,
PhysicalKeyboardKey
.
numpad1
}));
expect
(
HardwareKeyboard
.
instance
.
logicalKeysPressed
,
equals
(<
LogicalKeyboardKey
>{
LogicalKeyboardKey
.
numLock
,
LogicalKeyboardKey
.
numpad1
}));
expect
(
HardwareKeyboard
.
instance
.
lockModesEnabled
,
equals
(<
KeyboardLockMode
>{
KeyboardLockMode
.
numLock
}));
await
simulateKeyUpEvent
(
LogicalKeyboardKey
.
numLock
);
expect
(
HardwareKeyboard
.
instance
.
physicalKeysPressed
,
equals
(<
PhysicalKeyboardKey
>{
PhysicalKeyboardKey
.
numpad1
}));
expect
(
HardwareKeyboard
.
instance
.
logicalKeysPressed
,
equals
(<
LogicalKeyboardKey
>{
LogicalKeyboardKey
.
numpad1
}));
expect
(
HardwareKeyboard
.
instance
.
lockModesEnabled
,
equals
(<
KeyboardLockMode
>{
KeyboardLockMode
.
numLock
}));
await
simulateKeyDownEvent
(
LogicalKeyboardKey
.
numLock
,
platform:
'windows'
);
expect
(
HardwareKeyboard
.
instance
.
physicalKeysPressed
,
equals
(<
PhysicalKeyboardKey
>{
PhysicalKeyboardKey
.
numLock
,
PhysicalKeyboardKey
.
numpad1
}));
expect
(
HardwareKeyboard
.
instance
.
logicalKeysPressed
,
equals
(<
LogicalKeyboardKey
>{
LogicalKeyboardKey
.
numLock
,
LogicalKeyboardKey
.
numpad1
}));
expect
(
HardwareKeyboard
.
instance
.
lockModesEnabled
,
equals
(<
KeyboardLockMode
>{}));
await
simulateKeyUpEvent
(
LogicalKeyboardKey
.
numpad1
,
platform:
'windows'
);
expect
(
HardwareKeyboard
.
instance
.
physicalKeysPressed
,
equals
(<
PhysicalKeyboardKey
>{
PhysicalKeyboardKey
.
numLock
}));
expect
(
HardwareKeyboard
.
instance
.
logicalKeysPressed
,
equals
(<
LogicalKeyboardKey
>{
LogicalKeyboardKey
.
numLock
}));
expect
(
HardwareKeyboard
.
instance
.
lockModesEnabled
,
equals
(<
KeyboardLockMode
>{}));
await
simulateKeyUpEvent
(
LogicalKeyboardKey
.
numLock
,
platform:
'windows'
);
expect
(
HardwareKeyboard
.
instance
.
physicalKeysPressed
,
equals
(<
PhysicalKeyboardKey
>{}));
expect
(
HardwareKeyboard
.
instance
.
logicalKeysPressed
,
equals
(<
LogicalKeyboardKey
>{}));
expect
(
HardwareKeyboard
.
instance
.
lockModesEnabled
,
equals
(<
KeyboardLockMode
>{}));
},
variant:
KeySimulatorTransitModeVariant
.
keyDataThenRawKeyData
());
testWidgets
(
'Dispatch events to all handlers'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
();
final
List
<
int
>
logs
=
<
int
>[];
await
tester
.
pumpWidget
(
KeyboardListener
(
autofocus:
true
,
focusNode:
focusNode
,
child:
Container
(),
onKeyEvent:
(
KeyEvent
event
)
{
logs
.
add
(
1
);
},
),
);
// Only the Service binding handler.
expect
(
await
simulateKeyDownEvent
(
LogicalKeyboardKey
.
keyA
),
false
);
expect
(
logs
,
<
int
>[
1
]);
logs
.
clear
();
// Add a handler.
bool
handler2Result
=
false
;
bool
handler2
(
KeyEvent
event
)
{
logs
.
add
(
2
);
return
handler2Result
;
}
HardwareKeyboard
.
instance
.
addHandler
(
handler2
);
expect
(
await
simulateKeyUpEvent
(
LogicalKeyboardKey
.
keyA
),
false
);
expect
(
logs
,
<
int
>[
2
,
1
]);
logs
.
clear
();
handler2Result
=
true
;
expect
(
await
simulateKeyDownEvent
(
LogicalKeyboardKey
.
keyA
),
true
);
expect
(
logs
,
<
int
>[
2
,
1
]);
logs
.
clear
();
// Add another handler.
handler2Result
=
false
;
bool
handler3Result
=
false
;
bool
handler3
(
KeyEvent
event
)
{
logs
.
add
(
3
);
return
handler3Result
;
}
HardwareKeyboard
.
instance
.
addHandler
(
handler3
);
expect
(
await
simulateKeyUpEvent
(
LogicalKeyboardKey
.
keyA
),
false
);
expect
(
logs
,
<
int
>[
2
,
3
,
1
]);
logs
.
clear
();
handler2Result
=
true
;
expect
(
await
simulateKeyDownEvent
(
LogicalKeyboardKey
.
keyA
),
true
);
expect
(
logs
,
<
int
>[
2
,
3
,
1
]);
logs
.
clear
();
handler3Result
=
true
;
expect
(
await
simulateKeyUpEvent
(
LogicalKeyboardKey
.
keyA
),
true
);
expect
(
logs
,
<
int
>[
2
,
3
,
1
]);
logs
.
clear
();
// Add handler2 again.
HardwareKeyboard
.
instance
.
addHandler
(
handler2
);
handler3Result
=
false
;
handler2Result
=
false
;
expect
(
await
simulateKeyDownEvent
(
LogicalKeyboardKey
.
keyA
),
false
);
expect
(
logs
,
<
int
>[
2
,
3
,
2
,
1
]);
logs
.
clear
();
handler2Result
=
true
;
expect
(
await
simulateKeyUpEvent
(
LogicalKeyboardKey
.
keyA
),
true
);
expect
(
logs
,
<
int
>[
2
,
3
,
2
,
1
]);
logs
.
clear
();
// Remove handler2 once.
HardwareKeyboard
.
instance
.
removeHandler
(
handler2
);
expect
(
await
simulateKeyDownEvent
(
LogicalKeyboardKey
.
keyA
),
true
);
expect
(
logs
,
<
int
>[
3
,
2
,
1
]);
logs
.
clear
();
},
variant:
KeySimulatorTransitModeVariant
.
all
());
}
packages/flutter/test/services/raw_keyboard_test.dart
View file @
5f792ba1
...
@@ -201,7 +201,7 @@ void main() {
...
@@ -201,7 +201,7 @@ void main() {
testWidgets
(
'keysPressed modifiers are synchronized with key events on macOS'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'keysPressed modifiers are synchronized with key events on macOS'
,
(
WidgetTester
tester
)
async
{
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
// Generate the data for a regular key down event.
// Generate the data for a regular key down event.
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
getKeyData
(
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
get
Raw
KeyData
(
LogicalKeyboardKey
.
keyA
,
LogicalKeyboardKey
.
keyA
,
platform:
'macos'
,
platform:
'macos'
,
isDown:
true
,
isDown:
true
,
...
@@ -226,7 +226,7 @@ void main() {
...
@@ -226,7 +226,7 @@ void main() {
testWidgets
(
'keysPressed modifiers are synchronized with key events on iOS'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'keysPressed modifiers are synchronized with key events on iOS'
,
(
WidgetTester
tester
)
async
{
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
// Generate the data for a regular key down event.
// Generate the data for a regular key down event.
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
getKeyData
(
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
get
Raw
KeyData
(
LogicalKeyboardKey
.
keyA
,
LogicalKeyboardKey
.
keyA
,
platform:
'ios'
,
platform:
'ios'
,
isDown:
true
,
isDown:
true
,
...
@@ -251,7 +251,7 @@ void main() {
...
@@ -251,7 +251,7 @@ void main() {
testWidgets
(
'keysPressed modifiers are synchronized with key events on Windows'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'keysPressed modifiers are synchronized with key events on Windows'
,
(
WidgetTester
tester
)
async
{
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
// Generate the data for a regular key down event.
// Generate the data for a regular key down event.
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
getKeyData
(
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
get
Raw
KeyData
(
LogicalKeyboardKey
.
keyA
,
LogicalKeyboardKey
.
keyA
,
platform:
'windows'
,
platform:
'windows'
,
isDown:
true
,
isDown:
true
,
...
@@ -276,7 +276,7 @@ void main() {
...
@@ -276,7 +276,7 @@ void main() {
testWidgets
(
'keysPressed modifiers are synchronized with key events on android'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'keysPressed modifiers are synchronized with key events on android'
,
(
WidgetTester
tester
)
async
{
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
// Generate the data for a regular key down event.
// Generate the data for a regular key down event.
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
getKeyData
(
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
get
Raw
KeyData
(
LogicalKeyboardKey
.
keyA
,
LogicalKeyboardKey
.
keyA
,
platform:
'android'
,
platform:
'android'
,
isDown:
true
,
isDown:
true
,
...
@@ -301,7 +301,7 @@ void main() {
...
@@ -301,7 +301,7 @@ void main() {
testWidgets
(
'keysPressed modifiers are synchronized with key events on fuchsia'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'keysPressed modifiers are synchronized with key events on fuchsia'
,
(
WidgetTester
tester
)
async
{
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
// Generate the data for a regular key down event.
// Generate the data for a regular key down event.
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
getKeyData
(
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
get
Raw
KeyData
(
LogicalKeyboardKey
.
keyA
,
LogicalKeyboardKey
.
keyA
,
platform:
'fuchsia'
,
platform:
'fuchsia'
,
isDown:
true
,
isDown:
true
,
...
@@ -326,7 +326,7 @@ void main() {
...
@@ -326,7 +326,7 @@ void main() {
testWidgets
(
'keysPressed modifiers are synchronized with key events on Linux GLFW'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'keysPressed modifiers are synchronized with key events on Linux GLFW'
,
(
WidgetTester
tester
)
async
{
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
// Generate the data for a regular key down event.
// Generate the data for a regular key down event.
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
getKeyData
(
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
get
Raw
KeyData
(
LogicalKeyboardKey
.
keyA
,
LogicalKeyboardKey
.
keyA
,
platform:
'linux'
,
platform:
'linux'
,
isDown:
true
,
isDown:
true
,
...
@@ -359,7 +359,7 @@ void main() {
...
@@ -359,7 +359,7 @@ void main() {
// Generate the data for a regular key down event. Change the modifiers so
// Generate the data for a regular key down event. Change the modifiers so
// that they show the shift key as already down when this event is
// that they show the shift key as already down when this event is
// received, but it's not in keysPressed yet.
// received, but it's not in keysPressed yet.
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
getKeyData
(
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
get
Raw
KeyData
(
LogicalKeyboardKey
.
keyA
,
LogicalKeyboardKey
.
keyA
,
platform:
'web'
,
platform:
'web'
,
isDown:
true
,
isDown:
true
,
...
@@ -384,7 +384,7 @@ void main() {
...
@@ -384,7 +384,7 @@ void main() {
// Generate the data for a regular key up event. Don't set the modifiers
// Generate the data for a regular key up event. Don't set the modifiers
// for shift so that they show the shift key as already up when this event
// for shift so that they show the shift key as already up when this event
// is received, and it's in keysPressed.
// is received, and it's in keysPressed.
final
Map
<
String
,
dynamic
>
data2
=
KeyEventSimulator
.
getKeyData
(
final
Map
<
String
,
dynamic
>
data2
=
KeyEventSimulator
.
get
Raw
KeyData
(
LogicalKeyboardKey
.
keyA
,
LogicalKeyboardKey
.
keyA
,
platform:
'web'
,
platform:
'web'
,
isDown:
false
,
isDown:
false
,
...
@@ -403,7 +403,7 @@ void main() {
...
@@ -403,7 +403,7 @@ void main() {
);
);
// Press right modifier key
// Press right modifier key
final
Map
<
String
,
dynamic
>
data3
=
KeyEventSimulator
.
getKeyData
(
final
Map
<
String
,
dynamic
>
data3
=
KeyEventSimulator
.
get
Raw
KeyData
(
LogicalKeyboardKey
.
shiftRight
,
LogicalKeyboardKey
.
shiftRight
,
platform:
'web'
,
platform:
'web'
,
isDown:
true
,
isDown:
true
,
...
@@ -425,7 +425,7 @@ void main() {
...
@@ -425,7 +425,7 @@ void main() {
);
);
// Release the key
// Release the key
final
Map
<
String
,
dynamic
>
data4
=
KeyEventSimulator
.
getKeyData
(
final
Map
<
String
,
dynamic
>
data4
=
KeyEventSimulator
.
get
Raw
KeyData
(
LogicalKeyboardKey
.
shiftRight
,
LogicalKeyboardKey
.
shiftRight
,
platform:
'web'
,
platform:
'web'
,
isDown:
false
,
isDown:
false
,
...
@@ -447,7 +447,7 @@ void main() {
...
@@ -447,7 +447,7 @@ void main() {
testWidgets
(
'sided modifiers without a side set return all sides on Android'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'sided modifiers without a side set return all sides on Android'
,
(
WidgetTester
tester
)
async
{
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
// Generate the data for a regular key down event.
// Generate the data for a regular key down event.
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
getKeyData
(
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
get
Raw
KeyData
(
LogicalKeyboardKey
.
keyA
,
LogicalKeyboardKey
.
keyA
,
platform:
'android'
,
platform:
'android'
,
isDown:
true
,
isDown:
true
,
...
@@ -485,7 +485,7 @@ void main() {
...
@@ -485,7 +485,7 @@ void main() {
testWidgets
(
'sided modifiers without a side set return all sides on macOS'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'sided modifiers without a side set return all sides on macOS'
,
(
WidgetTester
tester
)
async
{
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
// Generate the data for a regular key down event.
// Generate the data for a regular key down event.
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
getKeyData
(
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
get
Raw
KeyData
(
LogicalKeyboardKey
.
keyA
,
LogicalKeyboardKey
.
keyA
,
platform:
'macos'
,
platform:
'macos'
,
isDown:
true
,
isDown:
true
,
...
@@ -523,7 +523,7 @@ void main() {
...
@@ -523,7 +523,7 @@ void main() {
testWidgets
(
'sided modifiers without a side set return all sides on iOS'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'sided modifiers without a side set return all sides on iOS'
,
(
WidgetTester
tester
)
async
{
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
// Generate the data for a regular key down event.
// Generate the data for a regular key down event.
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
getKeyData
(
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
get
Raw
KeyData
(
LogicalKeyboardKey
.
keyA
,
LogicalKeyboardKey
.
keyA
,
platform:
'ios'
,
platform:
'ios'
,
isDown:
true
,
isDown:
true
,
...
@@ -561,7 +561,7 @@ void main() {
...
@@ -561,7 +561,7 @@ void main() {
testWidgets
(
'sided modifiers without a side set return all sides on Windows'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'sided modifiers without a side set return all sides on Windows'
,
(
WidgetTester
tester
)
async
{
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
// Generate the data for a regular key down event.
// Generate the data for a regular key down event.
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
getKeyData
(
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
get
Raw
KeyData
(
LogicalKeyboardKey
.
keyA
,
LogicalKeyboardKey
.
keyA
,
platform:
'windows'
,
platform:
'windows'
,
isDown:
true
,
isDown:
true
,
...
@@ -597,7 +597,7 @@ void main() {
...
@@ -597,7 +597,7 @@ void main() {
testWidgets
(
'sided modifiers without a side set return all sides on Linux GLFW'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'sided modifiers without a side set return all sides on Linux GLFW'
,
(
WidgetTester
tester
)
async
{
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
// Generate the data for a regular key down event.
// Generate the data for a regular key down event.
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
getKeyData
(
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
get
Raw
KeyData
(
LogicalKeyboardKey
.
keyA
,
LogicalKeyboardKey
.
keyA
,
platform:
'linux'
,
platform:
'linux'
,
isDown:
true
,
isDown:
true
,
...
@@ -636,7 +636,7 @@ void main() {
...
@@ -636,7 +636,7 @@ void main() {
testWidgets
(
'sided modifiers without a side set return left sides on web'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'sided modifiers without a side set return left sides on web'
,
(
WidgetTester
tester
)
async
{
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
// Generate the data for a regular key down event.
// Generate the data for a regular key down event.
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
getKeyData
(
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
get
Raw
KeyData
(
LogicalKeyboardKey
.
keyA
,
LogicalKeyboardKey
.
keyA
,
platform:
'web'
,
platform:
'web'
,
isDown:
true
,
isDown:
true
,
...
@@ -703,6 +703,70 @@ void main() {
...
@@ -703,6 +703,70 @@ void main() {
)),
)),
);
);
});
});
testWidgets
(
'Dispatch events to all handlers'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
();
final
List
<
int
>
logs
=
<
int
>[];
await
tester
.
pumpWidget
(
RawKeyboardListener
(
autofocus:
true
,
focusNode:
focusNode
,
child:
Container
(),
onKey:
(
RawKeyEvent
event
)
{
logs
.
add
(
1
);
},
),
);
// Only the Service binding handler.
expect
(
await
simulateKeyDownEvent
(
LogicalKeyboardKey
.
keyA
),
false
);
expect
(
logs
,
<
int
>[
1
]);
logs
.
clear
();
// Add a handler.
void
handler2
(
RawKeyEvent
event
)
{
logs
.
add
(
2
);
}
RawKeyboard
.
instance
.
addListener
(
handler2
);
expect
(
await
simulateKeyUpEvent
(
LogicalKeyboardKey
.
keyA
),
false
);
expect
(
logs
,
<
int
>[
1
,
2
]);
logs
.
clear
();
// Add another handler.
void
handler3
(
RawKeyEvent
event
)
{
logs
.
add
(
3
);
}
RawKeyboard
.
instance
.
addListener
(
handler3
);
expect
(
await
simulateKeyDownEvent
(
LogicalKeyboardKey
.
keyA
),
false
);
expect
(
logs
,
<
int
>[
1
,
2
,
3
]);
logs
.
clear
();
// Add handler2 again.
RawKeyboard
.
instance
.
addListener
(
handler2
);
expect
(
await
simulateKeyUpEvent
(
LogicalKeyboardKey
.
keyA
),
false
);
expect
(
logs
,
<
int
>[
1
,
2
,
3
,
2
]);
logs
.
clear
();
// Remove handler2 once.
RawKeyboard
.
instance
.
removeListener
(
handler2
);
expect
(
await
simulateKeyDownEvent
(
LogicalKeyboardKey
.
keyA
),
false
);
expect
(
logs
,
<
int
>[
1
,
3
,
2
]);
logs
.
clear
();
},
variant:
KeySimulatorTransitModeVariant
.
all
());
});
});
group
(
'RawKeyEventDataAndroid'
,
()
{
group
(
'RawKeyEventDataAndroid'
,
()
{
...
@@ -941,7 +1005,7 @@ void main() {
...
@@ -941,7 +1005,7 @@ void main() {
testWidgets
(
'Key events are responded to correctly.'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Key events are responded to correctly.'
,
(
WidgetTester
tester
)
async
{
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
// Generate the data for a regular key down event.
// Generate the data for a regular key down event.
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
getKeyData
(
final
Map
<
String
,
dynamic
>
data
=
KeyEventSimulator
.
get
Raw
KeyData
(
LogicalKeyboardKey
.
keyA
,
LogicalKeyboardKey
.
keyA
,
platform:
'android'
,
platform:
'android'
,
isDown:
true
,
isDown:
true
,
...
@@ -955,6 +1019,7 @@ void main() {
...
@@ -955,6 +1019,7 @@ void main() {
},
},
);
);
expect
(
message
,
equals
(<
String
,
dynamic
>{
'handled'
:
false
}));
expect
(
message
,
equals
(<
String
,
dynamic
>{
'handled'
:
false
}));
message
=
null
;
// Set up a widget that will receive focused text events.
// Set up a widget that will receive focused text events.
final
FocusNode
focusNode
=
FocusNode
(
debugLabel:
'Test Node'
);
final
FocusNode
focusNode
=
FocusNode
(
debugLabel:
'Test Node'
);
...
...
packages/flutter/test/widgets/app_test.dart
View file @
5f792ba1
...
@@ -143,7 +143,7 @@ void main() {
...
@@ -143,7 +143,7 @@ void main() {
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
gameButtonA
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
gameButtonA
);
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
checked
,
isTrue
);
expect
(
checked
,
isTrue
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
group
(
'error control test'
,
()
{
group
(
'error control test'
,
()
{
Future
<
void
>
expectFlutterError
({
Future
<
void
>
expectFlutterError
({
...
...
packages/flutter/test/widgets/default_text_editing_actions_test.dart
View file @
5f792ba1
...
@@ -114,5 +114,5 @@ void main() {
...
@@ -114,5 +114,5 @@ void main() {
await
tester
.
pump
();
await
tester
.
pump
();
expect
(
leftCalled
,
isFalse
);
expect
(
leftCalled
,
isFalse
);
expect
(
rightCalled
,
isTrue
);
expect
(
rightCalled
,
isTrue
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
}
}
packages/flutter/test/widgets/editable_text_cursor_test.dart
View file @
5f792ba1
...
@@ -328,6 +328,8 @@ void main() {
...
@@ -328,6 +328,8 @@ void main() {
});
});
testWidgets
(
'Cursor animation restarts when it is moved using keys on desktop'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Cursor animation restarts when it is moved using keys on desktop'
,
(
WidgetTester
tester
)
async
{
debugDefaultTargetPlatformOverride
=
TargetPlatform
.
macOS
;
const
String
testText
=
'Some text long enough to move the cursor around'
;
const
String
testText
=
'Some text long enough to move the cursor around'
;
final
TextEditingController
controller
=
TextEditingController
(
text:
testText
);
final
TextEditingController
controller
=
TextEditingController
(
text:
testText
);
final
Widget
widget
=
MaterialApp
(
final
Widget
widget
=
MaterialApp
(
...
@@ -400,7 +402,9 @@ void main() {
...
@@ -400,7 +402,9 @@ void main() {
await
tester
.
pump
(
const
Duration
(
milliseconds:
1
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
1
));
expect
(
renderEditable
.
cursorColor
!.
alpha
,
0
);
expect
(
renderEditable
.
cursorColor
!.
alpha
,
0
);
expect
(
renderEditable
,
paintsExactlyCountTimes
(
#drawRect
,
0
));
expect
(
renderEditable
,
paintsExactlyCountTimes
(
#drawRect
,
0
));
},
variant:
const
TargetPlatformVariant
(<
TargetPlatform
>{
TargetPlatform
.
macOS
}));
debugDefaultTargetPlatformOverride
=
null
;
},
variant:
KeySimulatorTransitModeVariant
.
all
());
testWidgets
(
'Cursor does not show when showCursor set to false'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Cursor does not show when showCursor set to false'
,
(
WidgetTester
tester
)
async
{
const
Widget
widget
=
MaterialApp
(
const
Widget
widget
=
MaterialApp
(
...
...
packages/flutter/test/widgets/editable_text_test.dart
View file @
5f792ba1
...
@@ -4864,8 +4864,23 @@ void main() {
...
@@ -4864,8 +4864,23 @@ void main() {
expect
(
controller
.
text
,
isEmpty
,
reason:
'on
$platform
'
);
expect
(
controller
.
text
,
isEmpty
,
reason:
'on
$platform
'
);
}
}
testWidgets
(
'keyboard text selection works'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'keyboard text selection works (RawKeyEvent)'
,
(
WidgetTester
tester
)
async
{
debugKeyEventSimulatorTransitModeOverride
=
KeyDataTransitMode
.
rawKeyData
;
await
testTextEditing
(
tester
,
targetPlatform:
defaultTargetPlatform
);
await
testTextEditing
(
tester
,
targetPlatform:
defaultTargetPlatform
);
debugKeyEventSimulatorTransitModeOverride
=
null
;
// On web, using keyboard for selection is handled by the browser.
},
skip:
kIsWeb
,
variant:
TargetPlatformVariant
.
all
());
testWidgets
(
'keyboard text selection works (ui.KeyData then RawKeyEvent)'
,
(
WidgetTester
tester
)
async
{
debugKeyEventSimulatorTransitModeOverride
=
KeyDataTransitMode
.
keyDataThenRawKeyData
;
await
testTextEditing
(
tester
,
targetPlatform:
defaultTargetPlatform
);
debugKeyEventSimulatorTransitModeOverride
=
null
;
// On web, using keyboard for selection is handled by the browser.
// On web, using keyboard for selection is handled by the browser.
},
skip:
kIsWeb
,
variant:
TargetPlatformVariant
.
all
());
},
skip:
kIsWeb
,
variant:
TargetPlatformVariant
.
all
());
...
@@ -7675,7 +7690,7 @@ void main() {
...
@@ -7675,7 +7690,7 @@ void main() {
expect
(
controller
.
selection
.
isCollapsed
,
true
);
expect
(
controller
.
selection
.
isCollapsed
,
true
);
expect
(
controller
.
selection
.
baseOffset
,
1
);
expect
(
controller
.
selection
.
baseOffset
,
1
);
}
}
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'the toolbar is disposed when selection changes and there is no selectionControls'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'the toolbar is disposed when selection changes and there is no selectionControls'
,
(
WidgetTester
tester
)
async
{
late
StateSetter
setState
;
late
StateSetter
setState
;
...
...
packages/flutter/test/widgets/focus_manager_test.dart
View file @
5f792ba1
...
@@ -165,7 +165,101 @@ void main() {
...
@@ -165,7 +165,101 @@ void main() {
'hasPrimaryFocus: false'
,
'hasPrimaryFocus: false'
,
]);
]);
});
});
testWidgets
(
'onKeyEvent and onKey correctly cooperate'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
(
debugLabel:
'Test Node 3'
);
List
<
List
<
KeyEventResult
>>
results
=
<
List
<
KeyEventResult
>>[
<
KeyEventResult
>[
KeyEventResult
.
ignored
,
KeyEventResult
.
ignored
],
<
KeyEventResult
>[
KeyEventResult
.
ignored
,
KeyEventResult
.
ignored
],
<
KeyEventResult
>[
KeyEventResult
.
ignored
,
KeyEventResult
.
ignored
],
];
final
List
<
int
>
logs
=
<
int
>[];
await
tester
.
pumpWidget
(
Focus
(
focusNode:
FocusNode
(
debugLabel:
'Test Node 1'
),
onKeyEvent:
(
_
,
KeyEvent
event
)
{
logs
.
add
(
0
);
return
results
[
0
][
0
];
},
onKey:
(
_
,
RawKeyEvent
event
)
{
logs
.
add
(
1
);
return
results
[
0
][
1
];
},
child:
Focus
(
focusNode:
FocusNode
(
debugLabel:
'Test Node 2'
),
onKeyEvent:
(
_
,
KeyEvent
event
)
{
logs
.
add
(
10
);
return
results
[
1
][
0
];
},
onKey:
(
_
,
RawKeyEvent
event
)
{
logs
.
add
(
11
);
return
results
[
1
][
1
];
},
child:
Focus
(
focusNode:
focusNode
,
onKeyEvent:
(
_
,
KeyEvent
event
)
{
logs
.
add
(
20
);
return
results
[
2
][
0
];
},
onKey:
(
_
,
RawKeyEvent
event
)
{
logs
.
add
(
21
);
return
results
[
2
][
1
];
},
child:
const
SizedBox
(
width:
200
,
height:
100
),
),
),
),
);
focusNode
.
requestFocus
();
await
tester
.
pump
();
// All ignored.
results
=
<
List
<
KeyEventResult
>>[
<
KeyEventResult
>[
KeyEventResult
.
ignored
,
KeyEventResult
.
ignored
],
<
KeyEventResult
>[
KeyEventResult
.
ignored
,
KeyEventResult
.
ignored
],
<
KeyEventResult
>[
KeyEventResult
.
ignored
,
KeyEventResult
.
ignored
],
];
expect
(
await
simulateKeyDownEvent
(
LogicalKeyboardKey
.
digit1
),
false
);
expect
(
logs
,
<
int
>[
20
,
21
,
10
,
11
,
0
,
1
]);
logs
.
clear
();
// The onKeyEvent should be able to stop propagation.
results
=
<
List
<
KeyEventResult
>>[
<
KeyEventResult
>[
KeyEventResult
.
ignored
,
KeyEventResult
.
ignored
],
<
KeyEventResult
>[
KeyEventResult
.
handled
,
KeyEventResult
.
ignored
],
<
KeyEventResult
>[
KeyEventResult
.
ignored
,
KeyEventResult
.
ignored
],
];
expect
(
await
simulateKeyUpEvent
(
LogicalKeyboardKey
.
digit1
),
true
);
expect
(
logs
,
<
int
>[
20
,
21
,
10
,
11
]);
logs
.
clear
();
// The onKey should be able to stop propagation.
results
=
<
List
<
KeyEventResult
>>[
<
KeyEventResult
>[
KeyEventResult
.
ignored
,
KeyEventResult
.
ignored
],
<
KeyEventResult
>[
KeyEventResult
.
ignored
,
KeyEventResult
.
handled
],
<
KeyEventResult
>[
KeyEventResult
.
ignored
,
KeyEventResult
.
ignored
],
];
expect
(
await
simulateKeyDownEvent
(
LogicalKeyboardKey
.
digit1
),
true
);
expect
(
logs
,
<
int
>[
20
,
21
,
10
,
11
]);
logs
.
clear
();
// KeyEventResult.skipRemainingHandlers works.
results
=
<
List
<
KeyEventResult
>>[
<
KeyEventResult
>[
KeyEventResult
.
ignored
,
KeyEventResult
.
ignored
],
<
KeyEventResult
>[
KeyEventResult
.
skipRemainingHandlers
,
KeyEventResult
.
ignored
],
<
KeyEventResult
>[
KeyEventResult
.
ignored
,
KeyEventResult
.
ignored
],
];
expect
(
await
simulateKeyUpEvent
(
LogicalKeyboardKey
.
digit1
),
false
);
expect
(
logs
,
<
int
>[
20
,
21
,
10
,
11
]);
logs
.
clear
();
},
variant:
KeySimulatorTransitModeVariant
.
all
());
});
});
group
(
FocusScopeNode
,
()
{
group
(
FocusScopeNode
,
()
{
testWidgets
(
'Can setFirstFocus on a scope with no manager.'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Can setFirstFocus on a scope with no manager.'
,
(
WidgetTester
tester
)
async
{
...
@@ -935,7 +1029,7 @@ void main() {
...
@@ -935,7 +1029,7 @@ void main() {
// Since none of the focused nodes handle this event, nothing should
// Since none of the focused nodes handle this event, nothing should
// receive it.
// receive it.
expect
(
receivedAnEvent
,
isEmpty
);
expect
(
receivedAnEvent
,
isEmpty
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Initial highlight mode guesses correctly.'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Initial highlight mode guesses correctly.'
,
(
WidgetTester
tester
)
async
{
FocusManager
.
instance
.
highlightStrategy
=
FocusHighlightStrategy
.
automatic
;
FocusManager
.
instance
.
highlightStrategy
=
FocusHighlightStrategy
.
automatic
;
...
...
packages/flutter/test/widgets/focus_traversal_test.dart
View file @
5f792ba1
...
@@ -1678,7 +1678,7 @@ void main() {
...
@@ -1678,7 +1678,7 @@ void main() {
expect
(
Focus
.
of
(
lowerLeftKey
.
currentContext
!).
hasPrimaryFocus
,
isTrue
);
expect
(
Focus
.
of
(
lowerLeftKey
.
currentContext
!).
hasPrimaryFocus
,
isTrue
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowUp
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowUp
);
expect
(
Focus
.
of
(
upperLeftKey
.
currentContext
!).
hasPrimaryFocus
,
isTrue
);
expect
(
Focus
.
of
(
upperLeftKey
.
currentContext
!).
hasPrimaryFocus
,
isTrue
);
},
skip:
isBrowser
);
// https://github.com/flutter/flutter/issues/35347
},
skip:
isBrowser
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
// https://github.com/flutter/flutter/issues/35347
testWidgets
(
'Focus traversal inside a vertical scrollable scrolls to stay visible.'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Focus traversal inside a vertical scrollable scrolls to stay visible.'
,
(
WidgetTester
tester
)
async
{
final
List
<
int
>
items
=
List
<
int
>.
generate
(
11
,
(
int
index
)
=>
index
).
toList
();
final
List
<
int
>
items
=
List
<
int
>.
generate
(
11
,
(
int
index
)
=>
index
).
toList
();
...
@@ -1776,7 +1776,7 @@ void main() {
...
@@ -1776,7 +1776,7 @@ void main() {
await
tester
.
pump
();
await
tester
.
pump
();
expect
(
topNode
.
hasPrimaryFocus
,
isTrue
);
expect
(
topNode
.
hasPrimaryFocus
,
isTrue
);
expect
(
controller
.
offset
,
equals
(
0.0
));
expect
(
controller
.
offset
,
equals
(
0.0
));
},
skip:
isBrowser
);
// https://github.com/flutter/flutter/issues/35347
},
skip:
isBrowser
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
// https://github.com/flutter/flutter/issues/35347
testWidgets
(
'Focus traversal inside a horizontal scrollable scrolls to stay visible.'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Focus traversal inside a horizontal scrollable scrolls to stay visible.'
,
(
WidgetTester
tester
)
async
{
final
List
<
int
>
items
=
List
<
int
>.
generate
(
11
,
(
int
index
)
=>
index
).
toList
();
final
List
<
int
>
items
=
List
<
int
>.
generate
(
11
,
(
int
index
)
=>
index
).
toList
();
...
@@ -1874,7 +1874,7 @@ void main() {
...
@@ -1874,7 +1874,7 @@ void main() {
await
tester
.
pump
();
await
tester
.
pump
();
expect
(
leftNode
.
hasPrimaryFocus
,
isTrue
);
expect
(
leftNode
.
hasPrimaryFocus
,
isTrue
);
expect
(
controller
.
offset
,
equals
(
0.0
));
expect
(
controller
.
offset
,
equals
(
0.0
));
},
skip:
isBrowser
);
// https://github.com/flutter/flutter/issues/35347
},
skip:
isBrowser
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
// https://github.com/flutter/flutter/issues/35347
testWidgets
(
'Arrow focus traversal actions can be re-enabled for text fields.'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Arrow focus traversal actions can be re-enabled for text fields.'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
upperLeftKey
=
GlobalKey
(
debugLabel:
'upperLeftKey'
);
final
GlobalKey
upperLeftKey
=
GlobalKey
(
debugLabel:
'upperLeftKey'
);
...
@@ -1997,10 +1997,10 @@ void main() {
...
@@ -1997,10 +1997,10 @@ void main() {
expect
(
focusNodeUpperLeft
.
hasPrimaryFocus
,
isTrue
);
expect
(
focusNodeUpperLeft
.
hasPrimaryFocus
,
isTrue
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowUp
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowUp
);
expect
(
focusNodeUpperLeft
.
hasPrimaryFocus
,
isTrue
);
expect
(
focusNodeUpperLeft
.
hasPrimaryFocus
,
isTrue
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Focus traversal does not break when no focusable is available on a MaterialApp'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Focus traversal does not break when no focusable is available on a MaterialApp'
,
(
WidgetTester
tester
)
async
{
final
List
<
RawKeyEvent
>
events
=
<
RawKeyEven
t
>[];
final
List
<
Object
>
events
=
<
Objec
t
>[];
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Container
()));
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Container
()));
...
@@ -2013,7 +2013,7 @@ void main() {
...
@@ -2013,7 +2013,7 @@ void main() {
await
tester
.
idle
();
await
tester
.
idle
();
expect
(
events
.
length
,
2
);
expect
(
events
.
length
,
2
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Focus traversal does not throw when no focusable is available in a group'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Focus traversal does not throw when no focusable is available in a group'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
MaterialApp
(
home:
Scaffold
(
body:
ListTile
(
title:
Text
(
'title'
)))));
await
tester
.
pumpWidget
(
const
MaterialApp
(
home:
Scaffold
(
body:
ListTile
(
title:
Text
(
'title'
)))));
...
@@ -2047,7 +2047,7 @@ void main() {
...
@@ -2047,7 +2047,7 @@ void main() {
await
tester
.
idle
();
await
tester
.
idle
();
expect
(
events
.
length
,
2
);
expect
(
events
.
length
,
2
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
});
});
group
(
FocusTraversalGroup
,
()
{
group
(
FocusTraversalGroup
,
()
{
testWidgets
(
"Focus traversal group doesn't introduce a Semantics node"
,
(
WidgetTester
tester
)
async
{
testWidgets
(
"Focus traversal group doesn't introduce a Semantics node"
,
(
WidgetTester
tester
)
async
{
...
...
packages/flutter/test/widgets/keyboard_listener_test.dart
0 → 100644
View file @
5f792ba1
// 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/services.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
testWidgets
(
'Can dispose without keyboard'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
();
await
tester
.
pumpWidget
(
KeyboardListener
(
focusNode:
focusNode
,
onKeyEvent:
null
,
child:
Container
()));
await
tester
.
pumpWidget
(
KeyboardListener
(
focusNode:
focusNode
,
onKeyEvent:
null
,
child:
Container
()));
await
tester
.
pumpWidget
(
Container
());
});
testWidgets
(
'Fuchsia key event'
,
(
WidgetTester
tester
)
async
{
final
List
<
KeyEvent
>
events
=
<
KeyEvent
>[];
final
FocusNode
focusNode
=
FocusNode
();
await
tester
.
pumpWidget
(
KeyboardListener
(
focusNode:
focusNode
,
onKeyEvent:
events
.
add
,
child:
Container
(),
),
);
focusNode
.
requestFocus
();
await
tester
.
idle
();
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
metaLeft
,
platform:
'fuchsia'
);
await
tester
.
idle
();
expect
(
events
.
length
,
2
);
expect
(
events
[
0
],
isA
<
KeyDownEvent
>());
expect
(
events
[
0
].
physicalKey
,
PhysicalKeyboardKey
.
metaLeft
);
expect
(
events
[
0
].
logicalKey
,
LogicalKeyboardKey
.
metaLeft
);
await
tester
.
pumpWidget
(
Container
());
focusNode
.
dispose
();
},
skip:
isBrowser
);
// This is a Fuchsia-specific test.
testWidgets
(
'Web key event'
,
(
WidgetTester
tester
)
async
{
final
List
<
KeyEvent
>
events
=
<
KeyEvent
>[];
final
FocusNode
focusNode
=
FocusNode
();
await
tester
.
pumpWidget
(
KeyboardListener
(
focusNode:
focusNode
,
onKeyEvent:
events
.
add
,
child:
Container
(),
),
);
focusNode
.
requestFocus
();
await
tester
.
idle
();
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
metaLeft
,
platform:
'web'
);
await
tester
.
idle
();
expect
(
events
.
length
,
2
);
expect
(
events
[
0
],
isA
<
KeyDownEvent
>());
expect
(
events
[
0
].
physicalKey
,
PhysicalKeyboardKey
.
metaLeft
);
expect
(
events
[
0
].
logicalKey
,
LogicalKeyboardKey
.
metaLeft
);
await
tester
.
pumpWidget
(
Container
());
focusNode
.
dispose
();
});
testWidgets
(
'Defunct listeners do not receive events'
,
(
WidgetTester
tester
)
async
{
final
List
<
KeyEvent
>
events
=
<
KeyEvent
>[];
final
FocusNode
focusNode
=
FocusNode
();
await
tester
.
pumpWidget
(
KeyboardListener
(
focusNode:
focusNode
,
onKeyEvent:
events
.
add
,
child:
Container
(),
),
);
focusNode
.
requestFocus
();
await
tester
.
idle
();
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
metaLeft
,
platform:
'fuchsia'
);
await
tester
.
idle
();
expect
(
events
.
length
,
2
);
events
.
clear
();
await
tester
.
pumpWidget
(
Container
());
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
metaLeft
,
platform:
'fuchsia'
);
await
tester
.
idle
();
expect
(
events
.
length
,
0
);
await
tester
.
pumpWidget
(
Container
());
focusNode
.
dispose
();
});
}
packages/flutter/test/widgets/scrollable_test.dart
View file @
5f792ba1
...
@@ -520,7 +520,7 @@ void main() {
...
@@ -520,7 +520,7 @@ void main() {
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
controller
.
position
.
pixels
,
equals
(
0.0
));
expect
(
controller
.
position
.
pixels
,
equals
(
0.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
0.0
,
800.0
,
50.0
)));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
0.0
,
800.0
,
50.0
)));
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Vertical scrollables are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Vertical scrollables are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
final
ScrollController
controller
=
ScrollController
();
...
@@ -571,7 +571,7 @@ void main() {
...
@@ -571,7 +571,7 @@ void main() {
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
pageUp
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
pageUp
);
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
0.0
,
800.0
,
50.0
)));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
0.0
,
800.0
,
50.0
)));
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Horizontal scrollables are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Horizontal scrollables are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
final
ScrollController
controller
=
ScrollController
();
...
@@ -617,7 +617,7 @@ void main() {
...
@@ -617,7 +617,7 @@ void main() {
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
0.0
,
50.0
,
600.0
)));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
0.0
,
50.0
,
600.0
)));
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Horizontal scrollables are scrolled the correct direction in RTL locales.'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Horizontal scrollables are scrolled the correct direction in RTL locales.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
final
ScrollController
controller
=
ScrollController
();
...
@@ -666,7 +666,7 @@ void main() {
...
@@ -666,7 +666,7 @@ void main() {
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
750.0
,
0.0
,
800.0
,
600.0
)));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
750.0
,
0.0
,
800.0
,
600.0
)));
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Reversed vertical scrollables are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Reversed vertical scrollables are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
final
ScrollController
controller
=
ScrollController
();
...
@@ -720,7 +720,7 @@ void main() {
...
@@ -720,7 +720,7 @@ void main() {
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
pageDown
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
pageDown
);
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
550.0
,
800.0
,
600.0
)));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
550.0
,
800.0
,
600.0
)));
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Reversed horizontal scrollables are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Reversed horizontal scrollables are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
final
ScrollController
controller
=
ScrollController
();
...
@@ -768,7 +768,7 @@ void main() {
...
@@ -768,7 +768,7 @@ void main() {
if
(!
kIsWeb
)
if
(!
kIsWeb
)
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Custom scrollables with a center sliver are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Custom scrollables with a center sliver are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
final
ScrollController
controller
=
ScrollController
();
...
@@ -828,7 +828,7 @@ void main() {
...
@@ -828,7 +828,7 @@ void main() {
// Goes up two past "center" where it started, so negative.
// Goes up two past "center" where it started, so negative.
expect
(
controller
.
position
.
pixels
,
equals
(-
100.0
));
expect
(
controller
.
position
.
pixels
,
equals
(-
100.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Item 10'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
100.0
,
800.0
,
200.0
)));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Item 10'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
100.0
,
800.0
,
200.0
)));
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Can recommendDeferredLoadingForContext - animation'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Can recommendDeferredLoadingForContext - animation'
,
(
WidgetTester
tester
)
async
{
final
List
<
String
>
widgetTracker
=
<
String
>[];
final
List
<
String
>
widgetTracker
=
<
String
>[];
...
...
packages/flutter/test/widgets/selectable_text_test.dart
View file @
5f792ba1
...
@@ -1570,7 +1570,7 @@ void main() {
...
@@ -1570,7 +1570,7 @@ void main() {
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
shift
);
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
shift
);
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
arrowLeft
);
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
arrowLeft
);
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
-
1
);
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
-
1
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Shift test 2'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Shift test 2'
,
(
WidgetTester
tester
)
async
{
await
setupWidget
(
tester
,
'abcdefghi'
);
await
setupWidget
(
tester
,
'abcdefghi'
);
...
@@ -1582,7 +1582,7 @@ void main() {
...
@@ -1582,7 +1582,7 @@ void main() {
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
1
);
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
1
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Control Shift test'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Control Shift test'
,
(
WidgetTester
tester
)
async
{
await
setupWidget
(
tester
,
'their big house'
);
await
setupWidget
(
tester
,
'their big house'
);
...
@@ -1594,7 +1594,7 @@ void main() {
...
@@ -1594,7 +1594,7 @@ void main() {
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
-
5
);
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
-
5
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Down and up test'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Down and up test'
,
(
WidgetTester
tester
)
async
{
await
setupWidget
(
tester
,
'a big house'
);
await
setupWidget
(
tester
,
'a big house'
);
...
@@ -1612,7 +1612,7 @@ void main() {
...
@@ -1612,7 +1612,7 @@ void main() {
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
0
);
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
0
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Down and up test 2'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Down and up test 2'
,
(
WidgetTester
tester
)
async
{
await
setupWidget
(
tester
,
'a big house
\n
jumped over a mouse
\n
One more line yay'
);
await
setupWidget
(
tester
,
'a big house
\n
jumped over a mouse
\n
One more line yay'
);
...
@@ -1663,7 +1663,7 @@ void main() {
...
@@ -1663,7 +1663,7 @@ void main() {
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
-
5
);
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
-
5
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
});
});
testWidgets
(
'Copy test'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Copy test'
,
(
WidgetTester
tester
)
async
{
...
@@ -1722,7 +1722,7 @@ void main() {
...
@@ -1722,7 +1722,7 @@ void main() {
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Select all test'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Select all test'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
();
final
FocusNode
focusNode
=
FocusNode
();
...
@@ -1757,7 +1757,7 @@ void main() {
...
@@ -1757,7 +1757,7 @@ void main() {
expect
(
controller
.
selection
.
baseOffset
,
0
);
expect
(
controller
.
selection
.
baseOffset
,
0
);
expect
(
controller
.
selection
.
extentOffset
,
31
);
expect
(
controller
.
selection
.
extentOffset
,
31
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'keyboard selection should call onSelectionChanged'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'keyboard selection should call onSelectionChanged'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
();
final
FocusNode
focusNode
=
FocusNode
();
...
@@ -1804,7 +1804,7 @@ void main() {
...
@@ -1804,7 +1804,7 @@ void main() {
expect
(
newSelection
!.
extentOffset
,
i
+
1
);
expect
(
newSelection
!.
extentOffset
,
i
+
1
);
newSelection
=
null
;
newSelection
=
null
;
}
}
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Changing positions of selectable text'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Changing positions of selectable text'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
();
final
FocusNode
focusNode
=
FocusNode
();
...
@@ -1894,7 +1894,7 @@ void main() {
...
@@ -1894,7 +1894,7 @@ void main() {
c1
=
editableTextWidget
.
controller
;
c1
=
editableTextWidget
.
controller
;
expect
(
c1
.
selection
.
extentOffset
-
c1
.
selection
.
baseOffset
,
-
6
);
expect
(
c1
.
selection
.
extentOffset
-
c1
.
selection
.
baseOffset
,
-
6
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Changing focus test'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Changing focus test'
,
(
WidgetTester
tester
)
async
{
...
@@ -1964,7 +1964,7 @@ void main() {
...
@@ -1964,7 +1964,7 @@ void main() {
expect
(
c1
.
selection
.
extentOffset
-
c1
.
selection
.
baseOffset
,
0
);
expect
(
c1
.
selection
.
extentOffset
-
c1
.
selection
.
baseOffset
,
0
);
expect
(
c2
.
selection
.
extentOffset
-
c2
.
selection
.
baseOffset
,
-
5
);
expect
(
c2
.
selection
.
extentOffset
-
c2
.
selection
.
baseOffset
,
-
5
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Caret works when maxLines is null'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Caret works when maxLines is null'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
...
...
packages/flutter/test/widgets/shortcuts_test.dart
View file @
5f792ba1
...
@@ -435,7 +435,33 @@ void main() {
...
@@ -435,7 +435,33 @@ void main() {
invoked
=
0
;
invoked
=
0
;
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
});
},
variant:
KeySimulatorTransitModeVariant
.
all
());
testWidgets
(
'handles repeated events'
,
(
WidgetTester
tester
)
async
{
int
invoked
=
0
;
await
tester
.
pumpWidget
(
activatorTester
(
const
SingleActivator
(
LogicalKeyboardKey
.
keyC
,
control:
true
,
),
(
Intent
intent
)
{
invoked
+=
1
;
},
));
await
tester
.
pump
();
// LCtrl -> KeyC: Accept
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
controlLeft
);
expect
(
invoked
,
0
);
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
keyC
);
expect
(
invoked
,
1
);
await
tester
.
sendKeyRepeatEvent
(
LogicalKeyboardKey
.
keyC
);
expect
(
invoked
,
2
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
keyC
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
controlLeft
);
expect
(
invoked
,
2
);
invoked
=
0
;
expect
(
RawKeyboard
.
instance
.
keysPressed
,
isEmpty
);
},
variant:
KeySimulatorTransitModeVariant
.
all
());
testWidgets
(
'handles Shift-Ctrl-C'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'handles Shift-Ctrl-C'
,
(
WidgetTester
tester
)
async
{
int
invoked
=
0
;
int
invoked
=
0
;
...
@@ -1075,7 +1101,27 @@ void main() {
...
@@ -1075,7 +1101,27 @@ void main() {
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
shiftLeft
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
shiftLeft
);
expect
(
invoked
,
1
);
expect
(
invoked
,
1
);
invoked
=
0
;
invoked
=
0
;
});
},
variant:
KeySimulatorTransitModeVariant
.
all
());
testWidgets
(
'handles repeated events'
,
(
WidgetTester
tester
)
async
{
int
invoked
=
0
;
await
tester
.
pumpWidget
(
activatorTester
(
const
CharacterActivator
(
'?'
),
(
Intent
intent
)
{
invoked
+=
1
;
},
));
await
tester
.
pump
();
// Press KeyC: Accepted by DumbLogicalActivator
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
shiftLeft
);
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
slash
,
character:
'?'
);
expect
(
invoked
,
1
);
await
tester
.
sendKeyRepeatEvent
(
LogicalKeyboardKey
.
slash
,
character:
'?'
);
expect
(
invoked
,
2
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
slash
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
shiftLeft
);
expect
(
invoked
,
2
);
invoked
=
0
;
},
variant:
KeySimulatorTransitModeVariant
.
all
());
});
});
group
(
'CallbackShortcuts'
,
()
{
group
(
'CallbackShortcuts'
,
()
{
...
...
packages/flutter_test/lib/src/binding.dart
View file @
5f792ba1
...
@@ -828,6 +828,9 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
...
@@ -828,6 +828,9 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
assert
(
debugAssertAllSchedulerVarsUnset
(
assert
(
debugAssertAllSchedulerVarsUnset
(
'The value of a scheduler debug variable was changed by the test.'
,
'The value of a scheduler debug variable was changed by the test.'
,
));
));
assert
(
debugAssertAllServicesVarsUnset
(
'The value of a services debug variable was changed by the test.'
,
));
}
}
void
_verifyAutoUpdateGoldensUnset
(
bool
valueBeforeTest
)
{
void
_verifyAutoUpdateGoldensUnset
(
bool
valueBeforeTest
)
{
...
@@ -894,6 +897,10 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
...
@@ -894,6 +897,10 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
// tests.
// tests.
// ignore: invalid_use_of_visible_for_testing_member
// ignore: invalid_use_of_visible_for_testing_member
RawKeyboard
.
instance
.
clearKeysPressed
();
RawKeyboard
.
instance
.
clearKeysPressed
();
// ignore: invalid_use_of_visible_for_testing_member
HardwareKeyboard
.
instance
.
clearState
();
// ignore: invalid_use_of_visible_for_testing_member
keyEventManager
.
clearState
();
assert
(!
RendererBinding
.
instance
!.
mouseTracker
.
mouseIsConnected
,
assert
(!
RendererBinding
.
instance
!.
mouseTracker
.
mouseIsConnected
,
'The MouseTracker thinks that there is still a mouse connected, which indicates that a '
'The MouseTracker thinks that there is still a mouse connected, which indicates that a '
'test has not removed the mouse pointer which it added. Call removePointer on the '
'test has not removed the mouse pointer which it added. Call removePointer on the '
...
...
packages/flutter_test/lib/src/controller.dart
View file @
5f792ba1
...
@@ -973,7 +973,7 @@ abstract class WidgetController {
...
@@ -973,7 +973,7 @@ abstract class WidgetController {
return
box
.
size
;
return
box
.
size
;
}
}
/// Simulates sending physical key down and up events
through the system channel
.
/// Simulates sending physical key down and up events.
///
///
/// This only simulates key events coming from a physical keyboard, not from a
/// This only simulates key events coming from a physical keyboard, not from a
/// soft keyboard.
/// soft keyboard.
...
@@ -984,6 +984,9 @@ abstract class WidgetController {
...
@@ -984,6 +984,9 @@ abstract class WidgetController {
/// else. Must not be null. Some platforms (e.g. Windows, iOS) are not yet
/// else. Must not be null. Some platforms (e.g. Windows, iOS) are not yet
/// supported.
/// supported.
///
///
/// Whether the event is sent through [RawKeyEvent] or [KeyEvent] is
/// controlled by [debugKeyEventSimulatorTransitModeOverride].
///
/// Keys that are down when the test completes are cleared after each test.
/// Keys that are down when the test completes are cleared after each test.
///
///
/// This method sends both the key down and the key up events, to simulate a
/// This method sends both the key down and the key up events, to simulate a
...
@@ -1004,7 +1007,7 @@ abstract class WidgetController {
...
@@ -1004,7 +1007,7 @@ abstract class WidgetController {
return
handled
;
return
handled
;
}
}
/// Simulates sending a physical key down event
through the system channel
.
/// Simulates sending a physical key down event.
///
///
/// This only simulates key down events coming from a physical keyboard, not
/// This only simulates key down events coming from a physical keyboard, not
/// from a soft keyboard.
/// from a soft keyboard.
...
@@ -1015,13 +1018,17 @@ abstract class WidgetController {
...
@@ -1015,13 +1018,17 @@ abstract class WidgetController {
/// else. Must not be null. Some platforms (e.g. Windows, iOS) are not yet
/// else. Must not be null. Some platforms (e.g. Windows, iOS) are not yet
/// supported.
/// supported.
///
///
/// Whether the event is sent through [RawKeyEvent] or [KeyEvent] is
/// controlled by [debugKeyEventSimulatorTransitModeOverride].
///
/// Keys that are down when the test completes are cleared after each test.
/// Keys that are down when the test completes are cleared after each test.
///
///
/// Returns true if the key event was handled by the framework.
/// Returns true if the key event was handled by the framework.
///
///
/// See also:
/// See also:
///
///
/// - [sendKeyUpEvent] to simulate the corresponding key up event.
/// - [sendKeyUpEvent] and [sendKeyRepeatEvent] to simulate the corresponding
/// key up and repeat event.
/// - [sendKeyEvent] to simulate both the key up and key down in the same call.
/// - [sendKeyEvent] to simulate both the key up and key down in the same call.
Future
<
bool
>
sendKeyDownEvent
(
LogicalKeyboardKey
key
,
{
String
?
character
,
String
platform
=
_defaultPlatform
})
async
{
Future
<
bool
>
sendKeyDownEvent
(
LogicalKeyboardKey
key
,
{
String
?
character
,
String
platform
=
_defaultPlatform
})
async
{
assert
(
platform
!=
null
);
assert
(
platform
!=
null
);
...
@@ -1039,11 +1046,15 @@ abstract class WidgetController {
...
@@ -1039,11 +1046,15 @@ abstract class WidgetController {
/// that type of system. Defaults to "web" on web, and "android" everywhere
/// that type of system. Defaults to "web" on web, and "android" everywhere
/// else. May not be null.
/// else. May not be null.
///
///
/// Whether the event is sent through [RawKeyEvent] or [KeyEvent] is
/// controlled by [debugKeyEventSimulatorTransitModeOverride].
///
/// Returns true if the key event was handled by the framework.
/// Returns true if the key event was handled by the framework.
///
///
/// See also:
/// See also:
///
///
/// - [sendKeyDownEvent] to simulate the corresponding key down event.
/// - [sendKeyDownEvent] and [sendKeyRepeatEvent] to simulate the
/// corresponding key down and repeat event.
/// - [sendKeyEvent] to simulate both the key up and key down in the same call.
/// - [sendKeyEvent] to simulate both the key up and key down in the same call.
Future
<
bool
>
sendKeyUpEvent
(
LogicalKeyboardKey
key
,
{
String
platform
=
_defaultPlatform
})
async
{
Future
<
bool
>
sendKeyUpEvent
(
LogicalKeyboardKey
key
,
{
String
platform
=
_defaultPlatform
})
async
{
assert
(
platform
!=
null
);
assert
(
platform
!=
null
);
...
@@ -1051,6 +1062,35 @@ abstract class WidgetController {
...
@@ -1051,6 +1062,35 @@ abstract class WidgetController {
return
simulateKeyUpEvent
(
key
,
platform:
platform
);
return
simulateKeyUpEvent
(
key
,
platform:
platform
);
}
}
/// Simulates sending a physical key repeat event.
///
/// This only simulates key repeat events coming from a physical keyboard, not
/// from a soft keyboard.
///
/// Specify `platform` as one of the platforms allowed in
/// [platform.Platform.operatingSystem] to make the event appear to be from that type
/// of system. Defaults to "web" on web, and "android" everywhere else. Must not be
/// null. Some platforms (e.g. Windows, iOS) are not yet supported.
///
/// Whether the event is sent through [RawKeyEvent] or [KeyEvent] is
/// controlled by [debugKeyEventSimulatorTransitModeOverride]. If through [RawKeyEvent],
/// this method is equivalent to [sendKeyDownEvent].
///
/// Keys that are down when the test completes are cleared after each test.
///
/// Returns true if the key event was handled by the framework.
///
/// See also:
///
/// - [sendKeyDownEvent] and [sendKeyUpEvent] to simulate the corresponding
/// key down and up event.
/// - [sendKeyEvent] to simulate both the key up and key down in the same call.
Future
<
bool
>
sendKeyRepeatEvent
(
LogicalKeyboardKey
key
,
{
String
?
character
,
String
platform
=
_defaultPlatform
})
async
{
assert
(
platform
!=
null
);
// Internally wrapped in async guard.
return
simulateKeyRepeatEvent
(
key
,
character:
character
,
platform:
platform
);
}
/// Returns the rect of the given widget. This is only valid once
/// Returns the rect of the given widget. This is only valid once
/// the widget's render object has been laid out at least once.
/// the widget's render object has been laid out at least once.
Rect
getRect
(
Finder
finder
)
=>
getTopLeft
(
finder
)
&
getSize
(
finder
);
Rect
getRect
(
Finder
finder
)
=>
getTopLeft
(
finder
)
&
getSize
(
finder
);
...
...
packages/flutter_test/lib/src/event_simulation.dart
View file @
5f792ba1
This diff is collapsed.
Click to expand it.
packages/flutter_test/lib/src/test_pointer.dart
View file @
5f792ba1
...
@@ -230,6 +230,7 @@ class TestPointer {
...
@@ -230,6 +230,7 @@ class TestPointer {
timeStamp:
timeStamp
,
timeStamp:
timeStamp
,
kind:
kind
,
kind:
kind
,
device:
_device
,
device:
_device
,
pointer:
pointer
,
position:
_location
??
Offset
.
zero
,
position:
_location
??
Offset
.
zero
,
);
);
}
}
...
@@ -255,6 +256,7 @@ class TestPointer {
...
@@ -255,6 +256,7 @@ class TestPointer {
timeStamp:
timeStamp
,
timeStamp:
timeStamp
,
kind:
kind
,
kind:
kind
,
device:
_device
,
device:
_device
,
pointer:
pointer
,
position:
newLocation
,
position:
newLocation
,
delta:
delta
,
delta:
delta
,
);
);
...
...
packages/flutter_test/lib/src/widget_tester.dart
View file @
5f792ba1
...
@@ -510,7 +510,7 @@ Future<void> expectLater(
...
@@ -510,7 +510,7 @@ Future<void> expectLater(
/// Class that programmatically interacts with widgets and the test environment.
/// Class that programmatically interacts with widgets and the test environment.
///
///
/// For convenience, instances of this class (such as the one provided by
/// For convenience, instances of this class (such as the one provided by
/// `testWidget`) can be used as the `vsync` for `AnimationController` objects.
/// `testWidget
s
`) can be used as the `vsync` for `AnimationController` objects.
class
WidgetTester
extends
WidgetController
implements
HitTestDispatcher
,
TickerProvider
{
class
WidgetTester
extends
WidgetController
implements
HitTestDispatcher
,
TickerProvider
{
WidgetTester
.
_
(
TestWidgetsFlutterBinding
binding
)
:
super
(
binding
)
{
WidgetTester
.
_
(
TestWidgetsFlutterBinding
binding
)
:
super
(
binding
)
{
if
(
binding
is
LiveTestWidgetsFlutterBinding
)
if
(
binding
is
LiveTestWidgetsFlutterBinding
)
...
...
packages/flutter_test/test/event_simulation_test.dart
View file @
5f792ba1
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment