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
730025fa
Unverified
Commit
730025fa
authored
Jun 03, 2019
by
chunhtai
Committed by
GitHub
Jun 03, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix issue 14014 read only text field (#32059)
parent
54410441
Changes
13
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
593 additions
and
71 deletions
+593
-71
text_field.dart
packages/flutter/lib/src/cupertino/text_field.dart
+22
-2
text_field.dart
packages/flutter/lib/src/material/text_field.dart
+31
-5
text_form_field.dart
packages/flutter/lib/src/material/text_form_field.dart
+5
-0
editable.dart
packages/flutter/lib/src/rendering/editable.dart
+7
-0
text_input.dart
packages/flutter/lib/src/services/text_input.dart
+12
-0
editable_text.dart
packages/flutter/lib/src/widgets/editable_text.dart
+83
-16
text_selection.dart
packages/flutter/lib/src/widgets/text_selection.dart
+68
-16
text_field_test.dart
packages/flutter/test/cupertino/text_field_test.dart
+38
-2
text_field_test.dart
packages/flutter/test/material/text_field_test.dart
+245
-23
text_form_field_test.dart
packages/flutter/test/material/text_form_field_test.dart
+45
-0
editable_text_cursor_test.dart
packages/flutter/test/widgets/editable_text_cursor_test.dart
+28
-0
editable_text_test.dart
packages/flutter/test/widgets/editable_text_test.dart
+6
-7
test_text_input.dart
packages/flutter_test/lib/src/test_text_input.dart
+3
-0
No files found.
packages/flutter/lib/src/cupertino/text_field.dart
View file @
730025fa
...
...
@@ -139,6 +139,9 @@ class CupertinoTextField extends StatefulWidget {
/// this is a single-line text field and will scroll horizontally when
/// overflown. [maxLines] must not be zero.
///
/// The text cursor is not shown if [showCursor] is false or if [showCursor]
/// is null (the default) and [readOnly] is true.
///
/// See also:
///
/// * [minLines]
...
...
@@ -167,6 +170,8 @@ class CupertinoTextField extends StatefulWidget {
this
.
style
,
this
.
strutStyle
,
this
.
textAlign
=
TextAlign
.
start
,
this
.
readOnly
=
false
,
this
.
showCursor
,
this
.
autofocus
=
false
,
this
.
obscureText
=
false
,
this
.
autocorrect
=
true
,
...
...
@@ -190,6 +195,7 @@ class CupertinoTextField extends StatefulWidget {
this
.
scrollController
,
this
.
scrollPhysics
,
})
:
assert
(
textAlign
!=
null
),
assert
(
readOnly
!=
null
),
assert
(
autofocus
!=
null
),
assert
(
obscureText
!=
null
),
assert
(
autocorrect
!=
null
),
...
...
@@ -313,6 +319,12 @@ class CupertinoTextField extends StatefulWidget {
/// {@macro flutter.widgets.editableText.textAlign}
final
TextAlign
textAlign
;
/// {@macro flutter.widgets.editableText.readOnly}
final
bool
readOnly
;
/// {@macro flutter.widgets.editableText.showCursor}
final
bool
showCursor
;
/// {@macro flutter.widgets.editableText.autofocus}
final
bool
autofocus
;
...
...
@@ -489,6 +501,8 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
// For backwards-compatibility, we treat a null kind the same as touch.
bool
_shouldShowSelectionToolbar
=
true
;
bool
_showSelectionHandles
=
false
;
@override
void
initState
()
{
super
.
initState
();
...
...
@@ -644,8 +658,11 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
if
(
cause
==
SelectionChangedCause
.
longPress
)
{
_editableText
?.
bringIntoView
(
selection
.
base
);
}
if
(
_shouldShowSelectionHandles
(
cause
))
{
_editableText
?.
showHandles
();
final
bool
willShowSelectionHandles
=
_shouldShowSelectionHandles
(
cause
);
if
(
willShowSelectionHandles
!=
_showSelectionHandles
)
{
setState
(()
{
_showSelectionHandles
=
willShowSelectionHandles
;
});
}
}
...
...
@@ -812,6 +829,9 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
child:
EditableText
(
key:
_editableTextKey
,
controller:
controller
,
readOnly:
widget
.
readOnly
,
showCursor:
widget
.
showCursor
,
showSelectionHandles:
_showSelectionHandles
,
focusNode:
_effectiveFocusNode
,
keyboardType:
widget
.
keyboardType
,
textInputAction:
widget
.
textInputAction
,
...
...
packages/flutter/lib/src/material/text_field.dart
View file @
730025fa
...
...
@@ -123,7 +123,10 @@ class TextField extends StatefulWidget {
/// characters may be entered, and the error counter and divider will
/// switch to the [decoration.errorStyle] when the limit is exceeded.
///
/// The [textAlign], [autofocus], [obscureText], [autocorrect],
/// The text cursor is not shown if [showCursor] is false or if [showCursor]
/// is null (the default) and [readOnly] is true.
///
/// The [textAlign], [autofocus], [obscureText], [readOnly], [autocorrect],
/// [maxLengthEnforced], [scrollPadding], [maxLines], and [maxLength]
/// arguments must not be null.
///
...
...
@@ -143,6 +146,8 @@ class TextField extends StatefulWidget {
this
.
strutStyle
,
this
.
textAlign
=
TextAlign
.
start
,
this
.
textDirection
,
this
.
readOnly
=
false
,
this
.
showCursor
,
this
.
autofocus
=
false
,
this
.
obscureText
=
false
,
this
.
autocorrect
=
true
,
...
...
@@ -168,6 +173,7 @@ class TextField extends StatefulWidget {
this
.
scrollController
,
this
.
scrollPhysics
,
})
:
assert
(
textAlign
!=
null
),
assert
(
readOnly
!=
null
),
assert
(
autofocus
!=
null
),
assert
(
obscureText
!=
null
),
assert
(
autocorrect
!=
null
),
...
...
@@ -289,6 +295,12 @@ class TextField extends StatefulWidget {
/// {@macro flutter.widgets.editableText.expands}
final
bool
expands
;
/// {@macro flutter.widgets.editableText.readOnly}
final
bool
readOnly
;
/// {@macro flutter.widgets.editableText.showCursor}
final
bool
showCursor
;
/// If [maxLength] is set to this value, only the "current input length"
/// part of the character counter is shown.
static
const
int
noMaxLength
=
-
1
;
...
...
@@ -522,6 +534,8 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
bool
_shouldShowSelectionToolbar
=
true
;
bool
_showSelectionHandles
=
false
;
InputDecoration
_getEffectiveDecoration
()
{
final
MaterialLocalizations
localizations
=
MaterialLocalizations
.
of
(
context
);
final
ThemeData
themeData
=
Theme
.
of
(
context
);
...
...
@@ -606,6 +620,11 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
if
(
wasEnabled
&&
!
isEnabled
)
{
_effectiveFocusNode
.
unfocus
();
}
if
(
_effectiveFocusNode
.
hasFocus
&&
widget
.
readOnly
!=
oldWidget
.
readOnly
)
{
if
(
_effectiveController
.
selection
.
isCollapsed
)
{
_showSelectionHandles
=
!
widget
.
readOnly
;
}
}
}
@override
...
...
@@ -629,6 +648,9 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
if
(
cause
==
SelectionChangedCause
.
keyboard
)
return
false
;
if
(
widget
.
readOnly
&&
_effectiveController
.
selection
.
isCollapsed
)
return
false
;
if
(
cause
==
SelectionChangedCause
.
longPress
)
return
true
;
...
...
@@ -639,10 +661,11 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
}
void
_handleSelectionChanged
(
TextSelection
selection
,
SelectionChangedCause
cause
)
{
// iOS cursor doesn't move via a selection handle. The scroll happens
// directly from new text selection changes.
if
(
_shouldShowSelectionHandles
(
cause
))
{
_editableText
?.
showHandles
();
final
bool
willShowSelectionHandles
=
_shouldShowSelectionHandles
(
cause
);
if
(
willShowSelectionHandles
!=
_showSelectionHandles
)
{
setState
(()
{
_showSelectionHandles
=
willShowSelectionHandles
;
});
}
switch
(
Theme
.
of
(
context
).
platform
)
{
...
...
@@ -927,6 +950,9 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
Widget
child
=
RepaintBoundary
(
child:
EditableText
(
key:
_editableTextKey
,
readOnly:
widget
.
readOnly
,
showCursor:
widget
.
showCursor
,
showSelectionHandles:
_showSelectionHandles
,
controller:
controller
,
focusNode:
focusNode
,
keyboardType:
widget
.
keyboardType
,
...
...
packages/flutter/lib/src/material/text_form_field.dart
View file @
730025fa
...
...
@@ -84,6 +84,8 @@ class TextFormField extends FormField<String> {
TextDirection
textDirection
,
TextAlign
textAlign
=
TextAlign
.
start
,
bool
autofocus
=
false
,
bool
readOnly
=
false
,
bool
showCursor
,
bool
obscureText
=
false
,
bool
autocorrect
=
true
,
bool
autovalidate
=
false
,
...
...
@@ -108,6 +110,7 @@ class TextFormField extends FormField<String> {
})
:
assert
(
initialValue
==
null
||
controller
==
null
),
assert
(
textAlign
!=
null
),
assert
(
autofocus
!=
null
),
assert
(
readOnly
!=
null
),
assert
(
obscureText
!=
null
),
assert
(
autocorrect
!=
null
),
assert
(
autovalidate
!=
null
),
...
...
@@ -149,6 +152,8 @@ class TextFormField extends FormField<String> {
textDirection:
textDirection
,
textCapitalization:
textCapitalization
,
autofocus:
autofocus
,
readOnly:
readOnly
,
showCursor:
showCursor
,
obscureText:
obscureText
,
autocorrect:
autocorrect
,
maxLengthEnforced:
maxLengthEnforced
,
...
...
packages/flutter/lib/src/rendering/editable.dart
View file @
730025fa
...
...
@@ -238,6 +238,13 @@ class RenderEditable extends RenderBox {
/// The default value of this property is false.
bool
ignorePointer
;
/// Whether text is composed.
///
/// Text is composed when user selects it for editing. The [TextSpan] will have
/// children with composing effect and leave text property to be null.
@visibleForTesting
bool
get
isComposingText
=>
text
.
text
==
null
;
/// The pixel ratio of the current device.
///
/// Should be obtained by querying MediaQuery for the devicePixelRatio.
...
...
packages/flutter/lib/src/services/text_input.dart
View file @
730025fa
...
...
@@ -585,6 +585,18 @@ abstract class TextSelectionDelegate {
/// Brings the provided [TextPosition] into the visible area of the text
/// input.
void
bringIntoView
(
TextPosition
position
);
/// Whether cut is enabled, must not be null.
bool
get
cutEnabled
=>
true
;
/// Whether copy is enabled, must not be null.
bool
get
copyEnabled
=>
true
;
/// Whether paste is enabled, must not be null.
bool
get
pasteEnabled
=>
true
;
/// Whether select all is enabled, must not be null.
bool
get
selectAllEnabled
=>
true
;
}
/// An interface to receive information from [TextInput].
...
...
packages/flutter/lib/src/widgets/editable_text.dart
View file @
730025fa
...
...
@@ -260,13 +260,17 @@ class EditableText extends StatefulWidget {
/// [TextInputType.text] unless [maxLines] is greater than one, when it will
/// default to [TextInputType.multiline].
///
/// The text cursor is not shown if [showCursor] is false or if [showCursor]
/// is null (the default) and [readOnly] is true.
///
/// The [controller], [focusNode], [style], [cursorColor], [backgroundCursorColor],
/// [textAlign], [dragStartBehavior]
and [rendererIgnoresPointer] arguments
/// must not be null.
/// [textAlign], [dragStartBehavior]
, [rendererIgnoresPointer] and [readOnly]
///
arguments
must not be null.
EditableText
({
Key
key
,
@required
this
.
controller
,
@required
this
.
focusNode
,
this
.
readOnly
=
false
,
this
.
obscureText
=
false
,
this
.
autocorrect
=
true
,
@required
this
.
style
,
...
...
@@ -281,6 +285,8 @@ class EditableText extends StatefulWidget {
this
.
minLines
,
this
.
expands
=
false
,
this
.
autofocus
=
false
,
bool
showCursor
,
this
.
showSelectionHandles
=
false
,
this
.
selectionColor
,
this
.
selectionControls
,
TextInputType
keyboardType
,
...
...
@@ -308,6 +314,8 @@ class EditableText extends StatefulWidget {
assert
(
focusNode
!=
null
),
assert
(
obscureText
!=
null
),
assert
(
autocorrect
!=
null
),
assert
(
showSelectionHandles
!=
null
),
assert
(
readOnly
!=
null
),
assert
(
style
!=
null
),
assert
(
cursorColor
!=
null
),
assert
(
cursorOpacityAnimates
!=
null
),
...
...
@@ -337,6 +345,7 @@ class EditableText extends StatefulWidget {
..
addAll
(
inputFormatters
??
const
Iterable
<
TextInputFormatter
>.
empty
())
)
:
inputFormatters
,
showCursor
=
showCursor
??
!
readOnly
,
super
(
key:
key
);
/// Controls the text being edited.
...
...
@@ -355,6 +364,38 @@ class EditableText extends StatefulWidget {
/// {@endtemplate}
final
bool
obscureText
;
/// {@template flutter.widgets.editableText.readOnly}
/// Whether the text can be changed.
///
/// When this is set to true, the text cannot be modified
/// by any shortcut or keyboard operation. The text is still selectable.
///
/// Defaults to false. Must not be null.
/// {@endtemplate}
final
bool
readOnly
;
/// Whether to show selection handles.
///
/// When a selection is active, there will be two handles at each side of
/// boundary, or one handle if the selection is collapsed. The handles can be
/// dragged to adjust the selection.
///
/// See also:
///
/// * [showCursor], which controls the visibility of the cursor..
final
bool
showSelectionHandles
;
/// {@template flutter.widgets.editableText.showCursor}
/// Whether to show cursor.
///
/// The cursor refers to the blinking caret when the [EditableText] is focused.
///
/// See also:
///
/// * [showSelectionHandles], which controls the visibility of the selection handles..
/// {@endtemplate}
final
bool
showCursor
;
/// {@template flutter.widgets.editableText.autocorrect}
/// Whether to enable autocorrection.
///
...
...
@@ -822,6 +863,18 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
Color
get
_cursorColor
=>
widget
.
cursorColor
.
withOpacity
(
_cursorBlinkOpacityController
.
value
);
@override
bool
get
cutEnabled
=>
!
widget
.
readOnly
;
@override
bool
get
copyEnabled
=>
true
;
@override
bool
get
pasteEnabled
=>
!
widget
.
readOnly
;
@override
bool
get
selectAllEnabled
=>
true
;
// State lifecycle:
@override
...
...
@@ -836,6 +889,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
_cursorBlinkOpacityController
.
addListener
(
_onCursorColorTick
);
_floatingCursorResetController
=
AnimationController
(
vsync:
this
);
_floatingCursorResetController
.
addListener
(
_onFloatingCursorResetTick
);
_cursorVisibilityNotifier
.
value
=
widget
.
showCursor
;
}
@override
...
...
@@ -855,6 +909,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
widget
.
controller
.
addListener
(
_didChangeTextEditingValue
);
_updateRemoteEditingValueIfNeeded
();
}
if
(
widget
.
controller
.
selection
!=
oldWidget
.
controller
.
selection
)
{
_selectionOverlay
?.
update
(
_value
);
}
_selectionOverlay
?.
handlesVisible
=
widget
.
showSelectionHandles
;
if
(
widget
.
focusNode
!=
oldWidget
.
focusNode
)
{
oldWidget
.
focusNode
.
removeListener
(
_handleFocusChanged
);
_focusAttachment
?.
detach
();
...
...
@@ -862,6 +920,12 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
widget
.
focusNode
.
addListener
(
_handleFocusChanged
);
updateKeepAlive
();
}
if
(
widget
.
readOnly
)
{
_closeInputConnectionIfNeeded
();
}
else
{
if
(
oldWidget
.
readOnly
&&
_hasFocus
)
_openInputConnection
();
}
}
@override
...
...
@@ -886,6 +950,11 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
@override
void
updateEditingValue
(
TextEditingValue
value
)
{
// Since we still have to support keyboard select, this is the best place
// to disable text updating.
if
(
widget
.
readOnly
)
{
return
;
}
if
(
value
.
text
!=
_value
.
text
)
{
_hideSelectionOverlayIfNeeded
();
_showCaretOnScreen
();
...
...
@@ -1067,6 +1136,9 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
bool
get
_hasInputConnection
=>
_textInputConnection
!=
null
&&
_textInputConnection
.
attached
;
void
_openInputConnection
()
{
if
(
widget
.
readOnly
)
{
return
;
}
if
(!
_hasInputConnection
)
{
final
TextEditingValue
localValue
=
_value
;
_lastKnownRemoteTextEditingValue
=
localValue
;
...
...
@@ -1156,7 +1228,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
dragStartBehavior:
widget
.
dragStartBehavior
,
onSelectionHandleTapped:
widget
.
onSelectionHandleTapped
,
);
_selectionOverlay
.
handlesVisible
=
widget
.
showSelectionHandles
;
_selectionOverlay
.
showHandles
();
if
(
widget
.
onSelectionChanged
!=
null
)
widget
.
onSelectionChanged
(
selection
,
cause
);
}
...
...
@@ -1239,7 +1312,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
void
_onCursorColorTick
()
{
renderEditable
.
cursorColor
=
widget
.
cursorColor
.
withOpacity
(
_cursorBlinkOpacityController
.
value
);
_cursorVisibilityNotifier
.
value
=
_cursorBlinkOpacityController
.
value
>
0
;
_cursorVisibilityNotifier
.
value
=
widget
.
showCursor
&&
_cursorBlinkOpacityController
.
value
>
0
;
}
/// Whether the blinking cursor is actually visible at this precise moment
...
...
@@ -1409,26 +1482,20 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
}
}
/// Shows the handles at the location of the current selection.
void
showHandles
()
{
assert
(
_selectionOverlay
!=
null
);
_selectionOverlay
.
showHandles
();
}
VoidCallback
_semanticsOnCopy
(
TextSelectionControls
controls
)
{
return
widget
.
selectionEnabled
&&
_hasFocus
&&
controls
?.
canCopy
(
this
)
==
true
return
widget
.
selectionEnabled
&&
copyEnabled
&&
_hasFocus
&&
controls
?.
canCopy
(
this
)
==
true
?
()
=>
controls
.
handleCopy
(
this
)
:
null
;
}
VoidCallback
_semanticsOnCut
(
TextSelectionControls
controls
)
{
return
widget
.
selectionEnabled
&&
_hasFocus
&&
controls
?.
canCut
(
this
)
==
true
return
widget
.
selectionEnabled
&&
cutEnabled
&&
_hasFocus
&&
controls
?.
canCut
(
this
)
==
true
?
()
=>
controls
.
handleCut
(
this
)
:
null
;
}
VoidCallback
_semanticsOnPaste
(
TextSelectionControls
controls
)
{
return
widget
.
selectionEnabled
&&
_hasFocus
&&
controls
?.
canPaste
(
this
)
==
true
return
widget
.
selectionEnabled
&&
pasteEnabled
&&
_hasFocus
&&
controls
?.
canPaste
(
this
)
==
true
?
()
=>
controls
.
handlePaste
(
this
)
:
null
;
}
...
...
@@ -1460,7 +1527,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
cursorColor:
_cursorColor
,
backgroundCursorColor:
widget
.
backgroundCursorColor
,
showCursor:
EditableText
.
debugDeterministicCursor
?
ValueNotifier
<
bool
>(
true
)
?
ValueNotifier
<
bool
>(
widget
.
showCursor
)
:
_cursorVisibilityNotifier
,
hasFocus:
_hasFocus
,
maxLines:
widget
.
maxLines
,
...
...
@@ -1497,11 +1564,11 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
/// By default makes text in composing range appear as underlined.
/// Descendants can override this method to customize appearance of text.
TextSpan
buildTextSpan
()
{
if
(!
widget
.
obscureText
&&
_value
.
composing
.
isValid
)
{
// Read only mode should not paint text composing.
if
(!
widget
.
obscureText
&&
_value
.
composing
.
isValid
&&
!
widget
.
readOnly
)
{
final
TextStyle
composingStyle
=
widget
.
style
.
merge
(
const
TextStyle
(
decoration:
TextDecoration
.
underline
),
);
return
TextSpan
(
style:
widget
.
style
,
children:
<
TextSpan
>[
...
...
packages/flutter/lib/src/widgets/text_selection.dart
View file @
730025fa
...
...
@@ -20,6 +20,7 @@ import 'gesture_detector.dart';
import
'overlay.dart'
;
import
'ticker_provider.dart'
;
import
'transitions.dart'
;
import
'visibility.dart'
;
export
'package:flutter/services.dart'
show
TextSelectionDelegate
;
...
...
@@ -130,7 +131,7 @@ abstract class TextSelectionControls {
/// Subclasses can use this to decide if they should expose the cut
/// functionality to the user.
bool
canCut
(
TextSelectionDelegate
delegate
)
{
return
!
delegate
.
textEditingValue
.
selection
.
isCollapsed
;
return
delegate
.
cutEnabled
&&
!
delegate
.
textEditingValue
.
selection
.
isCollapsed
;
}
/// Whether the current selection of the text field managed by the given
...
...
@@ -141,7 +142,7 @@ abstract class TextSelectionControls {
/// Subclasses can use this to decide if they should expose the copy
/// functionality to the user.
bool
canCopy
(
TextSelectionDelegate
delegate
)
{
return
!
delegate
.
textEditingValue
.
selection
.
isCollapsed
;
return
delegate
.
copyEnabled
&&
!
delegate
.
textEditingValue
.
selection
.
isCollapsed
;
}
/// Whether the current [Clipboard] content can be pasted into the text field
...
...
@@ -151,7 +152,7 @@ abstract class TextSelectionControls {
/// functionality to the user.
bool
canPaste
(
TextSelectionDelegate
delegate
)
{
// TODO(goderbauer): return false when clipboard is empty, https://github.com/flutter/flutter/issues/11254
return
true
;
return
delegate
.
pasteEnabled
;
}
/// Whether the current selection of the text field managed by the given
...
...
@@ -161,7 +162,7 @@ abstract class TextSelectionControls {
/// Subclasses can use this to decide if they should expose the select all
/// functionality to the user.
bool
canSelectAll
(
TextSelectionDelegate
delegate
)
{
return
delegate
.
textEditingValue
.
text
.
isNotEmpty
&&
delegate
.
textEditingValue
.
selection
.
isCollapsed
;
return
delegate
.
selectAllEnabled
&&
delegate
.
textEditingValue
.
text
.
isNotEmpty
&&
delegate
.
textEditingValue
.
selection
.
isCollapsed
;
}
/// Copy the current selection of the text field managed by the given
...
...
@@ -267,11 +268,14 @@ class TextSelectionOverlay {
@required
this
.
layerLink
,
@required
this
.
renderObject
,
this
.
selectionControls
,
bool
handlesVisible
=
false
,
this
.
selectionDelegate
,
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
onSelectionHandleTapped
,
})
:
assert
(
value
!=
null
),
assert
(
context
!=
null
),
assert
(
handlesVisible
!=
null
),
_handlesVisible
=
handlesVisible
,
_value
=
value
{
final
OverlayState
overlay
=
Overlay
.
of
(
context
);
assert
(
overlay
!=
null
,
...
...
@@ -337,6 +341,10 @@ class TextSelectionOverlay {
AnimationController
_toolbarController
;
Animation
<
double
>
get
_toolbarOpacity
=>
_toolbarController
.
view
;
/// Retrieve current value.
@visibleForTesting
TextEditingValue
get
value
=>
_value
;
TextEditingValue
_value
;
/// A pair of handles. If this is non-null, there are always 2, though the
...
...
@@ -348,16 +356,58 @@ class TextSelectionOverlay {
TextSelection
get
_selection
=>
_value
.
selection
;
/// Shows the handles by inserting them into the [context]'s overlay.
/// Whether selection handles are visible.
///
/// Set to false if you want to hide the handles. Use this property to show or
/// hide the handle without rebuilding them.
///
/// If this method is called while the [SchedulerBinding.schedulerPhase] is
/// [SchedulerPhase.persistentCallbacks], i.e. during the build, layout, or
/// paint phases (see [WidgetsBinding.drawFrame]), then the update is delayed
/// until the post-frame callbacks phase. Otherwise the update is done
/// synchronously. This means that it is safe to call during builds, but also
/// that if you do call this during a build, the UI will not update until the
/// next frame (i.e. many milliseconds later).
///
/// Defaults to false.
bool
get
handlesVisible
=>
_handlesVisible
;
bool
_handlesVisible
=
false
;
set
handlesVisible
(
bool
visible
)
{
assert
(
visible
!=
null
);
if
(
_handlesVisible
==
visible
)
return
;
_handlesVisible
=
visible
;
// If we are in build state, it will be too late to update visibility.
// We will need to schedule the build in next frame.
if
(
SchedulerBinding
.
instance
.
schedulerPhase
==
SchedulerPhase
.
persistentCallbacks
)
{
SchedulerBinding
.
instance
.
addPostFrameCallback
(
_markNeedsBuild
);
}
else
{
_markNeedsBuild
();
}
}
/// Builds the handles by inserting them into the [context]'s overlay.
void
showHandles
()
{
assert
(
_handles
==
null
);
_handles
=
<
OverlayEntry
>[
OverlayEntry
(
builder:
(
BuildContext
context
)
=>
_buildHandle
(
context
,
_TextSelectionHandlePosition
.
start
)),
OverlayEntry
(
builder:
(
BuildContext
context
)
=>
_buildHandle
(
context
,
_TextSelectionHandlePosition
.
end
)),
];
Overlay
.
of
(
context
,
debugRequiredFor:
debugRequiredFor
).
insertAll
(
_handles
);
}
/// Destroys the handles by removing them from overlay.
void
hideHandles
()
{
if
(
_handles
!=
null
)
{
_handles
[
0
].
remove
();
_handles
[
1
].
remove
();
_handles
=
null
;
}
}
/// Shows the toolbar by inserting it into the [context]'s overlay.
void
showToolbar
()
{
assert
(
_toolbar
==
null
);
...
...
@@ -403,7 +453,7 @@ class TextSelectionOverlay {
}
/// Whether the handles are currently visible.
bool
get
handlesAreVisible
=>
_handles
!=
null
;
bool
get
handlesAreVisible
=>
_handles
!=
null
&&
handlesVisible
;
/// Whether the toolbar is currently visible.
bool
get
toolbarIsVisible
=>
_toolbar
!=
null
;
...
...
@@ -440,16 +490,18 @@ class TextSelectionOverlay {
if
((
_selection
.
isCollapsed
&&
position
==
_TextSelectionHandlePosition
.
end
)
||
selectionControls
==
null
)
return
Container
();
// hide the second handle when collapsed
return
_TextSelectionHandleOverlay
(
onSelectionHandleChanged:
(
TextSelection
newSelection
)
{
_handleSelectionHandleChanged
(
newSelection
,
position
);
},
onSelectionHandleTapped:
onSelectionHandleTapped
,
layerLink:
layerLink
,
renderObject:
renderObject
,
selection:
_selection
,
selectionControls:
selectionControls
,
position:
position
,
dragStartBehavior:
dragStartBehavior
,
);
return
Visibility
(
visible:
handlesVisible
,
child:
_TextSelectionHandleOverlay
(
onSelectionHandleChanged:
(
TextSelection
newSelection
)
{
_handleSelectionHandleChanged
(
newSelection
,
position
);
},
onSelectionHandleTapped:
onSelectionHandleTapped
,
layerLink:
layerLink
,
renderObject:
renderObject
,
selection:
_selection
,
selectionControls:
selectionControls
,
position:
position
,
dragStartBehavior:
dragStartBehavior
,
));
}
Widget
_buildToolbar
(
BuildContext
context
)
{
...
...
packages/flutter/test/cupertino/text_field_test.dart
View file @
730025fa
...
...
@@ -1151,6 +1151,43 @@ void main() {
expect
(
text
.
style
.
fontWeight
,
FontWeight
.
w300
);
});
testWidgets
(
'Read only text field'
,
(
WidgetTester
tester
)
async
{
final
TextEditingController
controller
=
TextEditingController
(
text:
'readonly'
);
await
tester
.
pumpWidget
(
CupertinoApp
(
home:
Column
(
children:
<
Widget
>[
CupertinoTextField
(
controller:
controller
,
readOnly:
true
,
),
],
),
),
);
// Read only text field cannot open keyboard.
await
tester
.
showKeyboard
(
find
.
byType
(
CupertinoTextField
));
expect
(
tester
.
testTextInput
.
hasAnyClients
,
false
);
await
tester
.
longPressAt
(
tester
.
getTopRight
(
find
.
text
(
'readonly'
))
);
await
tester
.
pump
();
expect
(
find
.
text
(
'Paste'
),
findsNothing
);
expect
(
find
.
text
(
'Cut'
),
findsNothing
);
expect
(
find
.
text
(
'Select All'
),
findsOneWidget
);
await
tester
.
tap
(
find
.
text
(
'Select All'
));
await
tester
.
pump
();
expect
(
find
.
text
(
'Copy'
),
findsOneWidget
);
expect
(
find
.
text
(
'Paste'
),
findsNothing
);
expect
(
find
.
text
(
'Cut'
),
findsNothing
);
});
testWidgets
(
'copy paste'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
CupertinoApp
(
...
...
@@ -2015,7 +2052,7 @@ void main() {
expect
(
controller
.
selection
.
isCollapsed
,
isTrue
);
expect
(
controller
.
selection
.
baseOffset
,
4
);
await
tester
.
tapAt
(
ePos
,
pointer:
7
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
50
)
);
await
tester
.
pump
AndSettle
(
);
expect
(
controller
.
selection
.
baseOffset
,
4
);
expect
(
controller
.
selection
.
extentOffset
,
7
);
...
...
@@ -2035,7 +2072,6 @@ void main() {
await
tester
.
pump
();
await
gesture
.
moveTo
(
newHandlePos
);
await
tester
.
pump
();
expect
(
controller
.
selection
.
baseOffset
,
4
);
expect
(
controller
.
selection
.
extentOffset
,
5
);
...
...
packages/flutter/test/material/text_field_test.dart
View file @
730025fa
This diff is collapsed.
Click to expand it.
packages/flutter/test/material/text_form_field_test.dart
View file @
730025fa
...
...
@@ -5,6 +5,9 @@
import
'package:flutter/material.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/rendering.dart'
;
import
'../rendering/mock_canvas.dart'
;
void
main
(
)
{
testWidgets
(
'Passes textAlign to underlying TextField'
,
(
WidgetTester
tester
)
async
{
...
...
@@ -213,4 +216,46 @@ void main() {
expect
(
find
.
text
(
'5 of 10'
),
findsOneWidget
);
});
testWidgets
(
'readonly text form field will hide cursor by default'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Material
(
child:
Center
(
child:
TextFormField
(
initialValue:
'readonly'
,
readOnly:
true
,
),
),
),
),
);
await
tester
.
showKeyboard
(
find
.
byType
(
TextFormField
));
expect
(
tester
.
testTextInput
.
hasAnyClients
,
false
);
await
tester
.
tap
(
find
.
byType
(
TextField
));
await
tester
.
pump
();
expect
(
tester
.
testTextInput
.
hasAnyClients
,
false
);
await
tester
.
longPress
(
find
.
byType
(
TextFormField
));
await
tester
.
pump
();
// Context menu should not have paste.
expect
(
find
.
text
(
'SELECT ALL'
),
findsOneWidget
);
expect
(
find
.
text
(
'PASTE'
),
findsNothing
);
final
EditableTextState
editableTextState
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
final
RenderEditable
renderEditable
=
editableTextState
.
renderEditable
;
// Make sure it does not paint caret for a period of time.
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
expect
(
renderEditable
,
paintsExactlyCountTimes
(
#drawRect
,
0
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
expect
(
renderEditable
,
paintsExactlyCountTimes
(
#drawRect
,
0
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
expect
(
renderEditable
,
paintsExactlyCountTimes
(
#drawRect
,
0
));
});
}
packages/flutter/test/widgets/editable_text_cursor_test.dart
View file @
730025fa
...
...
@@ -318,6 +318,34 @@ void main() {
EditableText
.
debugDeterministicCursor
=
false
;
});
testWidgets
(
'Cursor does not show when showCursor set to false'
,
(
WidgetTester
tester
)
async
{
const
Widget
widget
=
MaterialApp
(
home:
Material
(
child:
TextField
(
showCursor:
false
,
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
;
// Make sure it does not paint for a period of time.
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
expect
(
renderEditable
,
paintsExactlyCountTimes
(
#drawRect
,
0
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
expect
(
renderEditable
,
paintsExactlyCountTimes
(
#drawRect
,
0
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
expect
(
renderEditable
,
paintsExactlyCountTimes
(
#drawRect
,
0
));
});
testWidgets
(
'Cursor radius is 2.0 on iOS'
,
(
WidgetTester
tester
)
async
{
final
Widget
widget
=
MaterialApp
(
theme:
ThemeData
(
platform:
TargetPlatform
.
iOS
),
...
...
packages/flutter/test/widgets/editable_text_test.dart
View file @
730025fa
...
...
@@ -1893,6 +1893,7 @@ void main() {
child:
SizedBox
(
width:
100
,
child:
EditableText
(
showSelectionHandles:
true
,
controller:
controller
,
focusNode:
FocusNode
(),
style:
Typography
(
platform:
TargetPlatform
.
android
).
black
.
subhead
,
...
...
@@ -2003,7 +2004,7 @@ void main() {
throw
TestFailure
(
'HandlePositionInViewport can
\'
t be null.'
);
}
}
expect
(
state
.
selectionOverlay
.
handlesAreVisible
,
isTrue
);
testPosition
(
container
[
0
].
offset
.
dx
,
leftPosition
);
testPosition
(
container
[
1
].
offset
.
dx
,
rightPosition
);
}
...
...
@@ -2011,7 +2012,6 @@ void main() {
// Select the first word. Both handles should be visible.
await
tester
.
tapAt
(
const
Offset
(
20
,
10
));
renderEditable
.
selectWord
(
cause:
SelectionChangedCause
.
longPress
);
state
.
showHandles
();
await
tester
.
pump
();
await
verifyVisibility
(
HandlePositionInViewport
.
leftEdge
,
true
,
HandlePositionInViewport
.
within
,
true
);
...
...
@@ -2033,7 +2033,6 @@ void main() {
// Now that the second word has been dragged fully into view, select it.
await
tester
.
tapAt
(
const
Offset
(
80
,
10
));
renderEditable
.
selectWord
(
cause:
SelectionChangedCause
.
longPress
);
state
.
showHandles
();
await
tester
.
pump
();
await
verifyVisibility
(
HandlePositionInViewport
.
within
,
true
,
HandlePositionInViewport
.
within
,
true
);
...
...
@@ -2060,6 +2059,7 @@ void main() {
width:
100
,
child:
EditableText
(
controller:
controller
,
showSelectionHandles:
true
,
focusNode:
FocusNode
(),
style:
Typography
(
platform:
TargetPlatform
.
android
).
black
.
subhead
,
cursorColor:
Colors
.
blue
,
...
...
@@ -2078,7 +2078,6 @@ void main() {
// Select the first word. Both handles should be visible.
await
tester
.
tapAt
(
const
Offset
(
20
,
10
));
state
.
renderEditable
.
selectWord
(
cause:
SelectionChangedCause
.
longPress
);
state
.
showHandles
();
await
tester
.
pump
();
final
List
<
CompositedTransformFollower
>
container
=
find
.
byType
(
CompositedTransformFollower
)
...
...
@@ -2100,6 +2099,7 @@ void main() {
70.0
+
kMinInteractiveSize
,
),
);
expect
(
state
.
selectionOverlay
.
handlesAreVisible
,
isTrue
);
expect
(
controller
.
selection
.
base
.
offset
,
0
);
expect
(
controller
.
selection
.
extent
.
offset
,
5
);
});
...
...
@@ -2119,6 +2119,7 @@ void main() {
child:
SizedBox
(
width:
100
,
child:
EditableText
(
showSelectionHandles:
true
,
controller:
controller
,
focusNode:
FocusNode
(),
style:
Typography
(
platform:
TargetPlatform
.
iOS
).
black
.
subhead
,
...
...
@@ -2228,7 +2229,7 @@ void main() {
throw
TestFailure
(
'HandlePositionInViewport can
\'
t be null.'
);
}
}
expect
(
state
.
selectionOverlay
.
handlesAreVisible
,
isTrue
);
testPosition
(
container
[
0
].
offset
.
dx
,
leftPosition
);
testPosition
(
container
[
1
].
offset
.
dx
,
rightPosition
);
}
...
...
@@ -2236,7 +2237,6 @@ void main() {
// Select the first word. Both handles should be visible.
await
tester
.
tapAt
(
const
Offset
(
20
,
10
));
renderEditable
.
selectWord
(
cause:
SelectionChangedCause
.
longPress
);
state
.
showHandles
();
await
tester
.
pump
();
await
verifyVisibility
(
HandlePositionInViewport
.
leftEdge
,
true
,
HandlePositionInViewport
.
within
,
true
);
...
...
@@ -2258,7 +2258,6 @@ void main() {
// Now that the second word has been dragged fully into view, select it.
await
tester
.
tapAt
(
const
Offset
(
80
,
10
));
renderEditable
.
selectWord
(
cause:
SelectionChangedCause
.
longPress
);
state
.
showHandles
();
await
tester
.
pump
();
await
verifyVisibility
(
HandlePositionInViewport
.
within
,
true
,
HandlePositionInViewport
.
within
,
true
);
...
...
packages/flutter_test/lib/src/test_text_input.dart
View file @
730025fa
...
...
@@ -58,6 +58,9 @@ class TestTextInput {
bool
get
isRegistered
=>
_isRegistered
;
bool
_isRegistered
=
false
;
/// Whether there are any active clients listening to text input.
bool
get
hasAnyClients
=>
_client
>
0
;
int
_client
=
0
;
/// Arguments supplied to the TextInput.setClient method call.
...
...
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