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
32b9c2f0
Unverified
Commit
32b9c2f0
authored
Feb 05, 2019
by
xster
Committed by
GitHub
Feb 05, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add more RenderEditable test coverage (#27003)
parent
37108c4c
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
1111 additions
and
824 deletions
+1111
-824
editable.dart
packages/flutter/lib/src/rendering/editable.dart
+1
-1
editable_text.dart
packages/flutter/lib/src/widgets/editable_text.dart
+13
-9
text_field_test.dart
packages/flutter/test/material/text_field_test.dart
+21
-20
editable_test.dart
packages/flutter/test/rendering/editable_test.dart
+63
-76
editable_text_cursor_test.dart
packages/flutter/test/widgets/editable_text_cursor_test.dart
+584
-0
editable_text_test.dart
packages/flutter/test/widgets/editable_text_test.dart
+429
-718
No files found.
packages/flutter/lib/src/rendering/editable.dart
View file @
32b9c2f0
...
...
@@ -1516,7 +1516,7 @@ class RenderEditable extends RenderBox {
_textPainter
.
paint
(
context
.
canvas
,
effectiveOffset
);
if
(
_selection
!=
null
&&
!
_floatingCursorOn
)
{
if
(
_selection
.
isCollapsed
&&
cursorColor
!=
null
&&
_hasFocus
)
{
if
(
_selection
.
isCollapsed
&&
_showCursor
.
value
&&
cursorColor
!=
null
)
{
_paintCaret
(
context
.
canvas
,
effectiveOffset
,
_selection
.
extent
);
}
else
if
(!
_selection
.
isCollapsed
&&
_selectionColor
!=
null
)
{
_selectionRects
??=
_textPainter
.
getBoxesForSelection
(
_selection
);
...
...
packages/flutter/lib/src/widgets/editable_text.dart
View file @
32b9c2f0
...
...
@@ -565,7 +565,8 @@ class EditableText extends StatefulWidget {
/// State for a [EditableText].
class
EditableTextState
extends
State
<
EditableText
>
with
AutomaticKeepAliveClientMixin
<
EditableText
>,
WidgetsBindingObserver
,
TickerProviderStateMixin
<
EditableText
>
implements
TextInputClient
,
TextSelectionDelegate
{
Timer
_cursorTimer
;
final
ValueNotifier
<
bool
>
_showCursor
=
ValueNotifier
<
bool
>(
false
);
bool
_targetCursorVisibility
=
false
;
final
ValueNotifier
<
bool
>
_cursorVisibilityNotifier
=
ValueNotifier
<
bool
>(
true
);
final
GlobalKey
_editableKey
=
GlobalKey
();
TextInputConnection
_textInputConnection
;
...
...
@@ -1006,12 +1007,13 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
void
_onCursorColorTick
()
{
renderEditable
.
cursorColor
=
widget
.
cursorColor
.
withOpacity
(
_cursorBlinkOpacityController
.
value
);
_cursorVisibilityNotifier
.
value
=
_cursorBlinkOpacityController
.
value
>
0
;
}
/// Whether the blinking cursor is actually visible at this precise moment
/// (it's hidden half the time, since it blinks).
@visibleForTesting
bool
get
cursorCurrentlyVisible
=>
_
showCursor
.
value
;
bool
get
cursorCurrentlyVisible
=>
_
cursorBlinkOpacityController
.
value
>
0
;
/// The cursor blink interval (the amount of time the cursor is in the "on"
/// state or the "off" state). A complete cursor blink period is twice this
...
...
@@ -1027,7 +1029,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
int
_obscureLatestCharIndex
;
void
_cursorTick
(
Timer
timer
)
{
_showCursor
.
value
=
!
_showCursor
.
value
;
_targetCursorVisibility
=
!
_targetCursorVisibility
;
final
double
targetOpacity
=
_targetCursorVisibility
?
1.0
:
0.0
;
if
(
widget
.
cursorOpacityAnimates
)
{
// If we want to show the cursor, we will animate the opacity to the value
// of 1.0, and likewise if we want to make it disappear, to 0.0. An easing
...
...
@@ -1036,10 +1039,9 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
//
// These values and curves have been obtained through eyeballing, so are
// likely not exactly the same as the values for native iOS.
final
double
toValue
=
_showCursor
.
value
?
1.0
:
0.0
;
_cursorBlinkOpacityController
.
animateTo
(
toValue
,
curve:
Curves
.
easeOut
);
_cursorBlinkOpacityController
.
animateTo
(
targetOpacity
,
curve:
Curves
.
easeOut
);
}
else
{
_cursorBlinkOpacityController
.
value
=
_showCursor
.
value
?
1.0
:
0.0
;
_cursorBlinkOpacityController
.
value
=
targetOpacity
;
}
if
(
_obscureShowCharTicksPending
>
0
)
{
...
...
@@ -1056,7 +1058,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
}
void
_startCursorTimer
()
{
_
showCursor
.
value
=
true
;
_
targetCursorVisibility
=
true
;
_cursorBlinkOpacityController
.
value
=
1.0
;
if
(
EditableText
.
debugDeterministicCursor
)
return
;
...
...
@@ -1070,7 +1072,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
void
_stopCursorTimer
({
bool
resetCharTicks
=
true
})
{
_cursorTimer
?.
cancel
();
_cursorTimer
=
null
;
_
showCursor
.
value
=
false
;
_
targetCursorVisibility
=
false
;
_cursorBlinkOpacityController
.
value
=
0.0
;
if
(
EditableText
.
debugDeterministicCursor
)
return
;
...
...
@@ -1196,7 +1198,9 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
value:
_value
,
cursorColor:
_cursorColor
,
backgroundCursorColor:
widget
.
backgroundCursorColor
,
showCursor:
EditableText
.
debugDeterministicCursor
?
ValueNotifier
<
bool
>(
true
)
:
_showCursor
,
showCursor:
EditableText
.
debugDeterministicCursor
?
ValueNotifier
<
bool
>(
true
)
:
_cursorVisibilityNotifier
,
hasFocus:
_hasFocus
,
maxLines:
widget
.
maxLines
,
selectionColor:
widget
.
selectionColor
,
...
...
packages/flutter/test/material/text_field_test.dart
View file @
32b9c2f0
...
...
@@ -125,12 +125,12 @@ void main() {
SystemChannels
.
platform
.
setMockMethodCallHandler
(
mockClipboard
.
handleMethodCall
);
const
String
kThreeLines
=
'First line of text is
'
'Second line goes until
'
'Third line of stuff
'
;
'First line of text is
\n
'
'Second line goes until
\n
'
'Third line of stuff'
;
const
String
kMoreThanFourLines
=
kThreeLines
+
'Fourth line won
\'
t display and ends at'
;
'
\n
Fourth line won
\'
t display and ends at'
;
// Returns the first RenderEditable.
RenderEditable
findRenderEditable
(
WidgetTester
tester
)
{
...
...
@@ -903,7 +903,7 @@ void main() {
);
const
String
testValue
=
kThreeLines
;
const
String
cutValue
=
'First line of stuff
'
;
const
String
cutValue
=
'First line of stuff'
;
await
tester
.
enterText
(
find
.
byType
(
TextField
),
testValue
);
await
skipPastScrollingAnimation
(
tester
);
...
...
@@ -973,7 +973,9 @@ void main() {
testWidgets
(
'Can scroll multiline input'
,
(
WidgetTester
tester
)
async
{
final
Key
textFieldKey
=
UniqueKey
();
final
TextEditingController
controller
=
TextEditingController
();
final
TextEditingController
controller
=
TextEditingController
(
text:
kMoreThanFourLines
,
);
await
tester
.
pumpWidget
(
overlay
(
...
...
@@ -986,12 +988,6 @@ void main() {
),
),
);
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
await
tester
.
enterText
(
find
.
byType
(
TextField
),
kMoreThanFourLines
);
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
RenderBox
findInputBox
()
=>
tester
.
renderObject
(
find
.
byKey
(
textFieldKey
));
final
RenderBox
inputBox
=
findInputBox
();
...
...
@@ -1016,6 +1012,7 @@ void main() {
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
await
gesture
.
up
();
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
// Now the first line is scrolled up, and the fourth line is visible.
Offset
newFirstPos
=
textOffsetToPosition
(
tester
,
kMoreThanFourLines
.
indexOf
(
'First'
));
...
...
@@ -1026,15 +1023,21 @@ void main() {
expect
(
inputBox
.
hitTest
(
HitTestResult
(),
position:
inputBox
.
globalToLocal
(
newFourthPos
)),
isTrue
);
// Now try scrolling by dragging the selection handle.
// Long press the 'i' in 'Fourth line' to select the word.
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
final
Offset
untilPos
=
textOffsetToPosition
(
tester
,
kMoreThanFourLines
.
indexOf
(
'Fourth line'
)+
8
);
gesture
=
await
tester
.
startGesture
(
untilPos
,
pointer:
7
);
final
Offset
selectedWordPos
=
textOffsetToPosition
(
tester
,
kMoreThanFourLines
.
indexOf
(
'Fourth line'
)
+
8
,
);
gesture
=
await
tester
.
startGesture
(
selectedWordPos
,
pointer:
7
);
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
await
gesture
.
up
();
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
controller
.
selection
.
base
.
offset
,
91
);
expect
(
controller
.
selection
.
extent
.
offset
,
94
);
final
RenderEditable
renderEditable
=
findRenderEditable
(
tester
);
final
List
<
TextSelectionPoint
>
endpoints
=
globalize
(
renderEditable
.
getEndpointsForSelection
(
controller
.
selection
),
...
...
@@ -1043,7 +1046,7 @@ void main() {
expect
(
endpoints
.
length
,
2
);
// Drag the left handle to the first line, just after 'First'.
final
Offset
handlePos
=
endpoints
[
0
].
point
+
const
Offset
(-
1
.0
,
1.0
);
final
Offset
handlePos
=
endpoints
[
0
].
point
+
const
Offset
(-
1
,
1
);
final
Offset
newHandlePos
=
textOffsetToPosition
(
tester
,
kMoreThanFourLines
.
indexOf
(
'First'
)
+
5
);
gesture
=
await
tester
.
startGesture
(
handlePos
,
pointer:
7
);
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
...
...
@@ -1059,9 +1062,7 @@ void main() {
expect
(
newFirstPos
.
dy
,
firstPos
.
dy
);
expect
(
inputBox
.
hitTest
(
HitTestResult
(),
position:
inputBox
.
globalToLocal
(
newFirstPos
)),
isTrue
);
expect
(
inputBox
.
hitTest
(
HitTestResult
(),
position:
inputBox
.
globalToLocal
(
newFourthPos
)),
isFalse
);
},
// This test fails on some Mac environments when libtxt is enabled.
skip:
Platform
.
isMacOS
);
});
testWidgets
(
'TextField smoke test'
,
(
WidgetTester
tester
)
async
{
String
textFieldValue
;
...
...
packages/flutter/test/rendering/editable_test.dart
View file @
32b9c2f0
...
...
@@ -10,6 +10,7 @@ import 'package:flutter/foundation.dart';
import
'../rendering/mock_canvas.dart'
;
import
'../rendering/recording_canvas.dart'
;
import
'rendering_tester.dart'
;
class
FakeEditableTextState
extends
TextSelectionDelegate
{
@override
...
...
@@ -26,10 +27,6 @@ class FakeEditableTextState extends TextSelectionDelegate {
}
void
main
(
)
{
final
TextEditingController
controller
=
TextEditingController
();
const
TextStyle
textStyle
=
TextStyle
();
test
(
'editable intrinsics'
,
()
{
final
TextSelectionDelegate
delegate
=
FakeEditableTextState
();
final
RenderEditable
editable
=
RenderEditable
(
...
...
@@ -99,86 +96,76 @@ void main() {
);
});
RenderEditable
findRenderEditable
(
WidgetTester
tester
)
{
final
RenderObject
root
=
tester
.
renderObject
(
find
.
byType
(
EditableText
));
expect
(
root
,
isNotNull
);
RenderEditable
renderEditable
;
void
recursiveFinder
(
RenderObject
child
)
{
if
(
child
is
RenderEditable
)
{
renderEditable
=
child
;
return
;
}
child
.
visitChildren
(
recursiveFinder
);
}
root
.
visitChildren
(
recursiveFinder
);
expect
(
renderEditable
,
isNotNull
);
return
renderEditable
;
}
testWidgets
(
'Floating cursor is painted'
,
(
WidgetTester
tester
)
async
{
debugDefaultTargetPlatformOverride
=
TargetPlatform
.
iOS
;
const
String
text
=
'hello world this is fun and cool and awesome!'
;
controller
.
text
=
text
;
final
FocusNode
focusNode
=
FocusNode
();
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Padding
(
padding:
const
EdgeInsets
.
only
(
top:
0.25
),
child:
Material
(
child:
TextField
(
controller:
controller
,
focusNode:
focusNode
,
style:
textStyle
,
),
test
(
'Can change cursor color, radius, visibility'
,
()
{
final
TextSelectionDelegate
delegate
=
FakeEditableTextState
();
final
ValueNotifier
<
bool
>
showCursor
=
ValueNotifier
<
bool
>(
true
);
EditableText
.
debugDeterministicCursor
=
true
;
final
RenderEditable
editable
=
RenderEditable
(
backgroundCursorColor:
Colors
.
grey
,
textDirection:
TextDirection
.
ltr
,
cursorColor:
const
Color
.
fromARGB
(
0xFF
,
0xFF
,
0x00
,
0x00
),
offset:
ViewportOffset
.
zero
(),
textSelectionDelegate:
delegate
,
text:
const
TextSpan
(
text:
'test'
,
style:
TextStyle
(
height:
1.0
,
fontSize:
10.0
,
fontFamily:
'Ahem'
,
),
),
selection:
const
TextSelection
.
collapsed
(
offset:
4
,
affinity:
TextAffinity
.
upstream
,
),
);
await
tester
.
tap
(
find
.
byType
(
EditableText
));
final
RenderEditable
editable
=
findRenderEditable
(
tester
);
editable
.
selection
=
const
TextSelection
(
baseOffset:
29
,
extentOffset:
29
);
final
EditableTextState
editableTextState
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Start
));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(
20
,
20
)));
await
tester
.
pump
();
expect
(
editable
,
paints
..
rrect
(
rrect:
RRect
.
fromRectAndRadius
(
Rect
.
fromLTRB
(
464.6666564941406
,
-
1.5833333730697632
,
466.6666564941406
,
16.41666603088379
),
const
Radius
.
circular
(
2.0
)),
color:
const
Color
(
0xff8e8e93
))
..
rrect
(
rrect:
RRect
.
fromRectAndRadius
(
Rect
.
fromLTRB
(
465.1666564941406
,
-
2.416666269302368
,
468.1666564941406
,
17.58333396911621
),
const
Radius
.
circular
(
1.0
)),
color:
const
Color
(
0xbf2196f3
))
);
layout
(
editable
);
// Moves the cursor right a few characters.
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(-
250
,
20
)));
expect
(
find
.
byType
(
EditableText
),
paints
..
rrect
(
rrect:
RRect
.
fromRectAndRadius
(
Rect
.
fromLTRB
(
192.6666717529297
,
-
1.5833333730697632
,
194.6666717529297
,
16.41666603088379
),
const
Radius
.
circular
(
2.0
)),
color:
const
Color
(
0xff8e8e93
))
..
rrect
(
rrect:
RRect
.
fromRectAndRadius
(
Rect
.
fromLTRB
(
195.16665649414062
,
-
2.416666269302368
,
198.16665649414062
,
17.58333396911621
),
const
Radius
.
circular
(
1.0
)),
color:
const
Color
(
0xbf2196f3
))
editable
.
layout
(
BoxConstraints
.
loose
(
const
Size
(
100
,
100
)));
expect
(
editable
,
// Draw no cursor by default.
paintsExactlyCountTimes
(
#drawRect
,
0
),
);
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
End
));
editable
.
showCursor
=
showCursor
;
pumpFrame
();
expect
(
editable
,
paints
..
rect
(
color:
const
Color
.
fromARGB
(
0xFF
,
0xFF
,
0x00
,
0x00
),
rect:
Rect
.
fromLTWH
(
40
,
2
,
1
,
6
),
));
// Now change to a rounded caret.
editable
.
cursorColor
=
const
Color
.
fromARGB
(
0xFF
,
0x00
,
0x00
,
0xFF
);
editable
.
cursorWidth
=
4
;
editable
.
cursorRadius
=
const
Radius
.
circular
(
3
);
pumpFrame
();
expect
(
editable
,
paints
..
rrect
(
color:
const
Color
.
fromARGB
(
0xFF
,
0x00
,
0x00
,
0xFF
),
rrect:
RRect
.
fromRectAndRadius
(
Rect
.
fromLTWH
(
40
,
2
,
4
,
6
),
const
Radius
.
circular
(
3
),
),
));
editable
.
textScaleFactor
=
2
;
pumpFrame
();
// Now the caret height is much bigger due to the bigger font scale.
expect
(
editable
,
paints
..
rrect
(
color:
const
Color
.
fromARGB
(
0xFF
,
0x00
,
0x00
,
0xFF
),
rrect:
RRect
.
fromRectAndRadius
(
Rect
.
fromLTWH
(
80
,
2
,
4
,
16
),
const
Radius
.
circular
(
3
),
),
));
await
tester
.
pumpAndSettle
();
// Can turn off caret.
showCursor
.
value
=
false
;
pumpFrame
();
debugDefaultTargetPlatformOverride
=
null
;
expect
(
editable
,
paintsExactlyCountTimes
(
#drawRRect
,
0
))
;
});
}
packages/flutter/test/widgets/editable_text_cursor_test.dart
0 → 100644
View file @
32b9c2f0
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:io'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter/services.dart'
;
import
'../rendering/mock_canvas.dart'
;
import
'editable_text_test.dart'
;
void
main
(
)
{
testWidgets
(
'cursor has expected width and radius'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
MediaQuery
(
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
controller:
controller
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
cursorWidth:
10.0
,
cursorRadius:
const
Radius
.
circular
(
2.0
),
))));
final
EditableText
editableText
=
tester
.
firstWidget
(
find
.
byType
(
EditableText
));
expect
(
editableText
.
cursorWidth
,
10.0
);
expect
(
editableText
.
cursorRadius
.
x
,
2.0
);
});
testWidgets
(
'cursor layout has correct width'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
EditableTextState
>
editableTextKey
=
GlobalKey
<
EditableTextState
>();
String
changedValue
;
final
Widget
widget
=
MaterialApp
(
home:
RepaintBoundary
(
key:
const
ValueKey
<
int
>(
1
),
child:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
key:
editableTextKey
,
controller:
TextEditingController
(),
focusNode:
FocusNode
(),
style:
Typography
(
platform:
TargetPlatform
.
android
).
black
.
subhead
,
cursorColor:
Colors
.
blue
,
selectionControls:
materialTextSelectionControls
,
keyboardType:
TextInputType
.
text
,
onChanged:
(
String
value
)
{
changedValue
=
value
;
},
cursorWidth:
15.0
,
),
),
);
await
tester
.
pumpWidget
(
widget
);
// Populate a fake clipboard.
const
String
clipboardContent
=
' '
;
SystemChannels
.
platform
.
setMockMethodCallHandler
((
MethodCall
methodCall
)
async
{
if
(
methodCall
.
method
==
'Clipboard.getData'
)
return
const
<
String
,
dynamic
>{
'text'
:
clipboardContent
};
return
null
;
});
// Long-press to bring up the text editing controls.
final
Finder
textFinder
=
find
.
byKey
(
editableTextKey
);
await
tester
.
longPress
(
textFinder
);
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
await
tester
.
tap
(
find
.
text
(
'PASTE'
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
changedValue
,
clipboardContent
);
await
expectLater
(
find
.
byKey
(
const
ValueKey
<
int
>(
1
)),
matchesGoldenFile
(
'editable_text_test.0.0.png'
),
);
},
skip:
!
Platform
.
isLinux
);
testWidgets
(
'cursor layout has correct radius'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
EditableTextState
>
editableTextKey
=
GlobalKey
<
EditableTextState
>();
String
changedValue
;
final
Widget
widget
=
MaterialApp
(
home:
RepaintBoundary
(
key:
const
ValueKey
<
int
>(
1
),
child:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
key:
editableTextKey
,
controller:
TextEditingController
(),
focusNode:
FocusNode
(),
style:
Typography
(
platform:
TargetPlatform
.
android
).
black
.
subhead
,
cursorColor:
Colors
.
blue
,
selectionControls:
materialTextSelectionControls
,
keyboardType:
TextInputType
.
text
,
onChanged:
(
String
value
)
{
changedValue
=
value
;
},
cursorWidth:
15.0
,
cursorRadius:
const
Radius
.
circular
(
3.0
),
),
),
);
await
tester
.
pumpWidget
(
widget
);
// Populate a fake clipboard.
const
String
clipboardContent
=
' '
;
SystemChannels
.
platform
.
setMockMethodCallHandler
((
MethodCall
methodCall
)
async
{
if
(
methodCall
.
method
==
'Clipboard.getData'
)
return
const
<
String
,
dynamic
>{
'text'
:
clipboardContent
};
return
null
;
});
// Long-press to bring up the text editing controls.
final
Finder
textFinder
=
find
.
byKey
(
editableTextKey
);
await
tester
.
longPress
(
textFinder
);
await
tester
.
pump
();
await
tester
.
tap
(
find
.
text
(
'PASTE'
));
await
tester
.
pump
();
expect
(
changedValue
,
clipboardContent
);
await
expectLater
(
find
.
byKey
(
const
ValueKey
<
int
>(
1
)),
matchesGoldenFile
(
'editable_text_test.1.0.png'
),
);
},
skip:
!
Platform
.
isLinux
);
testWidgets
(
'Cursor animates on iOS'
,
(
WidgetTester
tester
)
async
{
final
Widget
widget
=
MaterialApp
(
theme:
ThemeData
(
platform:
TargetPlatform
.
iOS
),
home:
const
Material
(
child:
TextField
(
maxLines:
3
,
),
),
);
await
tester
.
pumpWidget
(
widget
);
await
tester
.
tap
(
find
.
byType
(
TextField
));
await
tester
.
pump
();
final
EditableTextState
editableTextState
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
final
RenderEditable
renderEditable
=
editableTextState
.
renderEditable
;
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
// Trigger initial timer. When focusing the first time, the cursor shows
// for slightly longer than the average on time.
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
// Start timing standard cursor show period.
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
expect
(
renderEditable
,
paints
..
rrect
(
color:
const
Color
(
0xff2196f3
)));
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
// Start to animate the cursor away.
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
expect
(
renderEditable
,
paints
..
rrect
(
color:
const
Color
(
0xff2196f3
)));
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
110
);
expect
(
renderEditable
,
paints
..
rrect
(
color:
const
Color
(
0x6e2196f3
)));
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
16
);
expect
(
renderEditable
,
paints
..
rrect
(
color:
const
Color
(
0x102196f3
)));
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
0
);
// Don't try to draw the cursor.
expect
(
renderEditable
,
paintsExactlyCountTimes
(
#drawRRect
,
0
));
// Wait some more while the cursor is gone. It'll trigger the cursor to
// start animating in again.
await
tester
.
pump
(
const
Duration
(
milliseconds:
300
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
0
);
expect
(
renderEditable
,
paintsExactlyCountTimes
(
#drawRRect
,
0
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
50
));
// Cursor starts coming back.
expect
(
renderEditable
.
cursorColor
.
alpha
,
79
);
expect
(
renderEditable
,
paints
..
rrect
(
color:
const
Color
(
0x4f2196f3
)));
});
testWidgets
(
'Cursor does not animate on Android'
,
(
WidgetTester
tester
)
async
{
const
Widget
widget
=
MaterialApp
(
home:
Material
(
child:
TextField
(
maxLines:
3
,
),
),
);
await
tester
.
pumpWidget
(
widget
);
await
tester
.
tap
(
find
.
byType
(
TextField
));
await
tester
.
pump
();
final
EditableTextState
editableTextState
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
final
RenderEditable
renderEditable
=
editableTextState
.
renderEditable
;
await
tester
.
pump
();
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
expect
(
renderEditable
,
paints
..
rect
(
color:
const
Color
(
0xff4285f4
)));
// Android cursor goes from exactly on to exactly off on the 500ms dot.
await
tester
.
pump
(
const
Duration
(
milliseconds:
499
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
expect
(
renderEditable
,
paints
..
rect
(
color:
const
Color
(
0xff4285f4
)));
await
tester
.
pump
(
const
Duration
(
milliseconds:
1
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
0
);
// Don't try to draw the cursor.
expect
(
renderEditable
,
paintsExactlyCountTimes
(
#drawRect
,
0
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
expect
(
renderEditable
,
paints
..
rect
(
color:
const
Color
(
0xff4285f4
)));
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
0
);
expect
(
renderEditable
,
paintsExactlyCountTimes
(
#drawRect
,
0
));
});
testWidgets
(
'Cursor does not animates on iOS when debugDeterministicCursor is set'
,
(
WidgetTester
tester
)
async
{
EditableText
.
debugDeterministicCursor
=
true
;
final
Widget
widget
=
MaterialApp
(
theme:
ThemeData
(
platform:
TargetPlatform
.
iOS
),
home:
const
Material
(
child:
TextField
(
maxLines:
3
,
),
),
);
await
tester
.
pumpWidget
(
widget
);
await
tester
.
tap
(
find
.
byType
(
TextField
));
await
tester
.
pump
();
final
EditableTextState
editableTextState
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
final
RenderEditable
renderEditable
=
editableTextState
.
renderEditable
;
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
expect
(
renderEditable
,
paints
..
rrect
(
color:
const
Color
(
0xff2196f3
)));
// Cursor draw never changes.
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
expect
(
renderEditable
,
paints
..
rrect
(
color:
const
Color
(
0xff2196f3
)));
// No more transient calls.
await
tester
.
pumpAndSettle
();
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
expect
(
renderEditable
,
paints
..
rrect
(
color:
const
Color
(
0xff2196f3
)));
EditableText
.
debugDeterministicCursor
=
false
;
});
testWidgets
(
'Cursor does not animate on Android when debugDeterministicCursor is set'
,
(
WidgetTester
tester
)
async
{
EditableText
.
debugDeterministicCursor
=
true
;
const
Widget
widget
=
MaterialApp
(
home:
Material
(
child:
TextField
(
maxLines:
3
,
),
),
);
await
tester
.
pumpWidget
(
widget
);
await
tester
.
tap
(
find
.
byType
(
TextField
));
await
tester
.
pump
();
final
EditableTextState
editableTextState
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
final
RenderEditable
renderEditable
=
editableTextState
.
renderEditable
;
await
tester
.
pump
();
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
expect
(
renderEditable
,
paints
..
rect
(
color:
const
Color
(
0xff4285f4
)));
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
expect
(
renderEditable
,
paints
..
rect
(
color:
const
Color
(
0xff4285f4
)));
// Cursor draw never changes.
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
expect
(
renderEditable
,
paints
..
rect
(
color:
const
Color
(
0xff4285f4
)));
// No more transient calls.
await
tester
.
pumpAndSettle
();
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
expect
(
renderEditable
,
paints
..
rect
(
color:
const
Color
(
0xff4285f4
)));
EditableText
.
debugDeterministicCursor
=
false
;
});
testWidgets
(
'Cursor radius is 2.0 on iOS'
,
(
WidgetTester
tester
)
async
{
final
Widget
widget
=
MaterialApp
(
theme:
ThemeData
(
platform:
TargetPlatform
.
iOS
),
home:
const
Material
(
child:
TextField
(
maxLines:
3
,
),
),
);
await
tester
.
pumpWidget
(
widget
);
final
EditableTextState
editableTextState
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
final
RenderEditable
renderEditable
=
editableTextState
.
renderEditable
;
expect
(
renderEditable
.
cursorRadius
,
const
Radius
.
circular
(
2.0
));
});
testWidgets
(
'Cursor gets placed correctly after going out of bounds'
,
(
WidgetTester
tester
)
async
{
const
String
text
=
'hello world this is fun and cool and awesome!'
;
controller
.
text
=
text
;
final
FocusNode
focusNode
=
FocusNode
();
await
tester
.
pumpWidget
(
MediaQuery
(
data:
const
MediaQueryData
(
devicePixelRatio:
1
),
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
FocusScope
(
node:
focusScopeNode
,
autofocus:
true
,
child:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
controller:
controller
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
);
await
tester
.
tap
(
find
.
byType
(
EditableText
));
final
RenderEditable
renderEditable
=
findRenderEditable
(
tester
);
renderEditable
.
selection
=
const
TextSelection
(
baseOffset:
29
,
extentOffset:
29
);
expect
(
controller
.
selection
.
baseOffset
,
29
);
final
EditableTextState
editableTextState
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Start
));
expect
(
controller
.
selection
.
baseOffset
,
29
);
// Sets the origin.
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(
20
,
20
)));
expect
(
controller
.
selection
.
baseOffset
,
29
);
// Moves the cursor super far right
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(
2090
,
20
)));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(
2100
,
20
)));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(
2090
,
20
)));
// After peaking the cursor, we move in the opposite direction.
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(
1400
,
20
)));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
End
));
await
tester
.
pumpAndSettle
();
// The cursor has been set.
expect
(
controller
.
selection
.
baseOffset
,
8
);
// Go in the other direction.
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Start
));
// Sets the origin.
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(
20
,
20
)));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(-
5000
,
20
)));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(-
5010
,
20
)));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(-
5000
,
20
)));
// Move back in the opposite direction only a few hundred.
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(-
4850
,
20
)));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
End
));
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
baseOffset
,
10
);
});
testWidgets
(
'Updating the floating cursor correctly moves the cursor'
,
(
WidgetTester
tester
)
async
{
const
String
text
=
'hello world this is fun and cool and awesome!'
;
controller
.
text
=
text
;
final
FocusNode
focusNode
=
FocusNode
();
await
tester
.
pumpWidget
(
MediaQuery
(
data:
const
MediaQueryData
(
devicePixelRatio:
1
),
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
FocusScope
(
node:
focusScopeNode
,
autofocus:
true
,
child:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
controller:
controller
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
);
await
tester
.
tap
(
find
.
byType
(
EditableText
));
final
RenderEditable
renderEditable
=
findRenderEditable
(
tester
);
renderEditable
.
selection
=
const
TextSelection
(
baseOffset:
29
,
extentOffset:
29
);
expect
(
controller
.
selection
.
baseOffset
,
29
);
final
EditableTextState
editableTextState
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Start
));
expect
(
controller
.
selection
.
baseOffset
,
29
);
// Sets the origin.
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(
20
,
20
)));
expect
(
controller
.
selection
.
baseOffset
,
29
);
// Moves the cursor right a few characters.
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(-
250
,
20
)));
// But we have not yet set the offset because the user is not done placing the cursor.
expect
(
controller
.
selection
.
baseOffset
,
29
);
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
End
));
await
tester
.
pumpAndSettle
();
// The cursor has been set.
expect
(
controller
.
selection
.
baseOffset
,
10
);
});
testWidgets
(
'autofocus sets cursor to the end of text'
,
(
WidgetTester
tester
)
async
{
const
String
text
=
'hello world'
;
final
FocusScopeNode
focusScopeNode
=
FocusScopeNode
();
final
FocusNode
focusNode
=
FocusNode
();
controller
.
text
=
text
;
await
tester
.
pumpWidget
(
MediaQuery
(
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
FocusScope
(
node:
focusScopeNode
,
autofocus:
true
,
child:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
controller:
controller
,
focusNode:
focusNode
,
autofocus:
true
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
);
expect
(
focusNode
.
hasFocus
,
true
);
expect
(
controller
.
selection
.
isCollapsed
,
true
);
expect
(
controller
.
selection
.
baseOffset
,
text
.
length
);
});
testWidgets
(
'Floating cursor is painted'
,
(
WidgetTester
tester
)
async
{
const
String
text
=
'hello world this is fun and cool and awesome!'
;
controller
.
text
=
text
;
final
FocusNode
focusNode
=
FocusNode
();
await
tester
.
pumpWidget
(
MaterialApp
(
theme:
ThemeData
(
platform:
TargetPlatform
.
iOS
),
home:
Padding
(
padding:
const
EdgeInsets
.
only
(
top:
0.25
),
child:
Material
(
child:
TextField
(
controller:
controller
,
focusNode:
focusNode
,
style:
textStyle
,
),
),
),
),
);
await
tester
.
tap
(
find
.
byType
(
EditableText
));
final
RenderEditable
editable
=
findRenderEditable
(
tester
);
editable
.
selection
=
const
TextSelection
(
baseOffset:
29
,
extentOffset:
29
);
final
EditableTextState
editableTextState
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Start
),
);
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(
20
,
20
),
),
);
await
tester
.
pump
();
expect
(
editable
,
paints
..
rrect
(
rrect:
RRect
.
fromRectAndRadius
(
Rect
.
fromLTRB
(
464.6666564941406
,
2.0833332538604736
,
466.6666564941406
,
14.083333015441895
),
const
Radius
.
circular
(
2.0
),
),
color:
const
Color
(
0xff8e8e93
))
..
rrect
(
rrect:
RRect
.
fromRectAndRadius
(
Rect
.
fromLTRB
(
465.1666564941406
,
1.0833336114883423
,
468.1666564941406
,
15.083333969116211
),
const
Radius
.
circular
(
1.0
),
),
color:
const
Color
(
0xbf2196f3
))
);
// Moves the cursor right a few characters.
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(-
250
,
20
)));
expect
(
find
.
byType
(
EditableText
),
paints
..
rrect
(
rrect:
RRect
.
fromRectAndRadius
(
Rect
.
fromLTRB
(
192.6666717529297
,
2.0833332538604736
,
194.6666717529297
,
14.083333015441895
),
const
Radius
.
circular
(
2.0
),
),
color:
const
Color
(
0xff8e8e93
))
..
rrect
(
rrect:
RRect
.
fromRectAndRadius
(
Rect
.
fromLTRB
(
195.16665649414062
,
1.0833336114883423
,
198.16665649414062
,
15.083333969116211
),
const
Radius
.
circular
(
1.0
),
),
color:
const
Color
(
0xbf2196f3
))
);
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
End
));
await
tester
.
pumpAndSettle
();
});
}
packages/flutter/test/widgets/editable_text_test.dart
View file @
32b9c2f0
...
...
@@ -3,7 +3,6 @@
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:io'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
...
...
@@ -15,13 +14,30 @@ import 'package:flutter/foundation.dart';
import
'semantics_tester.dart'
;
void
main
(
)
{
final
TextEditingController
controller
=
TextEditingController
();
final
FocusNode
focusNode
=
FocusNode
();
final
FocusScopeNode
focusScopeNode
=
FocusScopeNode
();
const
TextStyle
textStyle
=
TextStyle
();
const
Color
cursorColor
=
Color
.
fromARGB
(
0xFF
,
0xFF
,
0x00
,
0x00
);
RenderEditable
findRenderEditable
(
WidgetTester
tester
)
{
final
RenderObject
root
=
tester
.
renderObject
(
find
.
byType
(
EditableText
));
expect
(
root
,
isNotNull
);
RenderEditable
renderEditable
;
void
recursiveFinder
(
RenderObject
child
)
{
if
(
child
is
RenderEditable
)
{
renderEditable
=
child
;
return
;
}
child
.
visitChildren
(
recursiveFinder
);
}
root
.
visitChildren
(
recursiveFinder
);
expect
(
renderEditable
,
isNotNull
);
return
renderEditable
;
}
final
TextEditingController
controller
=
TextEditingController
();
final
FocusNode
focusNode
=
FocusNode
();
final
FocusScopeNode
focusScopeNode
=
FocusScopeNode
();
const
TextStyle
textStyle
=
TextStyle
();
const
Color
cursorColor
=
Color
.
fromARGB
(
0xFF
,
0xFF
,
0x00
,
0x00
);
void
main
(
)
{
setUp
(()
{
debugResetSemanticsIdCounter
();
});
...
...
@@ -88,31 +104,10 @@ void main() {
expect
(
editableText
.
maxLines
,
equals
(
1
));
expect
(
editableText
.
obscureText
,
isFalse
);
expect
(
editableText
.
autocorrect
,
isTrue
);
expect
(
editableText
.
textAlign
,
TextAlign
.
start
);
expect
(
editableText
.
cursorWidth
,
2.0
);
});
testWidgets
(
'cursor has expected width and radius'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
MediaQuery
(
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
controller:
controller
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
cursorWidth:
10.0
,
cursorRadius:
const
Radius
.
circular
(
2.0
),
))));
final
EditableText
editableText
=
tester
.
firstWidget
(
find
.
byType
(
EditableText
));
expect
(
editableText
.
cursorWidth
,
10.0
);
expect
(
editableText
.
cursorRadius
.
x
,
2.0
);
});
testWidgets
(
'text keyboard is requested when maxLines is default'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
...
...
@@ -526,113 +521,6 @@ void main() {
expect
(
changedValue
,
clipboardContent
);
});
testWidgets
(
'cursor layout has correct width'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
EditableTextState
>
editableTextKey
=
GlobalKey
<
EditableTextState
>();
String
changedValue
;
final
Widget
widget
=
MaterialApp
(
home:
RepaintBoundary
(
key:
const
ValueKey
<
int
>(
1
),
child:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
key:
editableTextKey
,
controller:
TextEditingController
(),
focusNode:
FocusNode
(),
style:
Typography
(
platform:
TargetPlatform
.
android
).
black
.
subhead
,
cursorColor:
Colors
.
blue
,
selectionControls:
materialTextSelectionControls
,
keyboardType:
TextInputType
.
text
,
onChanged:
(
String
value
)
{
changedValue
=
value
;
},
cursorWidth:
15.0
,
),
),
);
await
tester
.
pumpWidget
(
widget
);
// Populate a fake clipboard.
const
String
clipboardContent
=
' '
;
SystemChannels
.
platform
.
setMockMethodCallHandler
((
MethodCall
methodCall
)
async
{
if
(
methodCall
.
method
==
'Clipboard.getData'
)
return
const
<
String
,
dynamic
>{
'text'
:
clipboardContent
};
return
null
;
});
// Long-press to bring up the text editing controls.
final
Finder
textFinder
=
find
.
byKey
(
editableTextKey
);
await
tester
.
longPress
(
textFinder
);
await
tester
.
pump
();
await
tester
.
tap
(
find
.
text
(
'PASTE'
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
600
));
expect
(
changedValue
,
clipboardContent
);
await
expectLater
(
find
.
byKey
(
const
ValueKey
<
int
>(
1
)),
matchesGoldenFile
(
'editable_text_test.0.0.png'
),
);
},
skip:
!
Platform
.
isLinux
);
testWidgets
(
'cursor layout has correct radius'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
EditableTextState
>
editableTextKey
=
GlobalKey
<
EditableTextState
>();
String
changedValue
;
final
Widget
widget
=
MaterialApp
(
home:
RepaintBoundary
(
key:
const
ValueKey
<
int
>(
1
),
child:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
key:
editableTextKey
,
controller:
TextEditingController
(),
focusNode:
FocusNode
(),
style:
Typography
(
platform:
TargetPlatform
.
android
).
black
.
subhead
,
cursorColor:
Colors
.
blue
,
selectionControls:
materialTextSelectionControls
,
keyboardType:
TextInputType
.
text
,
onChanged:
(
String
value
)
{
changedValue
=
value
;
},
cursorWidth:
15.0
,
cursorRadius:
const
Radius
.
circular
(
3.0
),
),
),
);
await
tester
.
pumpWidget
(
widget
);
// Populate a fake clipboard.
const
String
clipboardContent
=
' '
;
SystemChannels
.
platform
.
setMockMethodCallHandler
((
MethodCall
methodCall
)
async
{
if
(
methodCall
.
method
==
'Clipboard.getData'
)
return
const
<
String
,
dynamic
>{
'text'
:
clipboardContent
};
return
null
;
});
// Long-press to bring up the text editing controls.
final
Finder
textFinder
=
find
.
byKey
(
editableTextKey
);
await
tester
.
longPress
(
textFinder
);
await
tester
.
pump
();
await
tester
.
tap
(
find
.
text
(
'PASTE'
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
600
));
expect
(
changedValue
,
clipboardContent
);
await
expectLater
(
find
.
byKey
(
const
ValueKey
<
int
>(
1
)),
matchesGoldenFile
(
'editable_text_test.1.0.png'
),
);
},
skip:
!
Platform
.
isLinux
);
testWidgets
(
'Does not lose focus by default when "next" action is pressed'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
EditableTextState
>
editableTextKey
=
...
...
@@ -841,119 +729,7 @@ void main() {
// and onSubmission callbacks.
});
testWidgets
(
'Cursor animates on iOS'
,
(
WidgetTester
tester
)
async
{
debugDefaultTargetPlatformOverride
=
TargetPlatform
.
iOS
;
const
Widget
widget
=
MaterialApp
(
home:
Material
(
child:
TextField
(
maxLines:
3
,
)
),
);
await
tester
.
pumpWidget
(
widget
);
await
tester
.
tap
(
find
.
byType
(
TextField
));
await
tester
.
pump
();
final
EditableTextState
editableTextState
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
final
RenderEditable
renderEditable
=
editableTextState
.
renderEditable
;
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
110
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
16
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
0
);
debugDefaultTargetPlatformOverride
=
null
;
});
testWidgets
(
'Cursor does not animate on Android'
,
(
WidgetTester
tester
)
async
{
debugDefaultTargetPlatformOverride
=
TargetPlatform
.
android
;
const
Widget
widget
=
MaterialApp
(
home:
Material
(
child:
TextField
(
maxLines:
3
,
)
),
);
await
tester
.
pumpWidget
(
widget
);
await
tester
.
tap
(
find
.
byType
(
TextField
));
await
tester
.
pump
();
final
EditableTextState
editableTextState
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
final
RenderEditable
renderEditable
=
editableTextState
.
renderEditable
;
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
0
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
0
);
debugDefaultTargetPlatformOverride
=
null
;
});
testWidgets
(
'Cursor radius is 2.0 on iOS'
,
(
WidgetTester
tester
)
async
{
debugDefaultTargetPlatformOverride
=
TargetPlatform
.
iOS
;
const
Widget
widget
=
MaterialApp
(
home:
Material
(
child:
TextField
(
maxLines:
3
,
)
),
);
await
tester
.
pumpWidget
(
widget
);
final
EditableTextState
editableTextState
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
final
RenderEditable
renderEditable
=
editableTextState
.
renderEditable
;
expect
(
renderEditable
.
cursorRadius
,
const
Radius
.
circular
(
2.0
));
debugDefaultTargetPlatformOverride
=
null
;
});
testWidgets
(
testWidgets
(
'When "newline" action is called on a Editable text with maxLines != 1, onEditingComplete and onSubmitted callbacks are not invoked.'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
EditableTextState
>
editableTextKey
=
...
...
@@ -1067,7 +843,8 @@ testWidgets(
'composingBase'
:
-
1
,
'composingExtent'
:
-
1
,
},
));
),
);
});
testWidgets
(
'EditableText identifies as text field (w/ focus) in semantics'
,
...
...
@@ -1094,8 +871,7 @@ testWidgets(
),
);
expect
(
semantics
,
includesNodeWith
(
flags:
<
SemanticsFlag
>[
SemanticsFlag
.
isTextField
]));
expect
(
semantics
,
includesNodeWith
(
flags:
<
SemanticsFlag
>[
SemanticsFlag
.
isTextField
]));
await
tester
.
tap
(
find
.
byType
(
EditableText
));
await
tester
.
idle
();
...
...
@@ -1106,7 +882,8 @@ testWidgets(
includesNodeWith
(
flags:
<
SemanticsFlag
>[
SemanticsFlag
.
isTextField
,
SemanticsFlag
.
isFocused
]));
]),
);
semantics
.
dispose
();
});
...
...
@@ -1143,7 +920,8 @@ testWidgets(
includesNodeWith
(
flags:
<
SemanticsFlag
>[
SemanticsFlag
.
isTextField
],
value:
value1
,
));
),
);
const
String
value2
=
'Changed the EditableText content'
;
controller
.
text
=
value2
;
...
...
@@ -1155,13 +933,13 @@ testWidgets(
includesNodeWith
(
flags:
<
SemanticsFlag
>[
SemanticsFlag
.
isTextField
],
value:
value2
,
));
),
);
semantics
.
dispose
();
});
testWidgets
(
'changing selection with keyboard does not show handles'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'changing selection with keyboard does not show handles'
,
(
WidgetTester
tester
)
async
{
const
String
value1
=
'Hello World'
;
controller
.
text
=
value1
;
...
...
@@ -1191,7 +969,8 @@ testWidgets(
expect
(
textState
.
selectionOverlay
.
handlesAreVisible
,
isTrue
);
expect
(
textState
.
selectionOverlay
.
selectionDelegate
.
textEditingValue
.
selection
,
const
TextSelection
.
collapsed
(
offset:
4
));
const
TextSelection
.
collapsed
(
offset:
4
),
);
// Simulate selection change via keyboard and expect handles to disappear.
render
.
onSelectionChanged
(
const
TextSelection
.
collapsed
(
offset:
10
),
render
,
...
...
@@ -1201,7 +980,8 @@ testWidgets(
expect
(
textState
.
selectionOverlay
.
handlesAreVisible
,
isFalse
);
expect
(
textState
.
selectionOverlay
.
selectionDelegate
.
textEditingValue
.
selection
,
const
TextSelection
.
collapsed
(
offset:
10
));
const
TextSelection
.
collapsed
(
offset:
10
),
);
});
testWidgets
(
'exposes correct cursor movement semantics'
,
...
...
@@ -1224,7 +1004,8 @@ testWidgets(
semantics
,
includesNodeWith
(
value:
'test'
,
));
),
);
controller
.
selection
=
TextSelection
.
collapsed
(
offset:
controller
.
text
.
length
);
...
...
@@ -1240,7 +1021,8 @@ testWidgets(
SemanticsAction
.
moveCursorBackwardByWord
,
SemanticsAction
.
setSelection
,
],
));
),
);
controller
.
selection
=
TextSelection
.
collapsed
(
offset:
controller
.
text
.
length
-
2
);
...
...
@@ -1258,7 +1040,8 @@ testWidgets(
SemanticsAction
.
moveCursorForwardByWord
,
SemanticsAction
.
setSelection
,
],
));
),
);
controller
.
selection
=
const
TextSelection
.
collapsed
(
offset:
0
);
await
tester
.
pumpAndSettle
();
...
...
@@ -1273,7 +1056,8 @@ testWidgets(
SemanticsAction
.
moveCursorForwardByWord
,
SemanticsAction
.
setSelection
,
],
));
),
);
semantics
.
dispose
();
});
...
...
@@ -1304,7 +1088,8 @@ testWidgets(
SemanticsAction
.
moveCursorBackwardByCharacter
,
SemanticsAction
.
moveCursorBackwardByWord
,
],
));
),
);
final
RenderEditable
render
=
tester
.
allRenderObjects
.
firstWhere
((
RenderObject
o
)
=>
o
.
runtimeType
==
RenderEditable
);
...
...
@@ -1331,7 +1116,8 @@ testWidgets(
SemanticsAction
.
moveCursorForwardByWord
,
SemanticsAction
.
setSelection
,
],
));
),
);
tester
.
binding
.
pipelineOwner
.
semanticsOwner
.
performAction
(
semanticsId
,
SemanticsAction
.
moveCursorBackwardByCharacter
,
doNotExtendSelection
);
...
...
@@ -1356,7 +1142,8 @@ testWidgets(
SemanticsAction
.
moveCursorForwardByWord
,
SemanticsAction
.
setSelection
,
],
));
),
);
tester
.
binding
.
pipelineOwner
.
semanticsOwner
.
performAction
(
semanticsId
,
SemanticsAction
.
moveCursorForwardByCharacter
,
doNotExtendSelection
);
...
...
@@ -1394,7 +1181,8 @@ testWidgets(
SemanticsAction
.
moveCursorBackwardByCharacter
,
SemanticsAction
.
moveCursorBackwardByWord
,
],
));
),
);
final
RenderEditable
render
=
tester
.
allRenderObjects
.
firstWhere
((
RenderObject
o
)
=>
o
.
runtimeType
==
RenderEditable
);
...
...
@@ -1421,7 +1209,8 @@ testWidgets(
SemanticsAction
.
moveCursorForwardByWord
,
SemanticsAction
.
setSelection
,
],
));
),
);
tester
.
binding
.
pipelineOwner
.
semanticsOwner
.
performAction
(
semanticsId
,
SemanticsAction
.
moveCursorBackwardByWord
,
doNotExtendSelection
);
...
...
@@ -1447,7 +1236,8 @@ testWidgets(
SemanticsAction
.
moveCursorForwardByWord
,
SemanticsAction
.
setSelection
,
],
));
),
);
tester
.
binding
.
pipelineOwner
.
semanticsOwner
.
performAction
(
semanticsId
,
SemanticsAction
.
moveCursorForwardByWord
,
doNotExtendSelection
);
...
...
@@ -1494,7 +1284,8 @@ testWidgets(
SemanticsAction
.
moveCursorBackwardByCharacter
,
SemanticsAction
.
moveCursorBackwardByWord
,
],
));
),
);
final
RenderEditable
render
=
tester
.
allRenderObjects
.
firstWhere
((
RenderObject
o
)
=>
o
.
runtimeType
==
RenderEditable
);
...
...
@@ -1521,7 +1312,8 @@ testWidgets(
SemanticsAction
.
moveCursorForwardByWord
,
SemanticsAction
.
setSelection
,
],
));
),
);
tester
.
binding
.
pipelineOwner
.
semanticsOwner
.
performAction
(
semanticsId
,
SemanticsAction
.
moveCursorBackwardByCharacter
,
extendSelection
);
...
...
@@ -1546,7 +1338,8 @@ testWidgets(
SemanticsAction
.
moveCursorForwardByWord
,
SemanticsAction
.
setSelection
,
],
));
),
);
tester
.
binding
.
pipelineOwner
.
semanticsOwner
.
performAction
(
semanticsId
,
SemanticsAction
.
moveCursorForwardByCharacter
,
doNotExtendSelection
);
...
...
@@ -1593,7 +1386,8 @@ testWidgets(
SemanticsAction
.
moveCursorBackwardByCharacter
,
SemanticsAction
.
moveCursorBackwardByWord
,
],
));
),
);
final
RenderEditable
render
=
tester
.
allRenderObjects
.
firstWhere
((
RenderObject
o
)
=>
o
.
runtimeType
==
RenderEditable
);
...
...
@@ -1620,7 +1414,8 @@ testWidgets(
SemanticsAction
.
moveCursorForwardByWord
,
SemanticsAction
.
setSelection
,
],
));
),
);
tester
.
binding
.
pipelineOwner
.
semanticsOwner
.
performAction
(
semanticsId
,
SemanticsAction
.
moveCursorBackwardByWord
,
extendSelection
);
...
...
@@ -1646,7 +1441,8 @@ testWidgets(
SemanticsAction
.
moveCursorForwardByWord
,
SemanticsAction
.
setSelection
,
],
));
),
);
tester
.
binding
.
pipelineOwner
.
semanticsOwner
.
performAction
(
semanticsId
,
SemanticsAction
.
moveCursorForwardByWord
,
doNotExtendSelection
);
...
...
@@ -1710,7 +1506,105 @@ testWidgets(
),
ignoreTransform:
true
,
ignoreRect:
true
,
ignoreId:
true
));
ignoreId:
true
,
),
);
semantics
.
dispose
();
});
testWidgets
(
'password fields become obscured with the right semantics when set'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semantics
=
SemanticsTester
(
tester
);
const
String
originalText
=
'super-secret-password!!1'
;
controller
.
text
=
originalText
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
controller:
controller
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
));
const
String
expectedValue
=
'••••••••••••••••••••••••'
;
expect
(
semantics
,
hasSemantics
(
TestSemantics
(
children:
<
TestSemantics
>[
TestSemantics
.
rootChild
(
children:
<
TestSemantics
>[
TestSemantics
(
flags:
<
SemanticsFlag
>[
SemanticsFlag
.
scopesRoute
],
children:
<
TestSemantics
>[
TestSemantics
(
flags:
<
SemanticsFlag
>[
SemanticsFlag
.
isTextField
,
],
value:
originalText
,
textDirection:
TextDirection
.
ltr
,
),
],
),
],
),
],
),
ignoreTransform:
true
,
ignoreRect:
true
,
ignoreId:
true
,
),
);
// Now change it to make it obscure text.
await
tester
.
pumpWidget
(
MaterialApp
(
home:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
controller:
controller
,
obscureText:
true
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
));
expect
(
findRenderEditable
(
tester
).
text
.
text
,
expectedValue
);
expect
(
semantics
,
hasSemantics
(
TestSemantics
(
children:
<
TestSemantics
>[
TestSemantics
.
rootChild
(
children:
<
TestSemantics
>[
TestSemantics
(
flags:
<
SemanticsFlag
>[
SemanticsFlag
.
scopesRoute
],
children:
<
TestSemantics
>[
TestSemantics
(
flags:
<
SemanticsFlag
>[
SemanticsFlag
.
isTextField
,
SemanticsFlag
.
isObscured
,
SemanticsFlag
.
isFocused
,
],
value:
expectedValue
,
textDirection:
TextDirection
.
ltr
,
),
],
),
],
),
],
),
ignoreTransform:
true
,
ignoreRect:
true
,
ignoreId:
true
,
),
);
semantics
.
dispose
();
});
...
...
@@ -1763,7 +1657,8 @@ testWidgets(
SemanticsAction
.
moveCursorBackwardByWord
,
SemanticsAction
.
setSelection
,
],
));
),
);
when
(
controls
.
canCopy
(
any
)).
thenReturn
(
true
);
await
_buildApp
(
controls
,
tester
);
...
...
@@ -1777,7 +1672,8 @@ testWidgets(
SemanticsAction
.
setSelection
,
SemanticsAction
.
copy
,
],
));
),
);
when
(
controls
.
canCopy
(
any
)).
thenReturn
(
false
);
when
(
controls
.
canPaste
(
any
)).
thenReturn
(
true
);
...
...
@@ -1792,7 +1688,8 @@ testWidgets(
SemanticsAction
.
setSelection
,
SemanticsAction
.
paste
,
],
));
),
);
when
(
controls
.
canPaste
(
any
)).
thenReturn
(
false
);
when
(
controls
.
canCut
(
any
)).
thenReturn
(
true
);
...
...
@@ -1807,7 +1704,8 @@ testWidgets(
SemanticsAction
.
setSelection
,
SemanticsAction
.
cut
,
],
));
),
);
when
(
controls
.
canCopy
(
any
)).
thenReturn
(
true
);
when
(
controls
.
canCut
(
any
)).
thenReturn
(
true
);
...
...
@@ -1825,7 +1723,8 @@ testWidgets(
SemanticsAction
.
copy
,
SemanticsAction
.
paste
,
],
));
),
);
semantics
.
dispose
();
});
...
...
@@ -1881,7 +1780,9 @@ testWidgets(
],
),
ignoreRect:
true
,
ignoreTransform:
true
));
ignoreTransform:
true
,
),
);
owner
.
performAction
(
expectedNodeId
,
SemanticsAction
.
copy
);
verify
(
controls
.
handleCopy
(
any
)).
called
(
1
);
...
...
@@ -1915,196 +1816,6 @@ testWidgets(
expect
(
render
.
text
.
style
.
fontStyle
,
FontStyle
.
italic
);
});
testWidgets
(
'autofocus sets cursor to the end of text'
,
(
WidgetTester
tester
)
async
{
const
String
text
=
'hello world'
;
final
FocusScopeNode
focusScopeNode
=
FocusScopeNode
();
final
FocusNode
focusNode
=
FocusNode
();
controller
.
text
=
text
;
await
tester
.
pumpWidget
(
MediaQuery
(
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
FocusScope
(
node:
focusScopeNode
,
autofocus:
true
,
child:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
controller:
controller
,
focusNode:
focusNode
,
autofocus:
true
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
);
expect
(
focusNode
.
hasFocus
,
true
);
expect
(
controller
.
selection
.
isCollapsed
,
true
);
expect
(
controller
.
selection
.
baseOffset
,
text
.
length
);
});
RenderEditable
findRenderEditable
(
WidgetTester
tester
)
{
final
RenderObject
root
=
tester
.
renderObject
(
find
.
byType
(
EditableText
));
expect
(
root
,
isNotNull
);
RenderEditable
renderEditable
;
void
recursiveFinder
(
RenderObject
child
)
{
if
(
child
is
RenderEditable
)
{
renderEditable
=
child
;
return
;
}
child
.
visitChildren
(
recursiveFinder
);
}
root
.
visitChildren
(
recursiveFinder
);
expect
(
renderEditable
,
isNotNull
);
return
renderEditable
;
}
testWidgets
(
'Updating the floating cursor correctly moves the cursor'
,
(
WidgetTester
tester
)
async
{
const
String
text
=
'hello world this is fun and cool and awesome!'
;
controller
.
text
=
text
;
final
FocusNode
focusNode
=
FocusNode
();
await
tester
.
pumpWidget
(
MediaQuery
(
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
FocusScope
(
node:
focusScopeNode
,
autofocus:
true
,
child:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
controller:
controller
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
);
await
tester
.
tap
(
find
.
byType
(
EditableText
));
final
RenderEditable
renderEditable
=
findRenderEditable
(
tester
);
renderEditable
.
selection
=
const
TextSelection
(
baseOffset:
29
,
extentOffset:
29
);
expect
(
controller
.
selection
.
baseOffset
,
29
);
final
EditableTextState
editableTextState
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Start
));
expect
(
controller
.
selection
.
baseOffset
,
29
);
// Sets the origin.
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(
20
,
20
)));
expect
(
controller
.
selection
.
baseOffset
,
29
);
// Moves the cursor right a few characters.
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(-
250
,
20
)));
// But we have not yet set the offset because the user is not done placing the cursor.
expect
(
controller
.
selection
.
baseOffset
,
29
);
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
End
));
await
tester
.
pumpAndSettle
();
// The cursor has been set.
expect
(
controller
.
selection
.
baseOffset
,
10
);
});
testWidgets
(
'Cursor gets placed correctly after going out of bounds'
,
(
WidgetTester
tester
)
async
{
const
String
text
=
'hello world this is fun and cool and awesome!'
;
controller
.
text
=
text
;
final
FocusNode
focusNode
=
FocusNode
();
await
tester
.
pumpWidget
(
MediaQuery
(
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
FocusScope
(
node:
focusScopeNode
,
autofocus:
true
,
child:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
controller:
controller
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
);
await
tester
.
tap
(
find
.
byType
(
EditableText
));
final
RenderEditable
renderEditable
=
findRenderEditable
(
tester
);
renderEditable
.
selection
=
const
TextSelection
(
baseOffset:
29
,
extentOffset:
29
);
expect
(
controller
.
selection
.
baseOffset
,
29
);
final
EditableTextState
editableTextState
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Start
));
expect
(
controller
.
selection
.
baseOffset
,
29
);
// Sets the origin.
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(
20
,
20
)));
expect
(
controller
.
selection
.
baseOffset
,
29
);
// Moves the cursor super far right
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(
2090
,
20
)));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(
2100
,
20
)));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(
2090
,
20
)));
// After peaking the cursor, we move in the opposite direction.
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(
1400
,
20
)));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
End
));
await
tester
.
pumpAndSettle
();
// The cursor has been set.
expect
(
controller
.
selection
.
baseOffset
,
8
);
// Go in the other direction.
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Start
));
// Sets the origin.
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(
20
,
20
)));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(-
5000
,
20
)));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(-
5010
,
20
)));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(-
5000
,
20
)));
// Move back in the opposite direction only a few hundred.
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
offset:
const
Offset
(-
4850
,
20
)));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
End
));
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
baseOffset
,
10
);
});
testWidgets
(
'Formatters are skipped if text has not changed'
,
(
WidgetTester
tester
)
async
{
int
called
=
0
;
final
TextInputFormatter
formatter
=
TextInputFormatter
.
withFunction
((
TextEditingValue
oldValue
,
TextEditingValue
newValue
)
{
...
...
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