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
0f8c0da0
Unverified
Commit
0f8c0da0
authored
Dec 18, 2019
by
LongCatIsLooong
Committed by
GitHub
Dec 18, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
iOS UITextInput autocorrection prompt (#45354)
parent
e54e301d
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
266 additions
and
3 deletions
+266
-3
text_field.dart
packages/flutter/lib/src/cupertino/text_field.dart
+4
-1
text_field.dart
packages/flutter/lib/src/material/text_field.dart
+3
-0
editable.dart
packages/flutter/lib/src/rendering/editable.dart
+60
-1
text_input.dart
packages/flutter/lib/src/services/text_input.dart
+9
-0
editable_text.dart
packages/flutter/lib/src/widgets/editable_text.dart
+40
-1
editable_test.dart
packages/flutter/test/rendering/editable_test.dart
+38
-0
text_input_test.dart
packages/flutter/test/services/text_input_test.dart
+31
-0
editable_text_test.dart
packages/flutter/test/widgets/editable_text_test.dart
+81
-0
No files found.
packages/flutter/lib/src/cupertino/text_field.dart
View file @
0f8c0da0
...
@@ -876,6 +876,8 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
...
@@ -876,6 +876,8 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
color:
enabled
?
decorationColor
:
(
decorationColor
??
disabledColor
),
color:
enabled
?
decorationColor
:
(
decorationColor
??
disabledColor
),
);
);
final
Color
selectionColor
=
CupertinoTheme
.
of
(
context
).
primaryColor
.
withOpacity
(
0.2
);
final
Widget
paddedEditable
=
Padding
(
final
Widget
paddedEditable
=
Padding
(
padding:
widget
.
padding
,
padding:
widget
.
padding
,
child:
RepaintBoundary
(
child:
RepaintBoundary
(
...
@@ -902,7 +904,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
...
@@ -902,7 +904,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
maxLines:
widget
.
maxLines
,
maxLines:
widget
.
maxLines
,
minLines:
widget
.
minLines
,
minLines:
widget
.
minLines
,
expands:
widget
.
expands
,
expands:
widget
.
expands
,
selectionColor:
CupertinoTheme
.
of
(
context
).
primaryColor
.
withOpacity
(
0.2
)
,
selectionColor:
selectionColor
,
selectionControls:
widget
.
selectionEnabled
selectionControls:
widget
.
selectionEnabled
?
cupertinoTextSelectionControls
:
null
,
?
cupertinoTextSelectionControls
:
null
,
onChanged:
widget
.
onChanged
,
onChanged:
widget
.
onChanged
,
...
@@ -917,6 +919,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
...
@@ -917,6 +919,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
cursorOpacityAnimates:
true
,
cursorOpacityAnimates:
true
,
cursorOffset:
cursorOffset
,
cursorOffset:
cursorOffset
,
paintCursorAboveText:
true
,
paintCursorAboveText:
true
,
autocorrectionTextRectColor:
selectionColor
,
backgroundCursorColor:
CupertinoDynamicColor
.
resolve
(
CupertinoColors
.
inactiveGray
,
context
),
backgroundCursorColor:
CupertinoDynamicColor
.
resolve
(
CupertinoColors
.
inactiveGray
,
context
),
scrollPadding:
widget
.
scrollPadding
,
scrollPadding:
widget
.
scrollPadding
,
keyboardAppearance:
keyboardAppearance
,
keyboardAppearance:
keyboardAppearance
,
...
...
packages/flutter/lib/src/material/text_field.dart
View file @
0f8c0da0
...
@@ -935,6 +935,7 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
...
@@ -935,6 +935,7 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
bool
cursorOpacityAnimates
;
bool
cursorOpacityAnimates
;
Offset
cursorOffset
;
Offset
cursorOffset
;
Color
cursorColor
=
widget
.
cursorColor
;
Color
cursorColor
=
widget
.
cursorColor
;
Color
autocorrectionTextRectColor
;
Radius
cursorRadius
=
widget
.
cursorRadius
;
Radius
cursorRadius
=
widget
.
cursorRadius
;
switch
(
themeData
.
platform
)
{
switch
(
themeData
.
platform
)
{
...
@@ -947,6 +948,7 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
...
@@ -947,6 +948,7 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
cursorColor
??=
CupertinoTheme
.
of
(
context
).
primaryColor
;
cursorColor
??=
CupertinoTheme
.
of
(
context
).
primaryColor
;
cursorRadius
??=
const
Radius
.
circular
(
2.0
);
cursorRadius
??=
const
Radius
.
circular
(
2.0
);
cursorOffset
=
Offset
(
iOSHorizontalOffset
/
MediaQuery
.
of
(
context
).
devicePixelRatio
,
0
);
cursorOffset
=
Offset
(
iOSHorizontalOffset
/
MediaQuery
.
of
(
context
).
devicePixelRatio
,
0
);
autocorrectionTextRectColor
=
themeData
.
textSelectionColor
;
break
;
break
;
case
TargetPlatform
.
android
:
case
TargetPlatform
.
android
:
...
@@ -1006,6 +1008,7 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
...
@@ -1006,6 +1008,7 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
dragStartBehavior:
widget
.
dragStartBehavior
,
dragStartBehavior:
widget
.
dragStartBehavior
,
scrollController:
widget
.
scrollController
,
scrollController:
widget
.
scrollController
,
scrollPhysics:
widget
.
scrollPhysics
,
scrollPhysics:
widget
.
scrollPhysics
,
autocorrectionTextRectColor:
autocorrectionTextRectColor
,
),
),
);
);
...
...
packages/flutter/lib/src/rendering/editable.dart
View file @
0f8c0da0
...
@@ -210,6 +210,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
...
@@ -210,6 +210,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
double
devicePixelRatio
=
1.0
,
double
devicePixelRatio
=
1.0
,
bool
enableInteractiveSelection
,
bool
enableInteractiveSelection
,
EdgeInsets
floatingCursorAddedMargin
=
const
EdgeInsets
.
fromLTRB
(
4
,
4
,
4
,
5
),
EdgeInsets
floatingCursorAddedMargin
=
const
EdgeInsets
.
fromLTRB
(
4
,
4
,
4
,
5
),
TextRange
promptRectRange
,
Color
promptRectColor
,
@required
this
.
textSelectionDelegate
,
@required
this
.
textSelectionDelegate
,
})
:
assert
(
textAlign
!=
null
),
})
:
assert
(
textAlign
!=
null
),
assert
(
textDirection
!=
null
,
'RenderEditable created without a textDirection.'
),
assert
(
textDirection
!=
null
,
'RenderEditable created without a textDirection.'
),
...
@@ -266,10 +268,13 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
...
@@ -266,10 +268,13 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
_endHandleLayerLink
=
endHandleLayerLink
,
_endHandleLayerLink
=
endHandleLayerLink
,
_obscureText
=
obscureText
,
_obscureText
=
obscureText
,
_readOnly
=
readOnly
,
_readOnly
=
readOnly
,
_forceLine
=
forceLine
{
_forceLine
=
forceLine
,
_promptRectRange
=
promptRectRange
{
assert
(
_showCursor
!=
null
);
assert
(
_showCursor
!=
null
);
assert
(!
_showCursor
.
value
||
cursorColor
!=
null
);
assert
(!
_showCursor
.
value
||
cursorColor
!=
null
);
this
.
hasFocus
=
hasFocus
??
false
;
this
.
hasFocus
=
hasFocus
??
false
;
if
(
promptRectColor
!=
null
)
_promptRectPaint
.
color
=
promptRectColor
;
}
}
/// Character used to obscure text if [obscureText] is true.
/// Character used to obscure text if [obscureText] is true.
...
@@ -1113,6 +1118,40 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
...
@@ -1113,6 +1118,40 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
return
enableInteractiveSelection
??
!
obscureText
;
return
enableInteractiveSelection
??
!
obscureText
;
}
}
/// The color used to paint the prompt rectangle.
///
/// The prompt rectangle will only be requested on non-web iOS applications.
Color
get
promptRectColor
=>
_promptRectPaint
.
color
;
set
promptRectColor
(
Color
newValue
)
{
// Painter.color can not be null.
if
(
newValue
==
null
)
{
setPromptRectRange
(
null
);
return
;
}
if
(
promptRectColor
==
newValue
)
return
;
_promptRectPaint
.
color
=
newValue
;
if
(
_promptRectRange
!=
null
)
markNeedsPaint
();
}
TextRange
_promptRectRange
;
/// Dismisses the currently displayed prompt rectangle and displays a new prompt rectangle
/// over [newRange] in the given color [promptRectColor].
///
/// The prompt rectangle will only be requested on non-web iOS applications.
///
/// When set to null, the currently displayed prompt rectangle (if any) will be dismissed.
void
setPromptRectRange
(
TextRange
newRange
)
{
if
(
_promptRectRange
==
newRange
)
return
;
_promptRectRange
=
newRange
;
markNeedsPaint
();
}
/// The maximum amount the text is allowed to scroll.
/// The maximum amount the text is allowed to scroll.
///
///
/// This value is only valid after layout and can change as additional
/// This value is only valid after layout and can change as additional
...
@@ -1912,6 +1951,24 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
...
@@ -1912,6 +1951,24 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
canvas
.
drawRect
(
box
.
toRect
().
shift
(
effectiveOffset
),
paint
);
canvas
.
drawRect
(
box
.
toRect
().
shift
(
effectiveOffset
),
paint
);
}
}
final
Paint
_promptRectPaint
=
Paint
();
void
_paintPromptRectIfNeeded
(
Canvas
canvas
,
Offset
effectiveOffset
)
{
if
(
_promptRectRange
==
null
||
promptRectColor
==
null
)
{
return
;
}
final
List
<
TextBox
>
boxes
=
_textPainter
.
getBoxesForSelection
(
TextSelection
(
baseOffset:
_promptRectRange
.
start
,
extentOffset:
_promptRectRange
.
end
,
),
);
for
(
TextBox
box
in
boxes
)
{
canvas
.
drawRect
(
box
.
toRect
().
shift
(
effectiveOffset
),
_promptRectPaint
);
}
}
void
_paintContents
(
PaintingContext
context
,
Offset
offset
)
{
void
_paintContents
(
PaintingContext
context
,
Offset
offset
)
{
assert
(
_textLayoutLastMaxWidth
==
constraints
.
maxWidth
&&
assert
(
_textLayoutLastMaxWidth
==
constraints
.
maxWidth
&&
_textLayoutLastMinWidth
==
constraints
.
minWidth
,
_textLayoutLastMinWidth
==
constraints
.
minWidth
,
...
@@ -1934,6 +1991,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
...
@@ -1934,6 +1991,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
_paintSelection
(
context
.
canvas
,
effectiveOffset
);
_paintSelection
(
context
.
canvas
,
effectiveOffset
);
}
}
_paintPromptRectIfNeeded
(
context
.
canvas
,
effectiveOffset
);
// On iOS, the cursor is painted over the text, on Android, it's painted
// On iOS, the cursor is painted over the text, on Android, it's painted
// under it.
// under it.
if
(
paintCursorAboveText
)
if
(
paintCursorAboveText
)
...
...
packages/flutter/lib/src/services/text_input.dart
View file @
0f8c0da0
...
@@ -753,6 +753,12 @@ abstract class TextInputClient {
...
@@ -753,6 +753,12 @@ abstract class TextInputClient {
/// Updates the floating cursor position and state.
/// Updates the floating cursor position and state.
void
updateFloatingCursor
(
RawFloatingCursorPoint
point
);
void
updateFloatingCursor
(
RawFloatingCursorPoint
point
);
/// Requests that this client display a prompt rectangle for the given text range,
/// to indicate the range of text that will be changed by a pending autocorrection.
///
/// This method will only be called on iOS.
void
showAutocorrectionPromptRect
(
int
start
,
int
end
);
/// Platform notified framework of closed connection.
/// Platform notified framework of closed connection.
///
///
/// [TextInputClient] should cleanup its connection and finalize editing.
/// [TextInputClient] should cleanup its connection and finalize editing.
...
@@ -1076,6 +1082,9 @@ class TextInput {
...
@@ -1076,6 +1082,9 @@ class TextInput {
case
'TextInputClient.onConnectionClosed'
:
case
'TextInputClient.onConnectionClosed'
:
_currentConnection
.
_client
.
connectionClosed
();
_currentConnection
.
_client
.
connectionClosed
();
break
;
break
;
case
'TextInputClient.showAutocorrectionPromptRect'
:
_currentConnection
.
_client
.
showAutocorrectionPromptRect
(
args
[
1
],
args
[
2
]);
break
;
default
:
default
:
throw
MissingPluginException
();
throw
MissingPluginException
();
}
}
...
...
packages/flutter/lib/src/widgets/editable_text.dart
View file @
0f8c0da0
...
@@ -395,6 +395,7 @@ class EditableText extends StatefulWidget {
...
@@ -395,6 +395,7 @@ class EditableText extends StatefulWidget {
this
.
enableInteractiveSelection
=
true
,
this
.
enableInteractiveSelection
=
true
,
this
.
scrollController
,
this
.
scrollController
,
this
.
scrollPhysics
,
this
.
scrollPhysics
,
this
.
autocorrectionTextRectColor
,
this
.
toolbarOptions
=
const
ToolbarOptions
(
this
.
toolbarOptions
=
const
ToolbarOptions
(
copy:
true
,
copy:
true
,
cut:
true
,
cut:
true
,
...
@@ -634,6 +635,18 @@ class EditableText extends StatefulWidget {
...
@@ -634,6 +635,18 @@ class EditableText extends StatefulWidget {
/// Cannot be null.
/// Cannot be null.
final
Color
cursorColor
;
final
Color
cursorColor
;
/// The color to use when painting the autocorrection Rect.
///
/// For [CupertinoTextField]s, the value is set to the ambient
/// [CupertinoThemeData.primaryColor] with 20% opacity. For [TextField]s, the
/// value is null on non-iOS platforms and the same color used in [CupertinoTextField]
/// on iOS.
///
/// Currently the autocorrection Rect only appears on iOS.
///
/// Defaults to null, which disables autocorrection Rect painting.
final
Color
autocorrectionTextRectColor
;
/// The color to use when painting the background cursor aligned with the text
/// The color to use when painting the background cursor aligned with the text
/// while rendering the floating cursor.
/// while rendering the floating cursor.
///
///
...
@@ -737,6 +750,10 @@ class EditableText extends StatefulWidget {
...
@@ -737,6 +750,10 @@ class EditableText extends StatefulWidget {
final
bool
autofocus
;
final
bool
autofocus
;
/// The color to use when painting the selection.
/// The color to use when painting the selection.
///
/// For [CupertinoTextField]s, the value is set to the ambient
/// [CupertinoThemeData.primaryColor] with 20% opacity. For [TextField]s, the
/// value is set to the ambient [ThemeData.textSelectionColor].
final
Color
selectionColor
;
final
Color
selectionColor
;
/// Optional delegate for building the text selection handles and toolbar.
/// Optional delegate for building the text selection handles and toolbar.
...
@@ -1212,6 +1229,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -1212,6 +1229,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
if
(
value
.
text
!=
_value
.
text
)
{
if
(
value
.
text
!=
_value
.
text
)
{
hideToolbar
();
hideToolbar
();
_showCaretOnScreen
();
_showCaretOnScreen
();
_currentPromptRectRange
=
null
;
if
(
widget
.
obscureText
&&
value
.
text
.
length
==
_value
.
text
.
length
+
1
)
{
if
(
widget
.
obscureText
&&
value
.
text
.
length
==
_value
.
text
.
length
+
1
)
{
_obscureShowCharTicksPending
=
_kObscureShowLatestCharCursorTicks
;
_obscureShowCharTicksPending
=
_kObscureShowLatestCharCursorTicks
;
_obscureLatestCharIndex
=
_value
.
selection
.
baseOffset
;
_obscureLatestCharIndex
=
_value
.
selection
.
baseOffset
;
...
@@ -1734,6 +1752,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -1734,6 +1752,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
WidgetsBinding
.
instance
.
removeObserver
(
this
);
WidgetsBinding
.
instance
.
removeObserver
(
this
);
// Clear the selection and composition state if this widget lost focus.
// Clear the selection and composition state if this widget lost focus.
_value
=
TextEditingValue
(
text:
_value
.
text
);
_value
=
TextEditingValue
(
text:
_value
.
text
);
_currentPromptRectRange
=
null
;
}
}
updateKeepAlive
();
updateKeepAlive
();
}
}
...
@@ -1812,6 +1831,16 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -1812,6 +1831,16 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
}
}
}
}
// null if no promptRect should be shown.
TextRange
_currentPromptRectRange
;
@override
void
showAutocorrectionPromptRect
(
int
start
,
int
end
)
{
setState
(()
{
_currentPromptRectRange
=
TextRange
(
start:
start
,
end:
end
);
});
}
VoidCallback
_semanticsOnCopy
(
TextSelectionControls
controls
)
{
VoidCallback
_semanticsOnCopy
(
TextSelectionControls
controls
)
{
return
widget
.
selectionEnabled
&&
copyEnabled
&&
_hasFocus
&&
controls
?.
canCopy
(
this
)
==
true
return
widget
.
selectionEnabled
&&
copyEnabled
&&
_hasFocus
&&
controls
?.
canCopy
(
this
)
==
true
?
()
=>
controls
.
handleCopy
(
this
)
?
()
=>
controls
.
handleCopy
(
this
)
...
@@ -1890,6 +1919,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -1890,6 +1919,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
enableInteractiveSelection:
widget
.
enableInteractiveSelection
,
enableInteractiveSelection:
widget
.
enableInteractiveSelection
,
textSelectionDelegate:
this
,
textSelectionDelegate:
this
,
devicePixelRatio:
_devicePixelRatio
,
devicePixelRatio:
_devicePixelRatio
,
promptRectRange:
_currentPromptRectRange
,
promptRectColor:
widget
.
autocorrectionTextRectColor
,
),
),
),
),
);
);
...
@@ -1958,6 +1989,8 @@ class _Editable extends LeafRenderObjectWidget {
...
@@ -1958,6 +1989,8 @@ class _Editable extends LeafRenderObjectWidget {
this
.
textSelectionDelegate
,
this
.
textSelectionDelegate
,
this
.
paintCursorAboveText
,
this
.
paintCursorAboveText
,
this
.
devicePixelRatio
,
this
.
devicePixelRatio
,
this
.
promptRectRange
,
this
.
promptRectColor
,
})
:
assert
(
textDirection
!=
null
),
})
:
assert
(
textDirection
!=
null
),
assert
(
rendererIgnoresPointer
!=
null
),
assert
(
rendererIgnoresPointer
!=
null
),
super
(
key:
key
);
super
(
key:
key
);
...
@@ -1998,6 +2031,8 @@ class _Editable extends LeafRenderObjectWidget {
...
@@ -1998,6 +2031,8 @@ class _Editable extends LeafRenderObjectWidget {
final
TextSelectionDelegate
textSelectionDelegate
;
final
TextSelectionDelegate
textSelectionDelegate
;
final
double
devicePixelRatio
;
final
double
devicePixelRatio
;
final
bool
paintCursorAboveText
;
final
bool
paintCursorAboveText
;
final
TextRange
promptRectRange
;
final
Color
promptRectColor
;
@override
@override
RenderEditable
createRenderObject
(
BuildContext
context
)
{
RenderEditable
createRenderObject
(
BuildContext
context
)
{
...
@@ -2034,6 +2069,8 @@ class _Editable extends LeafRenderObjectWidget {
...
@@ -2034,6 +2069,8 @@ class _Editable extends LeafRenderObjectWidget {
enableInteractiveSelection:
enableInteractiveSelection
,
enableInteractiveSelection:
enableInteractiveSelection
,
textSelectionDelegate:
textSelectionDelegate
,
textSelectionDelegate:
textSelectionDelegate
,
devicePixelRatio:
devicePixelRatio
,
devicePixelRatio:
devicePixelRatio
,
promptRectRange:
promptRectRange
,
promptRectColor:
promptRectColor
,
);
);
}
}
...
@@ -2069,6 +2106,8 @@ class _Editable extends LeafRenderObjectWidget {
...
@@ -2069,6 +2106,8 @@ class _Editable extends LeafRenderObjectWidget {
..
cursorOffset
=
cursorOffset
..
cursorOffset
=
cursorOffset
..
textSelectionDelegate
=
textSelectionDelegate
..
textSelectionDelegate
=
textSelectionDelegate
..
devicePixelRatio
=
devicePixelRatio
..
devicePixelRatio
=
devicePixelRatio
..
paintCursorAboveText
=
paintCursorAboveText
;
..
paintCursorAboveText
=
paintCursorAboveText
..
promptRectColor
=
promptRectColor
..
setPromptRectRange
(
promptRectRange
);
}
}
}
}
packages/flutter/test/rendering/editable_test.dart
View file @
0f8c0da0
...
@@ -546,6 +546,44 @@ void main() {
...
@@ -546,6 +546,44 @@ void main() {
expect
(
selectionChangedCount
,
1
);
expect
(
selectionChangedCount
,
1
);
},
skip:
isBrowser
);
},
skip:
isBrowser
);
test
(
'promptRect disappears when promptRectColor is set to null'
,
()
{
const
Color
promptRectColor
=
Color
(
0x12345678
);
final
TextSelectionDelegate
delegate
=
FakeEditableTextState
();
final
RenderEditable
editable
=
RenderEditable
(
text:
const
TextSpan
(
style:
TextStyle
(
height:
1.0
,
fontSize:
10.0
,
fontFamily:
'Ahem'
),
text:
'ABCDEFG'
,
),
startHandleLayerLink:
LayerLink
(),
endHandleLayerLink:
LayerLink
(),
textAlign:
TextAlign
.
start
,
textDirection:
TextDirection
.
ltr
,
locale:
const
Locale
(
'en'
,
'US'
),
offset:
ViewportOffset
.
fixed
(
10.0
),
textSelectionDelegate:
delegate
,
selection:
const
TextSelection
.
collapsed
(
offset:
0
),
promptRectColor:
promptRectColor
,
promptRectRange:
const
TextRange
(
start:
0
,
end:
1
),
);
editable
.
layout
(
BoxConstraints
.
loose
(
const
Size
(
1000.0
,
1000.0
)));
expect
(
(
Canvas
canvas
)
=>
editable
.
paint
(
TestRecordingPaintingContext
(
canvas
),
Offset
.
zero
),
paints
..
rect
(
color:
promptRectColor
),
);
editable
.
promptRectColor
=
null
;
editable
.
layout
(
BoxConstraints
.
loose
(
const
Size
(
1000.0
,
1000.0
)));
pumpFrame
();
expect
(
editable
.
promptRectColor
,
promptRectColor
);
expect
(
(
Canvas
canvas
)
=>
editable
.
paint
(
TestRecordingPaintingContext
(
canvas
),
Offset
.
zero
),
isNot
(
paints
..
rect
(
color:
promptRectColor
)),
);
});
test
(
'editable hasFocus correctly initialized'
,
()
{
test
(
'editable hasFocus correctly initialized'
,
()
{
// Regression test for https://github.com/flutter/flutter/issues/21640
// Regression test for https://github.com/flutter/flutter/issues/21640
final
TextSelectionDelegate
delegate
=
FakeEditableTextState
();
final
TextSelectionDelegate
delegate
=
FakeEditableTextState
();
...
...
packages/flutter/test/services/text_input_test.dart
View file @
0f8c0da0
...
@@ -72,6 +72,10 @@ void main() {
...
@@ -72,6 +72,10 @@ void main() {
});
});
group
(
'TextInputConfiguration'
,
()
{
group
(
'TextInputConfiguration'
,
()
{
tearDown
(()
{
TextInputConnection
.
debugResetId
();
});
test
(
'sets expected defaults'
,
()
{
test
(
'sets expected defaults'
,
()
{
const
TextInputConfiguration
configuration
=
TextInputConfiguration
();
const
TextInputConfiguration
configuration
=
TextInputConfiguration
();
expect
(
configuration
.
inputType
,
TextInputType
.
text
);
expect
(
configuration
.
inputType
,
TextInputType
.
text
);
...
@@ -172,6 +176,28 @@ void main() {
...
@@ -172,6 +176,28 @@ void main() {
expect
(
client
.
latestMethodCall
,
'connectionClosed'
);
expect
(
client
.
latestMethodCall
,
'connectionClosed'
);
});
});
test
(
'TextInputClient showAutocorrectionPromptRect method is called'
,
()
async
{
// Assemble a TextInputConnection so we can verify its change in state.
final
FakeTextInputClient
client
=
FakeTextInputClient
();
const
TextInputConfiguration
configuration
=
TextInputConfiguration
();
TextInput
.
attach
(
client
,
configuration
);
expect
(
client
.
latestMethodCall
,
isEmpty
);
// Send onConnectionClosed message.
final
ByteData
messageBytes
=
const
JSONMessageCodec
().
encodeMessage
(<
String
,
dynamic
>{
'args'
:
<
dynamic
>[
1
,
0
,
1
],
'method'
:
'TextInputClient.showAutocorrectionPromptRect'
,
});
await
ServicesBinding
.
instance
.
defaultBinaryMessenger
.
handlePlatformMessage
(
'flutter/textinput'
,
messageBytes
,
(
ByteData
_
)
{},
);
expect
(
client
.
latestMethodCall
,
'showAutocorrectionPromptRect'
);
});
});
});
}
}
...
@@ -198,6 +224,11 @@ class FakeTextInputClient implements TextInputClient {
...
@@ -198,6 +224,11 @@ class FakeTextInputClient implements TextInputClient {
latestMethodCall
=
'connectionClosed'
;
latestMethodCall
=
'connectionClosed'
;
}
}
@override
void
showAutocorrectionPromptRect
(
int
start
,
int
end
)
{
latestMethodCall
=
'showAutocorrectionPromptRect'
;
}
TextInputConfiguration
get
configuration
=>
const
TextInputConfiguration
();
TextInputConfiguration
get
configuration
=>
const
TextInputConfiguration
();
}
}
...
...
packages/flutter/test/widgets/editable_text_test.dart
View file @
0f8c0da0
...
@@ -13,6 +13,7 @@ import 'package:flutter/services.dart';
...
@@ -13,6 +13,7 @@ import 'package:flutter/services.dart';
import
'package:mockito/mockito.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/foundation.dart'
;
import
'../rendering/mock_canvas.dart'
;
import
'editable_text_utils.dart'
;
import
'editable_text_utils.dart'
;
import
'semantics_tester.dart'
;
import
'semantics_tester.dart'
;
...
@@ -1338,6 +1339,86 @@ void main() {
...
@@ -1338,6 +1339,86 @@ void main() {
assert
(!
onEditingCompleteCalled
);
assert
(!
onEditingCompleteCalled
);
});
});
testWidgets
(
'iOS autocorrection rectangle should appear on demand'
'and dismiss when the text changes or when focus is lost'
,
(
WidgetTester
tester
)
async
{
const
Color
rectColor
=
Color
(
0xFFFF0000
);
void
verifyAutocorrectionRectVisibility
({
bool
expectVisible
})
{
PaintPattern
evaluate
()
{
if
(
expectVisible
)
{
return
paints
..
something
(((
Symbol
method
,
List
<
dynamic
>
arguments
)
{
if
(
method
!=
#drawRect
)
return
false
;
final
Paint
paint
=
arguments
[
1
];
return
paint
.
color
==
rectColor
;
}));
}
else
{
return
paints
..
everything
(((
Symbol
method
,
List
<
dynamic
>
arguments
)
{
if
(
method
!=
#drawRect
)
return
true
;
final
Paint
paint
=
arguments
[
1
];
if
(
paint
.
color
!=
rectColor
)
return
true
;
throw
'Expected: autocorrection rect not visible, found:
${arguments[0]}
'
;
}));
}
}
expect
(
findRenderEditable
(
tester
),
evaluate
());
}
final
FocusNode
focusNode
=
FocusNode
();
final
TextEditingController
controller
=
TextEditingController
(
text:
'ABCDEFG'
);
final
Widget
widget
=
MaterialApp
(
home:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
controller:
controller
,
focusNode:
focusNode
,
style:
Typography
(
platform:
TargetPlatform
.
android
).
black
.
subhead
,
cursorColor:
Colors
.
blue
,
autocorrect:
true
,
autocorrectionTextRectColor:
rectColor
,
showCursor:
false
,
onEditingComplete:
()
{
},
),
);
await
tester
.
pumpWidget
(
widget
);
await
tester
.
tap
(
find
.
byType
(
EditableText
));
await
tester
.
pump
();
final
EditableTextState
state
=
tester
.
state
<
EditableTextState
>(
find
.
byType
(
EditableText
));
assert
(
focusNode
.
hasFocus
);
// The prompt rect should be invisible initially.
verifyAutocorrectionRectVisibility
(
expectVisible:
false
);
state
.
showAutocorrectionPromptRect
(
0
,
1
);
await
tester
.
pump
();
// Show prompt rect when told to.
verifyAutocorrectionRectVisibility
(
expectVisible:
true
);
// Text changed, prompt rect goes away.
controller
.
text
=
'12345'
;
await
tester
.
pump
();
verifyAutocorrectionRectVisibility
(
expectVisible:
false
);
state
.
showAutocorrectionPromptRect
(
0
,
1
);
await
tester
.
pump
();
verifyAutocorrectionRectVisibility
(
expectVisible:
true
);
// Unfocus, prompt rect should go away.
focusNode
.
unfocus
();
await
tester
.
pump
();
verifyAutocorrectionRectVisibility
(
expectVisible:
false
);
});
testWidgets
(
'Changing controller updates EditableText'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Changing controller updates EditableText'
,
(
WidgetTester
tester
)
async
{
final
TextEditingController
controller1
=
final
TextEditingController
controller1
=
TextEditingController
(
text:
'Wibble'
);
TextEditingController
(
text:
'Wibble'
);
...
...
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