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
caf876cf
Unverified
Commit
caf876cf
authored
Jul 29, 2021
by
Tong Mu
Committed by
GitHub
Jul 29, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Reland: Keyboard events (#87174)
parent
a82255b5
Changes
38
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
38 changed files
with
2501 additions
and
265 deletions
+2501
-265
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
+59
-59
raw_keyboard_web.dart
packages/flutter/lib/src/services/raw_keyboard_web.dart
+5
-7
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
+65
-0
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
+260
-56
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 @
caf876cf
...
...
@@ -327,6 +327,9 @@ class LogicalKeyboardKey extends KeyboardKey {
@@@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
// searched.
static const Map<int, LogicalKeyboardKey> _knownLogicalKeys = <int, LogicalKeyboardKey>{
...
...
@@ -489,6 +492,9 @@ class PhysicalKeyboardKey extends KeyboardKey {
@@@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
// can be searched.
static const Map<int, PhysicalKeyboardKey> _knownPhysicalKeys = <int, PhysicalKeyboardKey>{
...
...
dev/tools/gen_keycodes/data/windows_logical_to_window_vk.json
View file @
caf876cf
...
...
@@ -176,41 +176,5 @@
"Zoom"
:
[
"ZOOM"
],
"Noname"
:
[
"NONAME"
],
"Pa1"
:
[
"PA1"
],
"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"
]
"OemClear"
:
[
"OEM_CLEAR"
]
}
dev/tools/gen_keycodes/lib/keyboard_maps_code_gen.dart
View file @
caf876cf
...
...
@@ -24,6 +24,16 @@ bool _isAsciiLetter(String? char) {
||
(
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
/// data structure given to it.
class
KeyboardMapsCodeGenerator
extends
BaseCodeGenerator
{
...
...
@@ -171,7 +181,9 @@ class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
// because they are not used by the embedding. Add them manually.
final
List
<
int
>?
keyCodes
=
entry
.
windowsValues
.
isNotEmpty
?
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
)
{
for
(
final
int
code
in
keyCodes
)
{
lines
.
add
(
code
,
'
$code
: LogicalKeyboardKey.
${entry.constantName}
,'
);
...
...
packages/flutter/lib/services.dart
View file @
caf876cf
...
...
@@ -15,9 +15,11 @@ export 'src/services/autofill.dart';
export
'src/services/binary_messenger.dart'
;
export
'src/services/binding.dart'
;
export
'src/services/clipboard.dart'
;
export
'src/services/debug.dart'
;
export
'src/services/deferred_component.dart'
;
export
'src/services/font_loader.dart'
;
export
'src/services/haptic_feedback.dart'
;
export
'src/services/hardware_keyboard.dart'
;
export
'src/services/keyboard_key.dart'
;
export
'src/services/keyboard_maps.dart'
;
export
'src/services/message_codec.dart'
;
...
...
packages/flutter/lib/src/services/binding.dart
View file @
caf876cf
...
...
@@ -14,7 +14,9 @@ import 'package:flutter/scheduler.dart';
import
'asset_bundle.dart'
;
import
'binary_messenger.dart'
;
import
'hardware_keyboard.dart'
;
import
'message_codec.dart'
;
import
'raw_keyboard.dart'
;
import
'restoration.dart'
;
import
'system_channels.dart'
;
...
...
@@ -31,6 +33,7 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
_instance
=
this
;
_defaultBinaryMessenger
=
createBinaryMessenger
();
_restorationManager
=
createRestorationManager
();
_initKeyboard
();
initLicenses
();
SystemChannels
.
system
.
setMessageHandler
((
dynamic
message
)
=>
handleSystemMessage
(
message
as
Object
));
SystemChannels
.
lifecycle
.
setMessageHandler
(
_handleLifecycleMessage
);
...
...
@@ -42,6 +45,23 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
static
ServicesBinding
?
get
instance
=>
_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].
///
/// This is used to send messages from the application to the platform, and
...
...
packages/flutter/lib/src/services/debug.dart
0 → 100644
View file @
caf876cf
// 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 @
caf876cf
This diff is collapsed.
Click to expand it.
packages/flutter/lib/src/services/keyboard_key.dart
View file @
caf876cf
...
...
@@ -2624,6 +2624,9 @@ class LogicalKeyboardKey extends KeyboardKey {
/// See the function [RawKeyEvent.logicalKey] for more information.
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
// searched.
static
const
Map
<
int
,
LogicalKeyboardKey
>
_knownLogicalKeys
=
<
int
,
LogicalKeyboardKey
>{
...
...
@@ -5136,6 +5139,9 @@ class PhysicalKeyboardKey extends KeyboardKey {
/// See the function [RawKeyEvent.physicalKey] for more information.
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
// can be searched.
static
const
Map
<
int
,
PhysicalKeyboardKey
>
_knownPhysicalKeys
=
<
int
,
PhysicalKeyboardKey
>{
...
...
packages/flutter/lib/src/services/keyboard_maps.dart
View file @
caf876cf
...
...
@@ -2953,6 +2953,16 @@ const Map<int, LogicalKeyboardKey> kWindowsToLogicalKey = <int, LogicalKeyboardK
45
:
LogicalKeyboardKey
.
insert
,
46
:
LogicalKeyboardKey
.
delete
,
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
,
66
:
LogicalKeyboardKey
.
keyB
,
67
:
LogicalKeyboardKey
.
keyC
,
...
...
packages/flutter/lib/src/services/raw_keyboard.dart
View file @
caf876cf
...
...
@@ -3,10 +3,12 @@
// found in the LICENSE file.
import
'dart:io'
;
import
'dart:ui'
;
import
'dart:ui'
as
ui
;
import
'package:flutter/foundation.dart'
;
import
'binding.dart'
;
import
'hardware_keyboard.dart'
;
import
'keyboard_key.dart'
;
import
'raw_keyboard_android.dart'
;
import
'raw_keyboard_fuchsia.dart'
;
...
...
@@ -286,17 +288,20 @@ abstract class RawKeyEvent with Diagnosticable {
final
RawKeyEventData
data
;
String
?
character
;
if
(
kIsWeb
)
{
RawKeyEventData
_dataFromWeb
(
)
{
final
String
?
key
=
message
[
'key'
]
as
String
?;
data
=
RawKeyEventDataWeb
(
if
(
key
!=
null
&&
key
.
isNotEmpty
)
{
character
=
key
;
}
return
RawKeyEventDataWeb
(
code:
message
[
'code'
]
as
String
?
??
''
,
key:
key
??
''
,
location:
message
[
'location'
]
as
int
?
??
0
,
metaState:
message
[
'metaState'
]
as
int
?
??
0
,
);
if
(
key
!=
null
&&
key
.
isNotEmpty
)
{
character
=
key
;
}
}
if
(
kIsWeb
)
{
data
=
_dataFromWeb
();
}
else
{
final
String
keymap
=
message
[
'keymap'
]
as
String
;
switch
(
keymap
)
{
...
...
@@ -373,16 +378,7 @@ abstract class RawKeyEvent with Diagnosticable {
}
break
;
case
'web'
:
final
String
?
key
=
message
[
'key'
]
as
String
?;
data
=
RawKeyEventDataWeb
(
code:
message
[
'code'
]
as
String
?
??
''
,
key:
key
??
''
,
location:
message
[
'location'
]
as
int
?
??
0
,
metaState:
message
[
'metaState'
]
as
int
?
??
0
,
);
if
(
key
!=
null
&&
key
.
isNotEmpty
)
{
character
=
key
;
}
data
=
_dataFromWeb
();
break
;
default
:
/// This exception would only be hit on platforms that haven't yet
...
...
@@ -573,9 +569,7 @@ typedef RawKeyEventHandler = bool Function(RawKeyEvent event);
/// * [SystemChannels.keyEvent], the low-level channel used for receiving
/// events from the system.
class
RawKeyboard
{
RawKeyboard
.
_
()
{
SystemChannels
.
keyEvent
.
setMessageHandler
(
_handleKeyEvent
);
}
RawKeyboard
.
_
();
/// The shared instance of [RawKeyboard].
static
final
RawKeyboard
instance
=
RawKeyboard
.
_
();
...
...
@@ -606,39 +600,45 @@ class RawKeyboard {
_listeners
.
remove
(
listener
);
}
/// A handler for hardware keyboard events that will stop propagation if the
/// handler returns true.
///
/// Key events on the platform are given to Flutter to be handled by the
/// engine. If they are not handled, then the platform will continue to
/// distribute the keys (i.e. propagate them) to other (possibly non-Flutter)
/// components in the application. The return value from this handler tells
/// the platform to either stop propagation (by returning true: "event
/// handled"), or pass the event on to other controls (false: "event not
/// handled").
///
/// This handler is normally set by the [FocusManager] so that it can control
/// the key event propagation to focused widgets.
///
/// Most applications can use the focus system (see [Focus] and
/// [FocusManager]) to receive key events. If you are not using the
/// [FocusManager] to manage focus, then to be able to stop propagation of the
/// event by indicating that the event was handled, set this attribute to a
/// [RawKeyEventHandler]. Otherwise, key events will be assumed to not have
/// been handled by Flutter, and will also be sent to other (possibly
/// non-Flutter) controls in the application.
///
/// See also:
///
/// * [Focus.onKey], a [Focus] callback attribute that will be given key
/// events distributed by the [FocusManager] based on the current primary
/// focus.
/// * [addListener], to add passive key event listeners that do not stop event
/// propagation.
RawKeyEventHandler
?
keyEventHandler
;
/// A handler for raw hardware keyboard events that will stop propagation if
/// the handler returns true.
///
/// This property is only a wrapper over [KeyEventManager.keyMessageHandler],
/// and is kept only for backward compatibility. New code should use
/// [KeyEventManager.keyMessageHandler] to set custom global key event
/// handler. Setting [keyEventHandler] will cause
/// [KeyEventManager.keyMessageHandler] to be set with a converted handler.
/// If [KeyEventManager.keyMessageHandler] is set by [FocusManager] (the most
/// common situation), then the exact value of [keyEventHandler] is a dummy
/// callback and must not be invoked.
RawKeyEventHandler
?
get
keyEventHandler
{
if
(
ServicesBinding
.
instance
!.
keyEventManager
.
keyMessageHandler
!=
_cachedKeyMessageHandler
)
{
_cachedKeyMessageHandler
=
ServicesBinding
.
instance
!.
keyEventManager
.
keyMessageHandler
;
_cachedKeyEventHandler
=
_cachedKeyMessageHandler
==
null
?
null
:
(
RawKeyEvent
event
)
{
assert
(
false
,
'The RawKeyboard.instance.keyEventHandler assigned by Flutter is a dummy '
'callback kept for compatibility and should not be directly called. Use '
'ServicesBinding.instance!.keyMessageHandler instead.'
);
return
true
;
};
}
return
_cachedKeyEventHandler
;
}
RawKeyEventHandler
?
_cachedKeyEventHandler
;
KeyMessageHandler
?
_cachedKeyMessageHandler
;
set
keyEventHandler
(
RawKeyEventHandler
?
handler
)
{
_cachedKeyEventHandler
=
handler
;
_cachedKeyMessageHandler
=
handler
==
null
?
null
:
(
KeyMessage
message
)
=>
handler
(
message
.
rawEvent
);
ServicesBinding
.
instance
!.
keyEventManager
.
keyMessageHandler
=
_cachedKeyMessageHandler
;
}
Future
<
dynamic
>
_handleKeyEvent
(
dynamic
message
)
async
{
final
RawKeyEvent
event
=
RawKeyEvent
.
fromMessage
(
message
as
Map
<
String
,
dynamic
>);
/// Process a new [RawKeyEvent] by recording the state changes and
/// dispatching to listeners.
bool
handleRawKeyEvent
(
RawKeyEvent
event
)
{
bool
shouldDispatch
=
true
;
if
(
event
is
RawKeyDownEvent
)
{
if
(
event
.
data
.
shouldDispatchEvent
())
{
...
...
@@ -659,7 +659,7 @@ class RawKeyboard {
}
}
if
(!
shouldDispatch
)
{
return
<
String
,
dynamic
>{
'handled'
:
true
}
;
return
true
;
}
// Make sure that the modifiers reflect reality, in case a modifier key was
// pressed/released while the app didn't have focus.
...
...
@@ -678,12 +678,7 @@ class RawKeyboard {
}
}
// Send the key event to the keyEventHandler, then send the appropriate
// 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
};
return
false
;
}
static
final
Map
<
_ModifierSidePair
,
Set
<
PhysicalKeyboardKey
>>
_modifierKeyMap
=
<
_ModifierSidePair
,
Set
<
PhysicalKeyboardKey
>>{
...
...
@@ -808,6 +803,11 @@ class RawKeyboard {
/// Returns the set of physical keys currently pressed.
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].
///
/// This is used by the testing framework to make sure tests are hermetic.
...
...
@@ -832,5 +832,5 @@ class _ModifierSidePair {
}
@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 @
caf876cf
...
...
@@ -83,13 +83,11 @@ class RawKeyEventDataWeb extends RawKeyEventData {
@override
LogicalKeyboardKey
get
logicalKey
{
// Look to see if the keyCode is a printable number pad key, so that a
// difference between regular keys (e.g. ".") and the number pad version
// (e.g. the "." on the number pad) can be determined.
final
LogicalKeyboardKey
?
numPadKey
=
kWebNumPadMap
[
code
];
if
(
numPadKey
!=
null
)
{
return
numPadKey
;
}
// Look to see if the keyCode is a key based on location. Typically they are
// numpad keys (versus main area keys) and left/right modifiers.
final
LogicalKeyboardKey
?
maybeLocationKey
=
kWebLocationMap
[
key
]?[
location
];
if
(
maybeLocationKey
!=
null
)
return
maybeLocationKey
;
// Look to see if the [code] is one we know about and have a mapping for.
final
LogicalKeyboardKey
?
newKey
=
kWebToLogicalKey
[
code
];
...
...
packages/flutter/lib/src/widgets/focus_manager.dart
View file @
caf876cf
...
...
@@ -36,7 +36,7 @@ bool _focusDebug(String message, [Iterable<String>? details]) {
}
/// An enum that describes how to handle a key event handled by a
/// [FocusOnKeyCallback].
/// [FocusOnKeyCallback]
or [FocusOnKeyEventCallback]
.
enum
KeyEventResult
{
/// The key event has been handled, and the event should not be propagated to
/// other key event handlers.
...
...
@@ -52,6 +52,32 @@ enum KeyEventResult {
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]
/// to receive key events.
///
...
...
@@ -61,6 +87,15 @@ enum KeyEventResult {
/// was handled.
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].
///
/// Using a [FocusAttachment] is rarely needed, unless you are building
...
...
@@ -271,12 +306,13 @@ enum UnfocusDisposition {
/// {@template flutter.widgets.FocusNode.keyEvents}
/// ## Key Event Propagation
///
/// The [FocusManager] receives key events from [RawKeyboard] and will pass them
/// to the focused nodes. It starts with the node with the primary focus, and
/// will call the [onKey] callback for that node. If the callback returns false,
/// indicating that it did not handle the event, the [FocusManager] will move to
/// the parent of that node and call its [onKey]. If that [onKey] returns true,
/// then it will stop propagating the event. If it reaches the root
/// The [FocusManager] receives key events from [RawKeyboard] and
/// [HardwareKeyboard] and will pass them to the focused nodes. It starts with
/// the node with the primary focus, and will call the [onKey] or [onKeyEvent]
/// callback for that node. If the callback returns false, indicating that it did
/// not handle the event, the [FocusManager] will move to the parent of that node
/// 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.
/// {@endtemplate}
///
...
...
@@ -433,9 +469,14 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
///
/// The [skipTraversal], [descendantsAreFocusable], and [canRequestFocus]
/// 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
({
String
?
debugLabel
,
this
.
onKey
,
this
.
onKeyEvent
,
bool
skipTraversal
=
false
,
bool
canRequestFocus
=
true
,
bool
descendantsAreFocusable
=
true
,
...
...
@@ -574,9 +615,18 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
/// Called if this focus node receives a key event while focused (i.e. when
/// [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}
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
;
List
<
FocusNode
>?
_ancestors
;
List
<
FocusNode
>?
_descendants
;
...
...
@@ -1028,10 +1078,19 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
/// 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
/// 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
FocusAttachment
attach
(
BuildContext
?
context
,
{
FocusOnKeyCallback
?
onKey
})
{
FocusAttachment
attach
(
BuildContext
?
context
,
{
FocusOnKeyEventCallback
?
onKeyEvent
,
FocusOnKeyCallback
?
onKey
,
})
{
_context
=
context
;
this
.
onKey
=
onKey
??
this
.
onKey
;
this
.
onKeyEvent
=
onKeyEvent
??
this
.
onKeyEvent
;
_attachment
=
FocusAttachment
.
_
(
this
);
return
_attachment
!;
}
...
...
@@ -1225,6 +1284,7 @@ class FocusScopeNode extends FocusNode {
/// All parameters are optional.
FocusScopeNode
({
String
?
debugLabel
,
FocusOnKeyEventCallback
?
onKeyEvent
,
FocusOnKeyCallback
?
onKey
,
bool
skipTraversal
=
false
,
bool
canRequestFocus
=
true
,
...
...
@@ -1232,6 +1292,7 @@ class FocusScopeNode extends FocusNode {
assert
(
canRequestFocus
!=
null
),
super
(
debugLabel:
debugLabel
,
onKeyEvent:
onKeyEvent
,
onKey:
onKey
,
canRequestFocus:
canRequestFocus
,
descendantsAreFocusable:
true
,
...
...
@@ -1463,15 +1524,15 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
/// When this focus manager is no longer needed, calling [dispose] on it will
/// unregister these handlers.
void
registerGlobalHandlers
()
{
assert
(
RawKeyboard
.
instance
.
keyEvent
Handler
==
null
);
RawKeyboard
.
instance
.
keyEventHandler
=
_handleRawKeyEvent
;
assert
(
ServicesBinding
.
instance
!.
keyEventManager
.
keyMessage
Handler
==
null
);
ServicesBinding
.
instance
!.
keyEventManager
.
keyMessageHandler
=
_handleKeyMessage
;
GestureBinding
.
instance
!.
pointerRouter
.
addGlobalRoute
(
_handlePointerEvent
);
}
@override
void
dispose
()
{
if
(
RawKeyboard
.
instance
.
keyEventHandler
==
_handleRawKeyEvent
)
{
RawKeyboard
.
instance
.
keyEvent
Handler
=
null
;
if
(
ServicesBinding
.
instance
!.
keyEventManager
.
keyMessageHandler
==
_handleKeyMessage
)
{
ServicesBinding
.
instance
!.
keyEventManager
.
keyMessage
Handler
=
null
;
GestureBinding
.
instance
!.
pointerRouter
.
removeGlobalRoute
(
_handlePointerEvent
);
}
super
.
dispose
();
...
...
@@ -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
// look at the highlight mode, and it should be accurate.
_lastInteractionWasTouch
=
false
;
_updateHighlightMode
();
assert
(
_focusDebug
(
'Received key event
$
{event.logicalKey}
'
));
assert
(
_focusDebug
(
'Received key event
$
message
'
));
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
;
}
...
...
@@ -1677,25 +1738,35 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
// stop propagation, stop.
bool
handled
=
false
;
for
(
final
FocusNode
node
in
<
FocusNode
>[
_primaryFocus
!,
...
_primaryFocus
!.
ancestors
])
{
if
(
node
.
onKey
!=
null
)
{
final
KeyEventResult
result
=
node
.
onKey
!(
node
,
event
);
switch
(
result
)
{
case
KeyEventResult
.
handled
:
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
;
final
List
<
KeyEventResult
>
results
=
<
KeyEventResult
>[];
if
(
node
.
onKeyEvent
!=
null
)
{
for
(
final
KeyEvent
event
in
message
.
events
)
{
results
.
add
(
node
.
onKeyEvent
!(
node
,
event
));
}
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
)
{
assert
(
_focusDebug
(
'Key event not handled by anyone:
$
event
.'
));
assert
(
_focusDebug
(
'Key event not handled by anyone:
$
message
.'
));
}
return
handled
;
}
...
...
packages/flutter/lib/src/widgets/focus_scope.dart
View file @
caf876cf
...
...
@@ -283,6 +283,7 @@ class Focus extends StatefulWidget {
this
.
autofocus
=
false
,
this
.
onFocusChange
,
this
.
onKey
,
this
.
onKeyEvent
,
this
.
debugLabel
,
this
.
canRequestFocus
,
this
.
descendantsAreFocusable
=
true
,
...
...
@@ -315,6 +316,24 @@ class Focus extends StatefulWidget {
/// focus.
///
/// 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
/// node up the focus hierarchy in turn. If an event reaches the root of the
/// hierarchy, it is discarded.
...
...
@@ -540,7 +559,7 @@ class Focus extends StatefulWidget {
}
@override
State
<
Focus
>
createState
()
=>
_FocusState
();
State
<
Focus
>
createState
()
=>
_FocusState
();
}
class
_FocusState
extends
State
<
Focus
>
{
...
...
@@ -575,7 +594,7 @@ class _FocusState extends State<Focus> {
_canRequestFocus
=
focusNode
.
canRequestFocus
;
_descendantsAreFocusable
=
focusNode
.
descendantsAreFocusable
;
_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
// not be listening now if we're re-using a previous one because it should
...
...
@@ -914,6 +933,7 @@ class FocusScope extends Focus {
ValueChanged
<
bool
>?
onFocusChange
,
bool
?
canRequestFocus
,
bool
?
skipTraversal
,
FocusOnKeyEventCallback
?
onKeyEvent
,
FocusOnKeyCallback
?
onKey
,
String
?
debugLabel
,
})
:
assert
(
child
!=
null
),
...
...
@@ -926,6 +946,7 @@ class FocusScope extends Focus {
onFocusChange:
onFocusChange
,
canRequestFocus:
canRequestFocus
,
skipTraversal:
skipTraversal
,
onKeyEvent:
onKeyEvent
,
onKey:
onKey
,
debugLabel:
debugLabel
,
);
...
...
packages/flutter/lib/src/widgets/keyboard_listener.dart
0 → 100644
View file @
caf876cf
// 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 @
caf876cf
...
...
@@ -21,10 +21,16 @@ export 'package:flutter/services.dart' show RawKeyEvent;
/// For text entry, consider using a [EditableText], which integrates with
/// 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:
///
/// * [EditableText], which should be used instead of this widget for text
/// entry.
/// * [KeyboardListener], a similar widget based on the newer
/// [HardwareKeyboard] API.
class
RawKeyboardListener
extends
StatefulWidget
{
/// Creates a widget that receives raw keyboard events.
///
...
...
packages/flutter/lib/src/widgets/shortcuts.dart
View file @
caf876cf
...
...
@@ -194,13 +194,13 @@ abstract class ShortcutActivator {
/// event.
///
/// 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
/// check if KeyA is pressed, since it's already guaranteed.
///
/// This method must not cause any side effects for the `state`. Typically
/// this is only used to query whether [
RawKeyboard.keysPressed] contains
/// a key.
/// this is only used to query whether [
HardwareKeyboard.logicalKeysPressed]
///
contains
a key.
///
/// Since [ShortcutActivator] accepts all event types, subclasses might want
/// to check the event type in [accepts].
...
...
@@ -314,11 +314,13 @@ class LogicalKeySet extends KeySet<LogicalKeyboardKey> with Diagnosticable
@override
bool
accepts
(
RawKeyEvent
event
,
RawKeyboard
state
)
{
if
(
event
is
!
RawKeyDownEvent
)
return
false
;
final
Set
<
LogicalKeyboardKey
>
collapsedRequired
=
LogicalKeyboardKey
.
collapseSynonyms
(
keys
);
final
Set
<
LogicalKeyboardKey
>
collapsedPressed
=
LogicalKeyboardKey
.
collapseSynonyms
(
state
.
keysPressed
);
final
bool
keysEqual
=
collapsedRequired
.
difference
(
collapsedPressed
).
isEmpty
&&
collapsedRequired
.
length
==
collapsedPressed
.
length
;
return
event
is
RawKeyDownEvent
&&
keysEqual
;
return
keysEqual
;
}
static
final
Set
<
LogicalKeyboardKey
>
_modifiers
=
<
LogicalKeyboardKey
>{
...
...
@@ -425,7 +427,8 @@ class ShortcutMapProperty extends DiagnosticsProperty<Map<ShortcutActivator, Int
/// * [CharacterActivator], an activator that represents key combinations
/// that result in the specified character, such as question mark.
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
/// modifiers, such as [LogicalKeyboardKey.keyC] as in `Ctrl+C`. It must not be
...
...
@@ -434,6 +437,9 @@ class SingleActivator with Diagnosticable implements ShortcutActivator {
/// The `control`, `shift`, `alt`, and `meta` flags represent whether
/// 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}
/// In the following example, the shortcut `Control + C` increases the counter:
///
...
...
@@ -811,17 +817,7 @@ class ShortcutManager extends ChangeNotifier with Diagnosticable {
/// must be mapped to an [Action], and the [Action] must be enabled.
@protected
KeyEventResult
handleKeypress
(
BuildContext
context
,
RawKeyEvent
event
)
{
if
(
event
is
!
RawKeyDownEvent
)
{
return
KeyEventResult
.
ignored
;
}
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
);
if
(
matchedIntent
!=
null
)
{
final
BuildContext
?
primaryContext
=
primaryFocus
?.
context
;
...
...
@@ -1285,4 +1281,4 @@ class CallbackShortcuts extends StatelessWidget {
child:
child
,
);
}
}
\ No newline at end of file
}
packages/flutter/lib/widgets.dart
View file @
caf876cf
...
...
@@ -63,6 +63,7 @@ export 'src/widgets/inherited_model.dart';
export
'src/widgets/inherited_notifier.dart'
;
export
'src/widgets/inherited_theme.dart'
;
export
'src/widgets/interactive_viewer.dart'
;
export
'src/widgets/keyboard_listener.dart'
;
export
'src/widgets/layout_builder.dart'
;
export
'src/widgets/list_wheel_scroll_view.dart'
;
export
'src/widgets/localizations.dart'
;
...
...
packages/flutter/test/cupertino/text_field_test.dart
View file @
caf876cf
...
...
@@ -4348,7 +4348,7 @@ void main() {
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
pump
();
expect
(
focusNode3
.
hasPrimaryFocus
,
isTrue
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Scrolling shortcuts are disabled in text fields'
,
(
WidgetTester
tester
)
async
{
bool
scrollInvoked
=
false
;
...
...
@@ -4381,7 +4381,7 @@ void main() {
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowDown
);
expect
(
scrollInvoked
,
isFalse
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Cupertino text field semantics'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
...
...
packages/flutter/test/material/text_field_test.dart
View file @
caf876cf
...
...
@@ -4731,7 +4731,7 @@ void main() {
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowLeft
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
shift
);
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
-
1
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Shift test 2'
,
(
WidgetTester
tester
)
async
{
await
setupWidget
(
tester
);
...
...
@@ -4749,7 +4749,7 @@ void main() {
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
1
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Control Shift test'
,
(
WidgetTester
tester
)
async
{
await
setupWidget
(
tester
);
...
...
@@ -4766,7 +4766,7 @@ void main() {
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
5
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Down and up test'
,
(
WidgetTester
tester
)
async
{
await
setupWidget
(
tester
);
...
...
@@ -4793,7 +4793,7 @@ void main() {
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
0
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Down and up test 2'
,
(
WidgetTester
tester
)
async
{
await
setupWidget
(
tester
);
...
...
@@ -4849,7 +4849,7 @@ void main() {
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
-
5
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Read only keyboard selection test'
,
(
WidgetTester
tester
)
async
{
final
TextEditingController
controller
=
TextEditingController
(
text:
'readonly'
);
...
...
@@ -4869,7 +4869,7 @@ void main() {
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
shift
);
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
arrowLeft
);
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
-
1
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
},
skip:
areKeyEventsHandledByPlatform
);
testWidgets
(
'Copy paste test'
,
(
WidgetTester
tester
)
async
{
...
...
@@ -4944,7 +4944,7 @@ void main() {
const
String
expected
=
'a biga big house
\n
jumped over a mouse'
;
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
{
final
FocusNode
focusNode
=
FocusNode
();
...
...
@@ -5018,7 +5018,7 @@ void main() {
const
String
expected
=
'a biga big house jumped over a mouse'
;
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
testWidgets
(
'Paste does not crash when the section is inValid'
,
(
WidgetTester
tester
)
async
{
...
...
@@ -5069,7 +5069,7 @@ void main() {
// Do nothing.
expect
(
find
.
text
(
clipboardContent
),
findsNothing
);
expect
(
controller
.
selection
,
const
TextSelection
.
collapsed
(
offset:
-
1
));
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Cut test'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
();
...
...
@@ -5145,7 +5145,7 @@ void main() {
const
String
expected
=
' housa bige
\n
jumped over a mouse'
;
expect
(
find
.
text
(
expected
),
findsOneWidget
);
},
skip:
areKeyEventsHandledByPlatform
);
},
skip:
areKeyEventsHandledByPlatform
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Cut obscured text test'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
();
...
...
@@ -5220,7 +5220,7 @@ void main() {
const
String
expected
=
' housa bige jumped over a mouse'
;
expect
(
find
.
text
(
expected
),
findsOneWidget
);
},
skip:
areKeyEventsHandledByPlatform
);
},
skip:
areKeyEventsHandledByPlatform
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Select all test'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
();
...
...
@@ -5269,7 +5269,7 @@ void main() {
const
String
expected
=
''
;
expect
(
find
.
text
(
expected
),
findsOneWidget
);
},
skip:
areKeyEventsHandledByPlatform
);
},
skip:
areKeyEventsHandledByPlatform
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Delete test'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
();
...
...
@@ -5321,7 +5321,7 @@ void main() {
const
String
expected2
=
''
;
expect
(
find
.
text
(
expected2
),
findsOneWidget
);
},
skip:
areKeyEventsHandledByPlatform
);
},
skip:
areKeyEventsHandledByPlatform
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Changing positions of text fields'
,
(
WidgetTester
tester
)
async
{
...
...
@@ -5413,7 +5413,7 @@ void main() {
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
shift
);
expect
(
c1
.
selection
.
extentOffset
-
c1
.
selection
.
baseOffset
,
-
10
);
},
skip:
areKeyEventsHandledByPlatform
);
},
skip:
areKeyEventsHandledByPlatform
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Changing focus test'
,
(
WidgetTester
tester
)
async
{
...
...
@@ -5488,7 +5488,7 @@ void main() {
expect
(
c1
.
selection
.
extentOffset
-
c1
.
selection
.
baseOffset
,
0
);
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
{
final
TextEditingController
controller
=
TextEditingController
();
...
...
packages/flutter/test/rendering/editable_test.dart
View file @
caf876cf
...
...
@@ -16,6 +16,11 @@ import 'package:flutter/material.dart';
import
'package:flutter/rendering.dart'
;
import
'package:flutter/services.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/recording_canvas.dart'
;
...
...
@@ -35,6 +40,38 @@ class FakeEditableTextState with TextSelectionDelegate {
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
(
)
{
test
(
'RenderEditable respects clipBehavior'
,
()
{
const
BoxConstraints
viewport
=
BoxConstraints
(
maxHeight:
100.0
,
maxWidth:
100.0
);
...
...
@@ -1343,7 +1380,7 @@ void main() {
expect
(
currentSelection
.
extentOffset
,
1
);
},
skip:
isBrowser
);
// https://github.com/flutter/flutter/issues/58068
test
(
'respects enableInteractiveSelection'
,
()
async
{
test
Variants
(
'respects enableInteractiveSelection'
,
()
async
{
const
String
text
=
'012345'
;
final
TextSelectionDelegate
delegate
=
FakeEditableTextState
()
..
textEditingValue
=
const
TextEditingValue
(
text:
text
);
...
...
@@ -1403,7 +1440,7 @@ void main() {
await
simulateKeyUpEvent
(
wordModifier
);
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'
,
()
{
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 @
caf876cf
// 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 @
caf876cf
...
...
@@ -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'
,
()
{
...
...
@@ -955,6 +1019,7 @@ void main() {
},
);
expect
(
message
,
equals
(<
String
,
dynamic
>{
'handled'
:
false
}));
message
=
null
;
// Set up a widget that will receive focused text events.
final
FocusNode
focusNode
=
FocusNode
(
debugLabel:
'Test Node'
);
...
...
packages/flutter/test/widgets/app_test.dart
View file @
caf876cf
...
...
@@ -148,7 +148,7 @@ void main() {
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
gameButtonA
);
await
tester
.
pumpAndSettle
();
expect
(
checked
,
isTrue
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
group
(
'error control test'
,
()
{
Future
<
void
>
expectFlutterError
({
...
...
packages/flutter/test/widgets/default_text_editing_actions_test.dart
View file @
caf876cf
...
...
@@ -114,5 +114,5 @@ void main() {
await
tester
.
pump
();
expect
(
leftCalled
,
isFalse
);
expect
(
rightCalled
,
isTrue
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
}
packages/flutter/test/widgets/editable_text_cursor_test.dart
View file @
caf876cf
...
...
@@ -328,6 +328,8 @@ void main() {
});
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'
;
final
TextEditingController
controller
=
TextEditingController
(
text:
testText
);
final
Widget
widget
=
MaterialApp
(
...
...
@@ -400,7 +402,9 @@ void main() {
await
tester
.
pump
(
const
Duration
(
milliseconds:
1
));
expect
(
renderEditable
.
cursorColor
!.
alpha
,
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
{
const
Widget
widget
=
MaterialApp
(
...
...
packages/flutter/test/widgets/editable_text_test.dart
View file @
caf876cf
...
...
@@ -4864,8 +4864,23 @@ void main() {
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
);
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.
},
skip:
kIsWeb
,
variant:
TargetPlatformVariant
.
all
());
...
...
@@ -7675,7 +7690,7 @@ void main() {
expect
(
controller
.
selection
.
isCollapsed
,
true
);
expect
(
controller
.
selection
.
baseOffset
,
1
);
}
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'the toolbar is disposed when selection changes and there is no selectionControls'
,
(
WidgetTester
tester
)
async
{
late
StateSetter
setState
;
...
...
packages/flutter/test/widgets/focus_manager_test.dart
View file @
caf876cf
...
...
@@ -165,7 +165,101 @@ void main() {
'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
,
()
{
testWidgets
(
'Can setFirstFocus on a scope with no manager.'
,
(
WidgetTester
tester
)
async
{
...
...
@@ -935,7 +1029,7 @@ void main() {
// Since none of the focused nodes handle this event, nothing should
// receive it.
expect
(
receivedAnEvent
,
isEmpty
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Initial highlight mode guesses correctly.'
,
(
WidgetTester
tester
)
async
{
FocusManager
.
instance
.
highlightStrategy
=
FocusHighlightStrategy
.
automatic
;
...
...
packages/flutter/test/widgets/focus_traversal_test.dart
View file @
caf876cf
...
...
@@ -1678,7 +1678,7 @@ void main() {
expect
(
Focus
.
of
(
lowerLeftKey
.
currentContext
!).
hasPrimaryFocus
,
isTrue
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowUp
);
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
{
final
List
<
int
>
items
=
List
<
int
>.
generate
(
11
,
(
int
index
)
=>
index
).
toList
();
...
...
@@ -1776,7 +1776,7 @@ void main() {
await
tester
.
pump
();
expect
(
topNode
.
hasPrimaryFocus
,
isTrue
);
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
{
final
List
<
int
>
items
=
List
<
int
>.
generate
(
11
,
(
int
index
)
=>
index
).
toList
();
...
...
@@ -1874,7 +1874,7 @@ void main() {
await
tester
.
pump
();
expect
(
leftNode
.
hasPrimaryFocus
,
isTrue
);
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
{
final
GlobalKey
upperLeftKey
=
GlobalKey
(
debugLabel:
'upperLeftKey'
);
...
...
@@ -1997,10 +1997,10 @@ void main() {
expect
(
focusNodeUpperLeft
.
hasPrimaryFocus
,
isTrue
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowUp
);
expect
(
focusNodeUpperLeft
.
hasPrimaryFocus
,
isTrue
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
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
()));
...
...
@@ -2013,7 +2013,7 @@ void main() {
await
tester
.
idle
();
expect
(
events
.
length
,
2
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
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'
)))));
...
...
@@ -2047,7 +2047,7 @@ void main() {
await
tester
.
idle
();
expect
(
events
.
length
,
2
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
});
group
(
FocusTraversalGroup
,
()
{
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 @
caf876cf
// 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 @
caf876cf
...
...
@@ -520,7 +520,7 @@ void main() {
await
tester
.
pumpAndSettle
();
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
)));
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Vertical scrollables are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
...
...
@@ -571,7 +571,7 @@ void main() {
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
pageUp
);
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
)));
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Horizontal scrollables are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
...
...
@@ -617,7 +617,7 @@ void main() {
await
tester
.
sendKeyUpEvent
(
modifierKey
);
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
)));
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Horizontal scrollables are scrolled the correct direction in RTL locales.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
...
...
@@ -666,7 +666,7 @@ void main() {
await
tester
.
sendKeyUpEvent
(
modifierKey
);
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
)));
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Reversed vertical scrollables are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
...
...
@@ -720,7 +720,7 @@ void main() {
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
pageDown
);
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
)));
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Reversed horizontal scrollables are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
...
...
@@ -768,7 +768,7 @@ void main() {
if
(!
kIsWeb
)
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
pumpAndSettle
();
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Custom scrollables with a center sliver are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
...
...
@@ -828,7 +828,7 @@ void main() {
// Goes up two past "center" where it started, so negative.
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
)));
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Can recommendDeferredLoadingForContext - animation'
,
(
WidgetTester
tester
)
async
{
final
List
<
String
>
widgetTracker
=
<
String
>[];
...
...
packages/flutter/test/widgets/selectable_text_test.dart
View file @
caf876cf
...
...
@@ -1570,7 +1570,7 @@ void main() {
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
shift
);
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
arrowLeft
);
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
-
1
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Shift test 2'
,
(
WidgetTester
tester
)
async
{
await
setupWidget
(
tester
,
'abcdefghi'
);
...
...
@@ -1582,7 +1582,7 @@ void main() {
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
1
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Control Shift test'
,
(
WidgetTester
tester
)
async
{
await
setupWidget
(
tester
,
'their big house'
);
...
...
@@ -1594,7 +1594,7 @@ void main() {
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
-
5
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Down and up test'
,
(
WidgetTester
tester
)
async
{
await
setupWidget
(
tester
,
'a big house'
);
...
...
@@ -1612,7 +1612,7 @@ void main() {
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
0
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
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'
);
...
...
@@ -1663,7 +1663,7 @@ void main() {
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
extentOffset
-
controller
.
selection
.
baseOffset
,
-
5
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
});
testWidgets
(
'Copy test'
,
(
WidgetTester
tester
)
async
{
...
...
@@ -1722,7 +1722,7 @@ void main() {
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
pumpAndSettle
();
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Select all test'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
();
...
...
@@ -1757,7 +1757,7 @@ void main() {
expect
(
controller
.
selection
.
baseOffset
,
0
);
expect
(
controller
.
selection
.
extentOffset
,
31
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'keyboard selection should call onSelectionChanged'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
();
...
...
@@ -1804,7 +1804,7 @@ void main() {
expect
(
newSelection
!.
extentOffset
,
i
+
1
);
newSelection
=
null
;
}
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Changing positions of selectable text'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
();
...
...
@@ -1894,7 +1894,7 @@ void main() {
c1
=
editableTextWidget
.
controller
;
expect
(
c1
.
selection
.
extentOffset
-
c1
.
selection
.
baseOffset
,
-
6
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Changing focus test'
,
(
WidgetTester
tester
)
async
{
...
...
@@ -1964,7 +1964,7 @@ void main() {
expect
(
c1
.
selection
.
extentOffset
-
c1
.
selection
.
baseOffset
,
0
);
expect
(
c2
.
selection
.
extentOffset
-
c2
.
selection
.
baseOffset
,
-
5
);
});
}
,
variant:
KeySimulatorTransitModeVariant
.
all
()
);
testWidgets
(
'Caret works when maxLines is null'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
...
...
packages/flutter/test/widgets/shortcuts_test.dart
View file @
caf876cf
...
...
@@ -435,7 +435,33 @@ void main() {
invoked
=
0
;
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
{
int
invoked
=
0
;
...
...
@@ -1075,7 +1101,27 @@ void main() {
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
shiftLeft
);
expect
(
invoked
,
1
);
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'
,
()
{
...
...
packages/flutter_test/lib/src/binding.dart
View file @
caf876cf
...
...
@@ -828,6 +828,9 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
assert
(
debugAssertAllSchedulerVarsUnset
(
'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
)
{
...
...
@@ -894,6 +897,10 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
// tests.
// ignore: invalid_use_of_visible_for_testing_member
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
,
'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 '
...
...
packages/flutter_test/lib/src/controller.dart
View file @
caf876cf
...
...
@@ -973,7 +973,7 @@ abstract class WidgetController {
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
/// soft keyboard.
...
...
@@ -984,6 +984,9 @@ abstract class WidgetController {
/// 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].
///
/// 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
...
...
@@ -1004,7 +1007,7 @@ abstract class WidgetController {
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
/// from a soft keyboard.
...
...
@@ -1015,13 +1018,17 @@ abstract class WidgetController {
/// 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].
///
/// 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:
///
/// - [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.
Future
<
bool
>
sendKeyDownEvent
(
LogicalKeyboardKey
key
,
{
String
?
character
,
String
platform
=
_defaultPlatform
})
async
{
assert
(
platform
!=
null
);
...
...
@@ -1039,11 +1046,15 @@ abstract class WidgetController {
/// that type of system. Defaults to "web" on web, and "android" everywhere
/// 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.
///
/// 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.
Future
<
bool
>
sendKeyUpEvent
(
LogicalKeyboardKey
key
,
{
String
platform
=
_defaultPlatform
})
async
{
assert
(
platform
!=
null
);
...
...
@@ -1051,6 +1062,35 @@ abstract class WidgetController {
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
/// the widget's render object has been laid out at least once.
Rect
getRect
(
Finder
finder
)
=>
getTopLeft
(
finder
)
&
getSize
(
finder
);
...
...
packages/flutter_test/lib/src/event_simulation.dart
View file @
caf876cf
This diff is collapsed.
Click to expand it.
packages/flutter_test/lib/src/test_pointer.dart
View file @
caf876cf
...
...
@@ -230,6 +230,7 @@ class TestPointer {
timeStamp:
timeStamp
,
kind:
kind
,
device:
_device
,
pointer:
pointer
,
position:
_location
??
Offset
.
zero
,
);
}
...
...
@@ -255,6 +256,7 @@ class TestPointer {
timeStamp:
timeStamp
,
kind:
kind
,
device:
_device
,
pointer:
pointer
,
position:
newLocation
,
delta:
delta
,
);
...
...
packages/flutter_test/lib/src/widget_tester.dart
View file @
caf876cf
...
...
@@ -510,7 +510,7 @@ Future<void> expectLater(
/// Class that programmatically interacts with widgets and the test environment.
///
/// 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
{
WidgetTester
.
_
(
TestWidgetsFlutterBinding
binding
)
:
super
(
binding
)
{
if
(
binding
is
LiveTestWidgetsFlutterBinding
)
...
...
packages/flutter_test/test/event_simulation_test.dart
View file @
caf876cf
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