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
d2a2a5cf
Unverified
Commit
d2a2a5cf
authored
Jan 29, 2019
by
jslavitz
Committed by
GitHub
Jan 29, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Adds a fade in and out, rounds corners, fixes offset and fixes height of cursor on iOS (#24876)
* Fixes cursor on iOS devices
parent
9f20bd6c
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
977 additions
and
363 deletions
+977
-363
goldens.version
bin/internal/goldens.version
+1
-1
button.dart
packages/flutter/lib/src/cupertino/button.dart
+5
-0
text_field.dart
packages/flutter/lib/src/cupertino/text_field.dart
+13
-1
text_field.dart
packages/flutter/lib/src/material/text_field.dart
+39
-3
editable.dart
packages/flutter/lib/src/rendering/editable.dart
+109
-13
editable_text.dart
packages/flutter/lib/src/widgets/editable_text.dart
+109
-8
text_field_test.dart
packages/flutter/test/cupertino/text_field_test.dart
+67
-0
text_field_test.dart
packages/flutter/test/material/text_field_test.dart
+70
-7
editable_test.dart
packages/flutter/test/rendering/editable_test.dart
+31
-12
editable_text_show_on_screen_test.dart
...utter/test/widgets/editable_text_show_on_screen_test.dart
+24
-19
editable_text_test.dart
packages/flutter/test/widgets/editable_text_test.dart
+386
-207
form_test.dart
packages/flutter/test/widgets/form_test.dart
+111
-84
physical_model_test.dart
packages/flutter/test/widgets/physical_model_test.dart
+12
-8
No files found.
bin/internal/goldens.version
View file @
d2a2a5cf
6fc7ec65d51116c3f83acb5251e57e779af2ebbb
b530d67675a5aa9c5458b93019ce91e20ad88758
packages/flutter/lib/src/cupertino/button.dart
View file @
d2a2a5cf
...
@@ -110,6 +110,11 @@ class CupertinoButton extends StatefulWidget {
...
@@ -110,6 +110,11 @@ class CupertinoButton extends StatefulWidget {
/// Defaults to round corners of 8 logical pixels.
/// Defaults to round corners of 8 logical pixels.
final
BorderRadius
borderRadius
;
final
BorderRadius
borderRadius
;
/// The shape of the button.
///
/// Defaults to a super ellipse with
// final ShapeBorder shape;
final
bool
_filled
;
final
bool
_filled
;
/// Whether the button is enabled or disabled. Buttons are disabled by default. To
/// Whether the button is enabled or disabled. Buttons are disabled by default. To
...
...
packages/flutter/lib/src/cupertino/text_field.dart
View file @
d2a2a5cf
...
@@ -35,6 +35,14 @@ const Color _kSelectionHighlightColor = Color(0x667FAACF);
...
@@ -35,6 +35,14 @@ const Color _kSelectionHighlightColor = Color(0x667FAACF);
const
Color
_kInactiveTextColor
=
Color
(
0xFFC2C2C2
);
const
Color
_kInactiveTextColor
=
Color
(
0xFFC2C2C2
);
const
Color
_kDisabledBackground
=
Color
(
0xFFFAFAFA
);
const
Color
_kDisabledBackground
=
Color
(
0xFFFAFAFA
);
// An eyeballed value that moves the cursor slightly left of where it is
// rendered for text on Android so it's positioning more accurately matches the
// native iOS text cursor positioning.
//
// This value is in device pixels, not logical pixels as is typically used
// throughout the codebase.
const
int
_iOSHorizontalCursorOffsetPixels
=
-
2
;
/// Visibility of text field overlays based on the state of the current text entry.
/// Visibility of text field overlays based on the state of the current text entry.
///
///
/// Used to toggle the visibility behavior of the optional decorating widgets
/// Used to toggle the visibility behavior of the optional decorating widgets
...
@@ -163,7 +171,7 @@ class CupertinoTextField extends StatefulWidget {
...
@@ -163,7 +171,7 @@ class CupertinoTextField extends StatefulWidget {
this
.
inputFormatters
,
this
.
inputFormatters
,
this
.
enabled
,
this
.
enabled
,
this
.
cursorWidth
=
2.0
,
this
.
cursorWidth
=
2.0
,
this
.
cursorRadius
,
this
.
cursorRadius
=
const
Radius
.
circular
(
2.0
)
,
this
.
cursorColor
=
CupertinoColors
.
activeBlue
,
this
.
cursorColor
=
CupertinoColors
.
activeBlue
,
this
.
keyboardAppearance
,
this
.
keyboardAppearance
,
this
.
scrollPadding
=
const
EdgeInsets
.
all
(
20.0
),
this
.
scrollPadding
=
const
EdgeInsets
.
all
(
20.0
),
...
@@ -598,6 +606,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
...
@@ -598,6 +606,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
final
TextEditingController
controller
=
_effectiveController
;
final
TextEditingController
controller
=
_effectiveController
;
final
List
<
TextInputFormatter
>
formatters
=
widget
.
inputFormatters
??
<
TextInputFormatter
>[];
final
List
<
TextInputFormatter
>
formatters
=
widget
.
inputFormatters
??
<
TextInputFormatter
>[];
final
bool
enabled
=
widget
.
enabled
??
true
;
final
bool
enabled
=
widget
.
enabled
??
true
;
final
Offset
cursorOffset
=
Offset
(
_iOSHorizontalCursorOffsetPixels
/
MediaQuery
.
of
(
context
).
devicePixelRatio
,
0
);
if
(
widget
.
maxLength
!=
null
&&
widget
.
maxLengthEnforced
)
{
if
(
widget
.
maxLength
!=
null
&&
widget
.
maxLengthEnforced
)
{
formatters
.
add
(
LengthLimitingTextInputFormatter
(
widget
.
maxLength
));
formatters
.
add
(
LengthLimitingTextInputFormatter
(
widget
.
maxLength
));
}
}
...
@@ -631,6 +640,9 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
...
@@ -631,6 +640,9 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
cursorWidth:
widget
.
cursorWidth
,
cursorWidth:
widget
.
cursorWidth
,
cursorRadius:
widget
.
cursorRadius
,
cursorRadius:
widget
.
cursorRadius
,
cursorColor:
widget
.
cursorColor
,
cursorColor:
widget
.
cursorColor
,
cursorOpacityAnimates:
true
,
cursorOffset:
cursorOffset
,
paintCursorAboveText:
true
,
backgroundCursorColor:
CupertinoColors
.
inactiveGray
,
backgroundCursorColor:
CupertinoColors
.
inactiveGray
,
scrollPadding:
widget
.
scrollPadding
,
scrollPadding:
widget
.
scrollPadding
,
keyboardAppearance:
keyboardAppearance
,
keyboardAppearance:
keyboardAppearance
,
...
...
packages/flutter/lib/src/material/text_field.dart
View file @
d2a2a5cf
...
@@ -8,6 +8,7 @@ import 'package:flutter/cupertino.dart';
...
@@ -8,6 +8,7 @@ import 'package:flutter/cupertino.dart';
import
'package:flutter/rendering.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/gestures.dart'
;
import
'debug.dart'
;
import
'debug.dart'
;
...
@@ -36,6 +37,14 @@ typedef InputCounterWidgetBuilder = Widget Function(
...
@@ -36,6 +37,14 @@ typedef InputCounterWidgetBuilder = Widget Function(
}
}
);
);
// An eyeballed value that moves the cursor slightly left of where it is
// rendered for text on Android so it's positioning more accurately matches the
// native iOS text cursor positioning.
//
// This value is in device pixels, not logical pixels as is typically used
// throughout the codebase.
const
int
_iOSHorizontalCursorOffsetPixels
=
2
;
/// A material design text field.
/// A material design text field.
///
///
/// A text field lets the user enter text, either with hardware keyboard or with
/// A text field lets the user enter text, either with hardware keyboard or with
...
@@ -469,6 +478,14 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
...
@@ -469,6 +478,14 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
&&
widget
.
decoration
!=
null
&&
widget
.
decoration
!=
null
&&
widget
.
decoration
.
counterText
==
null
;
&&
widget
.
decoration
.
counterText
==
null
;
Radius
get
_cursorRadius
{
if
(
widget
.
cursorRadius
!=
null
)
return
widget
.
cursorRadius
;
if
(
Theme
.
of
(
context
).
platform
==
TargetPlatform
.
iOS
)
return
const
Radius
.
circular
(
2.0
);
return
null
;
}
InputDecoration
_getEffectiveDecoration
()
{
InputDecoration
_getEffectiveDecoration
()
{
final
MaterialLocalizations
localizations
=
MaterialLocalizations
.
of
(
context
);
final
MaterialLocalizations
localizations
=
MaterialLocalizations
.
of
(
context
);
final
ThemeData
themeData
=
Theme
.
of
(
context
);
final
ThemeData
themeData
=
Theme
.
of
(
context
);
...
@@ -682,6 +699,22 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
...
@@ -682,6 +699,22 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
@override
@override
bool
get
wantKeepAlive
=>
_splashes
!=
null
&&
_splashes
.
isNotEmpty
;
bool
get
wantKeepAlive
=>
_splashes
!=
null
&&
_splashes
.
isNotEmpty
;
bool
get
_cursorOpacityAnimates
=>
Theme
.
of
(
context
).
platform
==
TargetPlatform
.
iOS
?
true
:
false
;
Offset
get
_getCursorOffset
=>
Offset
(
_iOSHorizontalCursorOffsetPixels
/
MediaQuery
.
of
(
context
).
devicePixelRatio
,
0
);
bool
get
_paintCursorAboveText
=>
Theme
.
of
(
context
).
platform
==
TargetPlatform
.
iOS
?
true
:
false
;
Color
get
_cursorColor
{
if
(
widget
.
cursorColor
==
null
)
{
if
(
Theme
.
of
(
context
).
platform
==
TargetPlatform
.
iOS
)
return
CupertinoTheme
.
of
(
context
).
primaryColor
;
else
return
Theme
.
of
(
context
).
cursorColor
;
}
return
widget
.
cursorColor
;
}
@override
@override
void
deactivate
()
{
void
deactivate
()
{
if
(
_splashes
!=
null
)
{
if
(
_splashes
!=
null
)
{
...
@@ -704,7 +737,7 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
...
@@ -704,7 +737,7 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
assert
(
debugCheckHasDirectionality
(
context
));
assert
(
debugCheckHasDirectionality
(
context
));
assert
(
assert
(
!(
widget
.
style
!=
null
&&
widget
.
style
.
inherit
==
false
&&
!(
widget
.
style
!=
null
&&
widget
.
style
.
inherit
==
false
&&
(
widget
.
style
.
fontSize
==
null
||
widget
.
style
.
textBaseline
==
null
)),
(
widget
.
style
.
fontSize
==
null
||
widget
.
style
.
textBaseline
==
null
)),
'inherit false style must supply fontSize and textBaseline'
,
'inherit false style must supply fontSize and textBaseline'
,
);
);
...
@@ -755,8 +788,11 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
...
@@ -755,8 +788,11 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
inputFormatters:
formatters
,
inputFormatters:
formatters
,
rendererIgnoresPointer:
true
,
rendererIgnoresPointer:
true
,
cursorWidth:
widget
.
cursorWidth
,
cursorWidth:
widget
.
cursorWidth
,
cursorRadius:
widget
.
cursorRadius
,
cursorRadius:
_cursorRadius
,
cursorColor:
widget
.
cursorColor
??
themeData
.
cursorColor
,
cursorColor:
_cursorColor
,
cursorOpacityAnimates:
_cursorOpacityAnimates
,
cursorOffset:
_getCursorOffset
,
paintCursorAboveText:
_paintCursorAboveText
,
backgroundCursorColor:
CupertinoColors
.
inactiveGray
,
backgroundCursorColor:
CupertinoColors
.
inactiveGray
,
scrollPadding:
widget
.
scrollPadding
,
scrollPadding:
widget
.
scrollPadding
,
keyboardAppearance:
keyboardAppearance
,
keyboardAppearance:
keyboardAppearance
,
...
...
packages/flutter/lib/src/rendering/editable.dart
View file @
d2a2a5cf
...
@@ -21,7 +21,7 @@ const double _kCaretHeightOffset = 2.0; // pixels
...
@@ -21,7 +21,7 @@ const double _kCaretHeightOffset = 2.0; // pixels
// The additional size on the x and y axis with which to expand the prototype
// The additional size on the x and y axis with which to expand the prototype
// cursor to render the floating cursor in pixels.
// cursor to render the floating cursor in pixels.
const
Offset
_kFloatingCaretSizeIncrease
=
Offset
(
0.5
,
2
.0
);
const
Offset
_kFloatingCaretSizeIncrease
=
Offset
(
0.5
,
1
.0
);
// The corner radius of the floating cursor in pixels.
// The corner radius of the floating cursor in pixels.
const
double
_kFloatingCaretRadius
=
1.0
;
const
double
_kFloatingCaretRadius
=
1.0
;
...
@@ -149,8 +149,11 @@ class RenderEditable extends RenderBox {
...
@@ -149,8 +149,11 @@ class RenderEditable extends RenderBox {
Locale
locale
,
Locale
locale
,
double
cursorWidth
=
1.0
,
double
cursorWidth
=
1.0
,
Radius
cursorRadius
,
Radius
cursorRadius
,
bool
paintCursorAboveText
=
false
,
Offset
cursorOffset
,
double
devicePixelRatio
=
1.0
,
bool
enableInteractiveSelection
,
bool
enableInteractiveSelection
,
EdgeInsets
floatingCursorAddedMargin
=
const
EdgeInsets
.
fromLTRB
(
3
,
6
,
3
,
6
),
EdgeInsets
floatingCursorAddedMargin
=
const
EdgeInsets
.
fromLTRB
(
4
,
4
,
4
,
5
),
@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.'
),
...
@@ -158,9 +161,11 @@ class RenderEditable extends RenderBox {
...
@@ -158,9 +161,11 @@ class RenderEditable extends RenderBox {
assert
(
textScaleFactor
!=
null
),
assert
(
textScaleFactor
!=
null
),
assert
(
offset
!=
null
),
assert
(
offset
!=
null
),
assert
(
ignorePointer
!=
null
),
assert
(
ignorePointer
!=
null
),
assert
(
paintCursorAboveText
!=
null
),
assert
(
obscureText
!=
null
),
assert
(
obscureText
!=
null
),
assert
(
textSelectionDelegate
!=
null
),
assert
(
textSelectionDelegate
!=
null
),
assert
(
cursorWidth
!=
null
&&
cursorWidth
>=
0.0
),
assert
(
cursorWidth
!=
null
&&
cursorWidth
>=
0.0
),
assert
(
devicePixelRatio
!=
null
),
_textPainter
=
TextPainter
(
_textPainter
=
TextPainter
(
text:
text
,
text:
text
,
textAlign:
textAlign
,
textAlign:
textAlign
,
...
@@ -178,8 +183,11 @@ class RenderEditable extends RenderBox {
...
@@ -178,8 +183,11 @@ class RenderEditable extends RenderBox {
_offset
=
offset
,
_offset
=
offset
,
_cursorWidth
=
cursorWidth
,
_cursorWidth
=
cursorWidth
,
_cursorRadius
=
cursorRadius
,
_cursorRadius
=
cursorRadius
,
_paintCursorOnTop
=
paintCursorAboveText
,
_cursorOffset
=
cursorOffset
,
_floatingCursorAddedMargin
=
floatingCursorAddedMargin
,
_floatingCursorAddedMargin
=
floatingCursorAddedMargin
,
_enableInteractiveSelection
=
enableInteractiveSelection
,
_enableInteractiveSelection
=
enableInteractiveSelection
,
_devicePixelRatio
=
devicePixelRatio
,
_obscureText
=
obscureText
{
_obscureText
=
obscureText
{
assert
(
_showCursor
!=
null
);
assert
(
_showCursor
!=
null
);
assert
(!
_showCursor
.
value
||
cursorColor
!=
null
);
assert
(!
_showCursor
.
value
||
cursorColor
!=
null
);
...
@@ -208,6 +216,18 @@ class RenderEditable extends RenderBox {
...
@@ -208,6 +216,18 @@ class RenderEditable extends RenderBox {
/// The default value of this property is false.
/// The default value of this property is false.
bool
ignorePointer
;
bool
ignorePointer
;
/// The pixel ratio of the current device.
///
/// Should be obtained by querying MediaQuery for the devicePixelRatio.
double
get
devicePixelRatio
=>
_devicePixelRatio
;
double
_devicePixelRatio
;
set
devicePixelRatio
(
double
value
)
{
if
(
devicePixelRatio
==
value
)
return
;
_devicePixelRatio
=
value
;
markNeedsTextLayout
();
}
/// Whether to hide the text being edited (e.g., for passwords).
/// Whether to hide the text being edited (e.g., for passwords).
bool
get
obscureText
=>
_obscureText
;
bool
get
obscureText
=>
_obscureText
;
bool
_obscureText
;
bool
_obscureText
;
...
@@ -719,6 +739,38 @@ class RenderEditable extends RenderBox {
...
@@ -719,6 +739,38 @@ class RenderEditable extends RenderBox {
markNeedsLayout
();
markNeedsLayout
();
}
}
///{@template flutter.rendering.editable.paintCursorOnTop}
/// If the cursor should be painted on top of the text or underneath it.
///
/// By default, the cursor should be painted on top for iOS platforms and
/// underneath for Android platforms.
/// {@end template}
bool
get
paintCursorAboveText
=>
_paintCursorOnTop
;
bool
_paintCursorOnTop
;
set
paintCursorAboveText
(
bool
value
)
{
if
(
_paintCursorOnTop
==
value
)
return
;
_paintCursorOnTop
=
value
;
markNeedsLayout
();
}
/// {@template flutter.rendering.editable.cursorOffset}
/// The offset that is used, in pixels, when painting the cursor on screen.
///
/// By default, the cursor position should be set to an offset of
/// (-[cursorWidth] * 0.5, 0.0) on iOS platforms and (0, 0) on Android
/// platforms. The origin from where the offset is applied to is the arbitrary
/// location where the cursor ends up being rendered from by default.
/// {@end template}
Offset
get
cursorOffset
=>
_cursorOffset
;
Offset
_cursorOffset
;
set
cursorOffset
(
Offset
value
)
{
if
(
_cursorOffset
==
value
)
return
;
_cursorOffset
=
value
;
markNeedsLayout
();
}
/// How rounded the corners of the cursor should be.
/// How rounded the corners of the cursor should be.
Radius
get
cursorRadius
=>
_cursorRadius
;
Radius
get
cursorRadius
=>
_cursorRadius
;
Radius
_cursorRadius
;
Radius
_cursorRadius
;
...
@@ -732,7 +784,7 @@ class RenderEditable extends RenderBox {
...
@@ -732,7 +784,7 @@ class RenderEditable extends RenderBox {
/// The padding applied to text field. Used to determine the bounds when
/// The padding applied to text field. Used to determine the bounds when
/// moving the floating cursor.
/// moving the floating cursor.
///
///
/// Defaults to a padding with left,
right set to 3 and top, bottom to 6
.
/// Defaults to a padding with left,
top and right set to 4, bottom to 5
.
EdgeInsets
get
floatingCursorAddedMargin
=>
_floatingCursorAddedMargin
;
EdgeInsets
get
floatingCursorAddedMargin
=>
_floatingCursorAddedMargin
;
EdgeInsets
_floatingCursorAddedMargin
;
EdgeInsets
_floatingCursorAddedMargin
;
set
floatingCursorAddedMargin
(
EdgeInsets
value
)
{
set
floatingCursorAddedMargin
(
EdgeInsets
value
)
{
...
@@ -1055,7 +1107,12 @@ class RenderEditable extends RenderBox {
...
@@ -1055,7 +1107,12 @@ class RenderEditable extends RenderBox {
_layoutText
(
constraints
.
maxWidth
);
_layoutText
(
constraints
.
maxWidth
);
final
Offset
caretOffset
=
_textPainter
.
getOffsetForCaret
(
caretPosition
,
_caretPrototype
);
final
Offset
caretOffset
=
_textPainter
.
getOffsetForCaret
(
caretPosition
,
_caretPrototype
);
// This rect is the same as _caretPrototype but without the vertical padding.
// This rect is the same as _caretPrototype but without the vertical padding.
return
Rect
.
fromLTWH
(
0.0
,
0.0
,
cursorWidth
,
preferredLineHeight
).
shift
(
caretOffset
+
_paintOffset
);
Rect
rect
=
Rect
.
fromLTWH
(
0.0
,
0.0
,
cursorWidth
,
preferredLineHeight
).
shift
(
caretOffset
+
_paintOffset
);
// Add additional cursor offset (generally only if on iOS).
if
(
_cursorOffset
!=
null
)
rect
=
rect
.
shift
(
_cursorOffset
);
return
rect
.
shift
(
_getPixelPerfectCursorOffset
(
rect
));
}
}
@override
@override
...
@@ -1262,10 +1319,21 @@ class RenderEditable extends RenderBox {
...
@@ -1262,10 +1319,21 @@ class RenderEditable extends RenderBox {
_textLayoutLastWidth
=
constraintWidth
;
_textLayoutLastWidth
=
constraintWidth
;
}
}
/// On iOS, the cursor is taller than the the cursor on Android. The height
/// of the cursor for iOS is approximate and obtained through an eyeball
/// comparison.
Rect
get
_getCaretPrototype
{
switch
(
defaultTargetPlatform
){
case
TargetPlatform
.
iOS
:
return
Rect
.
fromLTWH
(
0.0
,
-
_kCaretHeightOffset
+
.
5
,
cursorWidth
,
preferredLineHeight
+
2
);
default
:
return
Rect
.
fromLTWH
(
0.0
,
_kCaretHeightOffset
,
cursorWidth
,
preferredLineHeight
-
2.0
*
_kCaretHeightOffset
);
}
}
@override
@override
void
performLayout
()
{
void
performLayout
()
{
_layoutText
(
constraints
.
maxWidth
);
_layoutText
(
constraints
.
maxWidth
);
_caretPrototype
=
Rect
.
fromLTWH
(
0.0
,
_kCaretHeightOffset
,
cursorWidth
,
preferredLineHeight
-
2.0
*
_kCaretHeightOffset
)
;
_caretPrototype
=
_getCaretPrototype
;
_selectionRects
=
null
;
_selectionRects
=
null
;
// We grab _textPainter.size here because assigning to `size` on the next
// We grab _textPainter.size here because assigning to `size` on the next
// line will trigger us to validate our intrinsic sizes, which will change
// line will trigger us to validate our intrinsic sizes, which will change
...
@@ -1283,15 +1351,30 @@ class RenderEditable extends RenderBox {
...
@@ -1283,15 +1351,30 @@ class RenderEditable extends RenderBox {
offset
.
applyContentDimensions
(
0.0
,
_maxScrollExtent
);
offset
.
applyContentDimensions
(
0.0
,
_maxScrollExtent
);
}
}
Offset
_getPixelPerfectCursorOffset
(
Rect
caretRect
)
{
final
Offset
caretPosition
=
localToGlobal
(
caretRect
.
topLeft
);
final
double
pixelMultiple
=
1.0
/
_devicePixelRatio
;
final
int
quotientX
=
(
caretPosition
.
dx
/
pixelMultiple
).
round
();
final
int
quotientY
=
(
caretPosition
.
dy
/
pixelMultiple
).
round
();
final
double
pixelPerfectOffsetX
=
quotientX
*
pixelMultiple
-
caretPosition
.
dx
;
final
double
pixelPerfectOffsetY
=
quotientY
*
pixelMultiple
-
caretPosition
.
dy
;
return
Offset
(
pixelPerfectOffsetX
,
pixelPerfectOffsetY
);
}
void
_paintCaret
(
Canvas
canvas
,
Offset
effectiveOffset
,
TextPosition
textPosition
)
{
void
_paintCaret
(
Canvas
canvas
,
Offset
effectiveOffset
,
TextPosition
textPosition
)
{
assert
(
_textLayoutLastWidth
==
constraints
.
maxWidth
);
assert
(
_textLayoutLastWidth
==
constraints
.
maxWidth
);
final
Offset
caretOffset
=
_textPainter
.
getOffsetForCaret
(
textPosition
,
_caretPrototype
);
final
Offset
caretOffset
=
_textPainter
.
getOffsetForCaret
(
textPosition
,
_caretPrototype
);
// If the floating cursor is enabled, the text cursor's color is [backgroundCursorColor] while
// If the floating cursor is enabled, the text cursor's color is [backgroundCursorColor] while
// the floating cursor's color is _cursorColor;
// the floating cursor's color is _cursorColor;
final
Paint
paint
=
Paint
()
final
Paint
paint
=
Paint
()
..
color
=
_floatingCursorOn
?
backgroundCursorColor
:
_cursorColor
;
..
color
=
_floatingCursorOn
?
backgroundCursorColor
:
_cursorColor
;
final
Rect
caretRect
=
_caretPrototype
.
shift
(
caretOffset
+
effectiveOffset
);
Rect
caretRect
=
_caretPrototype
.
shift
(
caretOffset
+
effectiveOffset
);
if
(
_cursorOffset
!=
null
)
caretRect
=
caretRect
.
shift
(
_cursorOffset
);
caretRect
=
caretRect
.
shift
(
_getPixelPerfectCursorOffset
(
caretRect
));
if
(
cursorRadius
==
null
)
{
if
(
cursorRadius
==
null
)
{
canvas
.
drawRect
(
caretRect
,
paint
);
canvas
.
drawRect
(
caretRect
,
paint
);
...
@@ -1334,7 +1417,8 @@ class RenderEditable extends RenderBox {
...
@@ -1334,7 +1417,8 @@ class RenderEditable extends RenderBox {
assert
(
_textLayoutLastWidth
==
constraints
.
maxWidth
);
assert
(
_textLayoutLastWidth
==
constraints
.
maxWidth
);
assert
(
_floatingCursorOn
);
assert
(
_floatingCursorOn
);
final
Paint
paint
=
Paint
()..
color
=
_cursorColor
;
// We always want the floating cursor to render at full opacity.
final
Paint
paint
=
Paint
()..
color
=
_cursorColor
.
withOpacity
(
0.75
);
double
sizeAdjustmentX
=
_kFloatingCaretSizeIncrease
.
dx
;
double
sizeAdjustmentX
=
_kFloatingCaretSizeIncrease
.
dx
;
double
sizeAdjustmentY
=
_kFloatingCaretSizeIncrease
.
dy
;
double
sizeAdjustmentY
=
_kFloatingCaretSizeIncrease
.
dy
;
...
@@ -1344,10 +1428,13 @@ class RenderEditable extends RenderBox {
...
@@ -1344,10 +1428,13 @@ class RenderEditable extends RenderBox {
sizeAdjustmentY
=
ui
.
lerpDouble
(
sizeAdjustmentY
,
0
,
_resetFloatingCursorAnimationValue
);
sizeAdjustmentY
=
ui
.
lerpDouble
(
sizeAdjustmentY
,
0
,
_resetFloatingCursorAnimationValue
);
}
}
final
Rect
floatingCaretPrototype
=
Rect
.
fromLTRB
(
_caretPrototype
.
left
-
sizeAdjustmentX
,
final
Rect
floatingCaretPrototype
=
Rect
.
fromLTRB
(
_caretPrototype
.
top
-
sizeAdjustmentY
,
_caretPrototype
.
left
-
sizeAdjustmentX
,
_caretPrototype
.
right
+
sizeAdjustmentX
,
_caretPrototype
.
top
-
sizeAdjustmentY
,
_caretPrototype
.
bottom
+
sizeAdjustmentY
);
_caretPrototype
.
right
+
sizeAdjustmentX
,
_caretPrototype
.
bottom
+
sizeAdjustmentY
);
final
Rect
caretRect
=
floatingCaretPrototype
.
shift
(
effectiveOffset
);
final
Rect
caretRect
=
floatingCaretPrototype
.
shift
(
effectiveOffset
);
const
Radius
floatingCursorRadius
=
Radius
.
circular
(
_kFloatingCaretRadius
);
const
Radius
floatingCursorRadius
=
Radius
.
circular
(
_kFloatingCaretRadius
);
final
RRect
caretRRect
=
RRect
.
fromRectAndRadius
(
caretRect
,
floatingCursorRadius
);
final
RRect
caretRRect
=
RRect
.
fromRectAndRadius
(
caretRect
,
floatingCursorRadius
);
...
@@ -1424,15 +1511,24 @@ class RenderEditable extends RenderBox {
...
@@ -1424,15 +1511,24 @@ class RenderEditable extends RenderBox {
void
_paintContents
(
PaintingContext
context
,
Offset
offset
)
{
void
_paintContents
(
PaintingContext
context
,
Offset
offset
)
{
assert
(
_textLayoutLastWidth
==
constraints
.
maxWidth
);
assert
(
_textLayoutLastWidth
==
constraints
.
maxWidth
);
final
Offset
effectiveOffset
=
offset
+
_paintOffset
;
final
Offset
effectiveOffset
=
offset
+
_paintOffset
;
// On iOS, the cursor is painted over the text, on Android, it's painted
// under it.
if
(
paintCursorAboveText
)
_textPainter
.
paint
(
context
.
canvas
,
effectiveOffset
);
if
(
_selection
!=
null
&&
!
_floatingCursorOn
)
{
if
(
_selection
!=
null
&&
!
_floatingCursorOn
)
{
if
(
_selection
.
isCollapsed
&&
_showCursor
.
value
&&
cursorColor
!=
null
)
{
if
(
_selection
.
isCollapsed
&&
cursorColor
!=
null
&&
_hasFocus
)
{
_paintCaret
(
context
.
canvas
,
effectiveOffset
,
_selection
.
extent
);
_paintCaret
(
context
.
canvas
,
effectiveOffset
,
_selection
.
extent
);
}
else
if
(!
_selection
.
isCollapsed
&&
_selectionColor
!=
null
)
{
}
else
if
(!
_selection
.
isCollapsed
&&
_selectionColor
!=
null
)
{
_selectionRects
??=
_textPainter
.
getBoxesForSelection
(
_selection
);
_selectionRects
??=
_textPainter
.
getBoxesForSelection
(
_selection
);
_paintSelection
(
context
.
canvas
,
effectiveOffset
);
_paintSelection
(
context
.
canvas
,
effectiveOffset
);
}
}
}
}
_textPainter
.
paint
(
context
.
canvas
,
effectiveOffset
);
if
(!
paintCursorAboveText
)
_textPainter
.
paint
(
context
.
canvas
,
effectiveOffset
);
if
(
_floatingCursorOn
)
{
if
(
_floatingCursorOn
)
{
if
(
_resetFloatingCursorAnimationValue
==
null
)
if
(
_resetFloatingCursorAnimationValue
==
null
)
_paintCaret
(
context
.
canvas
,
effectiveOffset
,
_floatingCursorTextPosition
);
_paintCaret
(
context
.
canvas
,
effectiveOffset
,
_floatingCursorTextPosition
);
...
...
packages/flutter/lib/src/widgets/editable_text.dart
View file @
d2a2a5cf
...
@@ -5,6 +5,7 @@
...
@@ -5,6 +5,7 @@
import
'dart:async'
;
import
'dart:async'
;
import
'dart:ui'
as
ui
;
import
'dart:ui'
as
ui
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/scheduler.dart'
;
import
'package:flutter/scheduler.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter/services.dart'
;
...
@@ -31,8 +32,15 @@ export 'package:flutter/rendering.dart' show SelectionChangedCause;
...
@@ -31,8 +32,15 @@ export 'package:flutter/rendering.dart' show SelectionChangedCause;
/// (including the cursor location).
/// (including the cursor location).
typedef
SelectionChangedCallback
=
void
Function
(
TextSelection
selection
,
SelectionChangedCause
cause
);
typedef
SelectionChangedCallback
=
void
Function
(
TextSelection
selection
,
SelectionChangedCause
cause
);
// The time it takes for the cursor to fade from fully opaque to fully
// transparent and vice versa. A full cursor blink, from transparent to opaque
// to transparent, is twice this duration.
const
Duration
_kCursorBlinkHalfPeriod
=
Duration
(
milliseconds:
500
);
const
Duration
_kCursorBlinkHalfPeriod
=
Duration
(
milliseconds:
500
);
// The time the cursor is static in opacity before animating to become
// transparent.
const
Duration
_kCursorBlinkWaitForStart
=
Duration
(
milliseconds:
150
);
// Number of cursor ticks during which the most recently entered character
// Number of cursor ticks during which the most recently entered character
// is shown in an obscured text field.
// is shown in an obscured text field.
const
int
_kObscureShowLatestCharCursorTicks
=
3
;
const
int
_kObscureShowLatestCharCursorTicks
=
3
;
...
@@ -215,6 +223,9 @@ class EditableText extends StatefulWidget {
...
@@ -215,6 +223,9 @@ class EditableText extends StatefulWidget {
this
.
rendererIgnoresPointer
=
false
,
this
.
rendererIgnoresPointer
=
false
,
this
.
cursorWidth
=
2.0
,
this
.
cursorWidth
=
2.0
,
this
.
cursorRadius
,
this
.
cursorRadius
,
this
.
cursorOpacityAnimates
=
false
,
this
.
cursorOffset
,
this
.
paintCursorAboveText
=
false
,
this
.
scrollPadding
=
const
EdgeInsets
.
all
(
20.0
),
this
.
scrollPadding
=
const
EdgeInsets
.
all
(
20.0
),
this
.
keyboardAppearance
=
Brightness
.
light
,
this
.
keyboardAppearance
=
Brightness
.
light
,
this
.
dragStartBehavior
=
DragStartBehavior
.
down
,
this
.
dragStartBehavior
=
DragStartBehavior
.
down
,
...
@@ -225,6 +236,8 @@ class EditableText extends StatefulWidget {
...
@@ -225,6 +236,8 @@ class EditableText extends StatefulWidget {
assert
(
autocorrect
!=
null
),
assert
(
autocorrect
!=
null
),
assert
(
style
!=
null
),
assert
(
style
!=
null
),
assert
(
cursorColor
!=
null
),
assert
(
cursorColor
!=
null
),
assert
(
cursorOpacityAnimates
!=
null
),
assert
(
paintCursorAboveText
!=
null
),
assert
(
backgroundCursorColor
!=
null
),
assert
(
backgroundCursorColor
!=
null
),
assert
(
textAlign
!=
null
),
assert
(
textAlign
!=
null
),
assert
(
maxLines
==
null
||
maxLines
>
0
),
assert
(
maxLines
==
null
||
maxLines
>
0
),
...
@@ -471,6 +484,19 @@ class EditableText extends StatefulWidget {
...
@@ -471,6 +484,19 @@ class EditableText extends StatefulWidget {
/// {@endtemplate}
/// {@endtemplate}
final
Radius
cursorRadius
;
final
Radius
cursorRadius
;
/// Whether the cursor will animate from fully transparent to fully opaque
/// during each cursor blink.
///
/// By default, the cursor opacity will animate on iOS platforms and will not
/// animate on Android platforms.
final
bool
cursorOpacityAnimates
;
///{@macro flutter.rendering.editable.cursorOffset}
final
Offset
cursorOffset
;
///{@macro flutter.rendering.editable.paintCursorOnTop}
final
bool
paintCursorAboveText
;
/// The appearance of the keyboard.
/// The appearance of the keyboard.
///
///
/// This setting is only honored on iOS devices.
/// This setting is only honored on iOS devices.
...
@@ -546,9 +572,15 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -546,9 +572,15 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
TextSelectionOverlay
_selectionOverlay
;
TextSelectionOverlay
_selectionOverlay
;
final
ScrollController
_scrollController
=
ScrollController
();
final
ScrollController
_scrollController
=
ScrollController
();
AnimationController
_cursorBlinkOpacityController
;
final
LayerLink
_layerLink
=
LayerLink
();
final
LayerLink
_layerLink
=
LayerLink
();
bool
_didAutoFocus
=
false
;
bool
_didAutoFocus
=
false
;
// This value is an eyeball estimation of the time it takes for the iOS cursor
// to ease in and out.
static
const
Duration
_fadeDuration
=
Duration
(
milliseconds:
250
);
// The time it takes for the floating cursor to snap to the text aligned
// The time it takes for the floating cursor to snap to the text aligned
// cursor position after the user has finished placing it.
// cursor position after the user has finished placing it.
static
const
Duration
_floatingCursorResetTime
=
Duration
(
milliseconds:
125
);
static
const
Duration
_floatingCursorResetTime
=
Duration
(
milliseconds:
125
);
...
@@ -558,6 +590,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -558,6 +590,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
@override
@override
bool
get
wantKeepAlive
=>
widget
.
focusNode
.
hasFocus
;
bool
get
wantKeepAlive
=>
widget
.
focusNode
.
hasFocus
;
Color
get
_cursorColor
=>
widget
.
cursorColor
.
withOpacity
(
_cursorBlinkOpacityController
.
value
);
// State lifecycle:
// State lifecycle:
@override
@override
...
@@ -566,6 +600,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -566,6 +600,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
widget
.
controller
.
addListener
(
_didChangeTextEditingValue
);
widget
.
controller
.
addListener
(
_didChangeTextEditingValue
);
widget
.
focusNode
.
addListener
(
_handleFocusChanged
);
widget
.
focusNode
.
addListener
(
_handleFocusChanged
);
_scrollController
.
addListener
(()
{
_selectionOverlay
?.
updateForScroll
();
});
_scrollController
.
addListener
(()
{
_selectionOverlay
?.
updateForScroll
();
});
_cursorBlinkOpacityController
=
AnimationController
(
vsync:
this
,
duration:
_fadeDuration
);
_cursorBlinkOpacityController
.
addListener
(
_onCursorColorTick
);
_floatingCursorResetController
=
AnimationController
(
vsync:
this
);
_floatingCursorResetController
=
AnimationController
(
vsync:
this
);
_floatingCursorResetController
.
addListener
(
_onFloatingCursorResetTick
);
_floatingCursorResetController
.
addListener
(
_onFloatingCursorResetTick
);
}
}
...
@@ -597,6 +633,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -597,6 +633,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
@override
@override
void
dispose
()
{
void
dispose
()
{
widget
.
controller
.
removeListener
(
_didChangeTextEditingValue
);
widget
.
controller
.
removeListener
(
_didChangeTextEditingValue
);
_cursorBlinkOpacityController
.
removeListener
(
_onCursorColorTick
);
_floatingCursorResetController
.
removeListener
(
_onFloatingCursorResetTick
);
_closeInputConnectionIfNeeded
();
_closeInputConnectionIfNeeded
();
assert
(!
_hasInputConnection
);
assert
(!
_hasInputConnection
);
_stopCursorTimer
();
_stopCursorTimer
();
...
@@ -623,6 +661,11 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -623,6 +661,11 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
}
}
_lastKnownRemoteTextEditingValue
=
value
;
_lastKnownRemoteTextEditingValue
=
value
;
_formatAndSetValue
(
value
);
_formatAndSetValue
(
value
);
// To keep the cursor from blinking while typing, we want to restart the
// cursor timer every time a new character is typed.
_stopCursorTimer
(
resetCharTicks:
false
);
_startCursorTimer
();
}
}
@override
@override
...
@@ -696,7 +739,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -696,7 +739,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
}
}
void
_onFloatingCursorResetTick
()
{
void
_onFloatingCursorResetTick
()
{
final
Offset
finalPosition
=
renderEditable
.
getLocalRectForCaret
(
_lastTextPosition
).
center
-
_floatingCursorOffset
;
final
Offset
finalPosition
=
renderEditable
.
getLocalRectForCaret
(
_lastTextPosition
).
center
Left
-
_floatingCursorOffset
;
if
(
_floatingCursorResetController
.
isCompleted
)
{
if
(
_floatingCursorResetController
.
isCompleted
)
{
renderEditable
.
setFloatingCursor
(
FloatingCursorDragState
.
End
,
finalPosition
,
_lastTextPosition
);
renderEditable
.
setFloatingCursor
(
FloatingCursorDragState
.
End
,
finalPosition
,
_lastTextPosition
);
if
(
_lastTextPosition
.
offset
!=
renderEditable
.
selection
.
baseOffset
)
if
(
_lastTextPosition
.
offset
!=
renderEditable
.
selection
.
baseOffset
)
...
@@ -957,6 +1000,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -957,6 +1000,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
widget
.
onChanged
(
value
.
text
);
widget
.
onChanged
(
value
.
text
);
}
}
void
_onCursorColorTick
()
{
renderEditable
.
cursorColor
=
widget
.
cursorColor
.
withOpacity
(
_cursorBlinkOpacityController
.
value
);
}
/// Whether the blinking cursor is actually visible at this precise moment
/// Whether the blinking cursor is actually visible at this precise moment
/// (it's hidden half the time, since it blinks).
/// (it's hidden half the time, since it blinks).
@visibleForTesting
@visibleForTesting
...
@@ -977,21 +1024,58 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -977,21 +1024,58 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
void
_cursorTick
(
Timer
timer
)
{
void
_cursorTick
(
Timer
timer
)
{
_showCursor
.
value
=
!
_showCursor
.
value
;
_showCursor
.
value
=
!
_showCursor
.
value
;
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
// curve is used for the animation to mimic the aesthetics of the native
// iOS cursor.
//
// 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
);
}
else
{
_cursorBlinkOpacityController
.
value
=
_showCursor
.
value
?
1.0
:
0.0
;
}
if
(
_obscureShowCharTicksPending
>
0
)
{
if
(
_obscureShowCharTicksPending
>
0
)
{
setState
(()
{
_obscureShowCharTicksPending
--;
});
setState
(()
{
_obscureShowCharTicksPending
--;
});
}
}
}
}
void
_cursorWaitForStart
(
Timer
timer
)
{
assert
(
_kCursorBlinkHalfPeriod
>
_fadeDuration
);
_cursorTimer
?.
cancel
();
_cursorTimer
=
Timer
.
periodic
(
_kCursorBlinkHalfPeriod
,
_cursorTick
);
}
void
_startCursorTimer
()
{
void
_startCursorTimer
()
{
_showCursor
.
value
=
true
;
_showCursor
.
value
=
true
;
_cursorTimer
=
Timer
.
periodic
(
_kCursorBlinkHalfPeriod
,
_cursorTick
);
_cursorBlinkOpacityController
.
value
=
1.0
;
if
(
EditableText
.
debugDeterministicCursor
)
return
;
if
(
widget
.
cursorOpacityAnimates
)
{
_cursorTimer
=
Timer
.
periodic
(
_kCursorBlinkWaitForStart
,
_cursorWaitForStart
);
}
else
{
_cursorTimer
=
Timer
.
periodic
(
_kCursorBlinkHalfPeriod
,
_cursorTick
);
}
}
}
void
_stopCursorTimer
()
{
void
_stopCursorTimer
(
{
bool
resetCharTicks
=
true
}
)
{
_cursorTimer
?.
cancel
();
_cursorTimer
?.
cancel
();
_cursorTimer
=
null
;
_cursorTimer
=
null
;
_showCursor
.
value
=
false
;
_showCursor
.
value
=
false
;
_obscureShowCharTicksPending
=
0
;
_cursorBlinkOpacityController
.
value
=
0.0
;
if
(
EditableText
.
debugDeterministicCursor
)
return
;
if
(
resetCharTicks
)
_obscureShowCharTicksPending
=
0
;
if
(
widget
.
cursorOpacityAnimates
)
{
_cursorBlinkOpacityController
.
stop
();
_cursorBlinkOpacityController
.
value
=
0.0
;
}
}
}
void
_startOrStopCursorTimerIfNeeded
()
{
void
_startOrStopCursorTimerIfNeeded
()
{
...
@@ -1047,6 +1131,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -1047,6 +1131,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
@override
@override
TextEditingValue
get
textEditingValue
=>
_value
;
TextEditingValue
get
textEditingValue
=>
_value
;
double
get
_devicePixelRatio
=>
MediaQuery
.
of
(
context
).
devicePixelRatio
??
1.0
;
@override
@override
set
textEditingValue
(
TextEditingValue
value
)
{
set
textEditingValue
(
TextEditingValue
value
)
{
_selectionOverlay
?.
update
(
value
);
_selectionOverlay
?.
update
(
value
);
...
@@ -1104,7 +1190,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -1104,7 +1190,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
key:
_editableKey
,
key:
_editableKey
,
textSpan:
buildTextSpan
(),
textSpan:
buildTextSpan
(),
value:
_value
,
value:
_value
,
cursorColor:
widget
.
cursorColor
,
cursorColor:
_
cursorColor
,
backgroundCursorColor:
widget
.
backgroundCursorColor
,
backgroundCursorColor:
widget
.
backgroundCursorColor
,
showCursor:
EditableText
.
debugDeterministicCursor
?
ValueNotifier
<
bool
>(
true
)
:
_showCursor
,
showCursor:
EditableText
.
debugDeterministicCursor
?
ValueNotifier
<
bool
>(
true
)
:
_showCursor
,
hasFocus:
_hasFocus
,
hasFocus:
_hasFocus
,
...
@@ -1122,8 +1208,11 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -1122,8 +1208,11 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
rendererIgnoresPointer:
widget
.
rendererIgnoresPointer
,
rendererIgnoresPointer:
widget
.
rendererIgnoresPointer
,
cursorWidth:
widget
.
cursorWidth
,
cursorWidth:
widget
.
cursorWidth
,
cursorRadius:
widget
.
cursorRadius
,
cursorRadius:
widget
.
cursorRadius
,
cursorOffset:
widget
.
cursorOffset
,
paintCursorAboveText:
widget
.
paintCursorAboveText
,
enableInteractiveSelection:
widget
.
enableInteractiveSelection
,
enableInteractiveSelection:
widget
.
enableInteractiveSelection
,
textSelectionDelegate:
this
,
textSelectionDelegate:
this
,
devicePixelRatio:
_devicePixelRatio
,
),
),
),
),
);
);
...
@@ -1188,8 +1277,11 @@ class _Editable extends LeafRenderObjectWidget {
...
@@ -1188,8 +1277,11 @@ class _Editable extends LeafRenderObjectWidget {
this
.
rendererIgnoresPointer
=
false
,
this
.
rendererIgnoresPointer
=
false
,
this
.
cursorWidth
,
this
.
cursorWidth
,
this
.
cursorRadius
,
this
.
cursorRadius
,
this
.
enableInteractiveSelection
,
this
.
cursorOffset
,
this
.
enableInteractiveSelection
=
true
,
this
.
textSelectionDelegate
,
this
.
textSelectionDelegate
,
this
.
paintCursorAboveText
,
this
.
devicePixelRatio
})
:
assert
(
textDirection
!=
null
),
})
:
assert
(
textDirection
!=
null
),
assert
(
rendererIgnoresPointer
!=
null
),
assert
(
rendererIgnoresPointer
!=
null
),
super
(
key:
key
);
super
(
key:
key
);
...
@@ -1214,8 +1306,11 @@ class _Editable extends LeafRenderObjectWidget {
...
@@ -1214,8 +1306,11 @@ class _Editable extends LeafRenderObjectWidget {
final
bool
rendererIgnoresPointer
;
final
bool
rendererIgnoresPointer
;
final
double
cursorWidth
;
final
double
cursorWidth
;
final
Radius
cursorRadius
;
final
Radius
cursorRadius
;
final
Offset
cursorOffset
;
final
bool
enableInteractiveSelection
;
final
bool
enableInteractiveSelection
;
final
TextSelectionDelegate
textSelectionDelegate
;
final
TextSelectionDelegate
textSelectionDelegate
;
final
double
devicePixelRatio
;
final
bool
paintCursorAboveText
;
@override
@override
RenderEditable
createRenderObject
(
BuildContext
context
)
{
RenderEditable
createRenderObject
(
BuildContext
context
)
{
...
@@ -1239,8 +1334,11 @@ class _Editable extends LeafRenderObjectWidget {
...
@@ -1239,8 +1334,11 @@ class _Editable extends LeafRenderObjectWidget {
obscureText:
obscureText
,
obscureText:
obscureText
,
cursorWidth:
cursorWidth
,
cursorWidth:
cursorWidth
,
cursorRadius:
cursorRadius
,
cursorRadius:
cursorRadius
,
cursorOffset:
cursorOffset
,
paintCursorAboveText:
paintCursorAboveText
,
enableInteractiveSelection:
enableInteractiveSelection
,
enableInteractiveSelection:
enableInteractiveSelection
,
textSelectionDelegate:
textSelectionDelegate
,
textSelectionDelegate:
textSelectionDelegate
,
devicePixelRatio:
devicePixelRatio
,
);
);
}
}
...
@@ -1265,6 +1363,9 @@ class _Editable extends LeafRenderObjectWidget {
...
@@ -1265,6 +1363,9 @@ class _Editable extends LeafRenderObjectWidget {
..
obscureText
=
obscureText
..
obscureText
=
obscureText
..
cursorWidth
=
cursorWidth
..
cursorWidth
=
cursorWidth
..
cursorRadius
=
cursorRadius
..
cursorRadius
=
cursorRadius
..
textSelectionDelegate
=
textSelectionDelegate
;
..
cursorOffset
=
cursorOffset
..
textSelectionDelegate
=
textSelectionDelegate
..
devicePixelRatio
=
devicePixelRatio
..
paintCursorAboveText
=
paintCursorAboveText
;
}
}
}
}
packages/flutter/test/cupertino/text_field_test.dart
View file @
d2a2a5cf
...
@@ -7,6 +7,7 @@ import 'dart:async';
...
@@ -7,6 +7,7 @@ import 'dart:async';
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
class
MockClipboard
{
class
MockClipboard
{
...
@@ -143,6 +144,72 @@ void main() {
...
@@ -143,6 +144,72 @@ void main() {
},
},
);
);
testWidgets
(
'iOS cursor has offset'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
CupertinoApp
(
home:
CupertinoTextField
(),
),
);
final
EditableText
editableText
=
tester
.
firstWidget
(
find
.
byType
(
EditableText
));
expect
(
editableText
.
cursorOffset
,
const
Offset
(-
2.0
/
3.0
,
0
));
});
testWidgets
(
'Cursor animates on iOS'
,
(
WidgetTester
tester
)
async
{
debugDefaultTargetPlatformOverride
=
TargetPlatform
.
iOS
;
await
tester
.
pumpWidget
(
const
CupertinoApp
(
home:
CupertinoTextField
(),
),
);
final
Finder
textFinder
=
find
.
byType
(
CupertinoTextField
);
await
tester
.
tap
(
textFinder
);
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:
400
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
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:
50
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
0
);
debugDefaultTargetPlatformOverride
=
null
;
});
testWidgets
(
'Cursor radius is 2.0 on iOS'
,
(
WidgetTester
tester
)
async
{
debugDefaultTargetPlatformOverride
=
TargetPlatform
.
iOS
;
await
tester
.
pumpWidget
(
const
CupertinoApp
(
home:
CupertinoTextField
(),
),
);
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
(
'can control text content via controller'
,
'can control text content via controller'
,
(
WidgetTester
tester
)
async
{
(
WidgetTester
tester
)
async
{
...
...
packages/flutter/test/material/text_field_test.dart
View file @
d2a2a5cf
...
@@ -277,6 +277,65 @@ void main() {
...
@@ -277,6 +277,65 @@ void main() {
await
checkCursorToggle
();
await
checkCursorToggle
();
});
});
testWidgets
(
'Cursor animates on iOS'
,
(
WidgetTester
tester
)
async
{
debugDefaultTargetPlatformOverride
=
TargetPlatform
.
iOS
;
await
tester
.
pumpWidget
(
const
MaterialApp
(
home:
Material
(
child:
TextField
(),
),
),
);
final
Finder
textFinder
=
find
.
byType
(
TextField
);
await
tester
.
tap
(
textFinder
);
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:
400
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
255
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
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:
50
));
expect
(
renderEditable
.
cursorColor
.
alpha
,
0
);
debugDefaultTargetPlatformOverride
=
null
;
});
testWidgets
(
'Cursor radius is 2.0 on iOS'
,
(
WidgetTester
tester
)
async
{
debugDefaultTargetPlatformOverride
=
TargetPlatform
.
iOS
;
await
tester
.
pumpWidget
(
const
MaterialApp
(
home:
Material
(
child:
TextField
(),
),
),
);
final
EditableTextState
editableTextState
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
final
RenderEditable
renderEditable
=
editableTextState
.
renderEditable
;
expect
(
renderEditable
.
cursorRadius
,
const
Radius
.
circular
(
2.0
));
debugDefaultTargetPlatformOverride
=
null
;
});
testWidgets
(
'cursor has expected defaults'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'cursor has expected defaults'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
overlay
(
overlay
(
...
@@ -305,6 +364,7 @@ void main() {
...
@@ -305,6 +364,7 @@ void main() {
});
});
testWidgets
(
'cursor layout has correct width'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'cursor layout has correct width'
,
(
WidgetTester
tester
)
async
{
EditableText
.
debugDeterministicCursor
=
true
;
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
overlay
(
overlay
(
child:
const
RepaintBoundary
(
child:
const
RepaintBoundary
(
...
@@ -321,9 +381,11 @@ void main() {
...
@@ -321,9 +381,11 @@ void main() {
find
.
byType
(
TextField
),
find
.
byType
(
TextField
),
matchesGoldenFile
(
'text_field_test.0.0.png'
),
matchesGoldenFile
(
'text_field_test.0.0.png'
),
);
);
EditableText
.
debugDeterministicCursor
=
false
;
},
skip:
!
Platform
.
isLinux
);
},
skip:
!
Platform
.
isLinux
);
testWidgets
(
'cursor layout has correct radius'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'cursor layout has correct radius'
,
(
WidgetTester
tester
)
async
{
EditableText
.
debugDeterministicCursor
=
true
;
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
overlay
(
overlay
(
child:
const
RepaintBoundary
(
child:
const
RepaintBoundary
(
...
@@ -341,6 +403,7 @@ void main() {
...
@@ -341,6 +403,7 @@ void main() {
find
.
byType
(
TextField
),
find
.
byType
(
TextField
),
matchesGoldenFile
(
'text_field_test.1.0.png'
),
matchesGoldenFile
(
'text_field_test.1.0.png'
),
);
);
EditableText
.
debugDeterministicCursor
=
false
;
},
skip:
!
Platform
.
isLinux
);
},
skip:
!
Platform
.
isLinux
);
testWidgets
(
'obscureText control test'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'obscureText control test'
,
(
WidgetTester
tester
)
async
{
...
@@ -1469,7 +1532,7 @@ void main() {
...
@@ -1469,7 +1532,7 @@ void main() {
editable
.
getLocalRectForCaret
(
const
TextPosition
(
offset:
0
)).
topLeft
,
editable
.
getLocalRectForCaret
(
const
TextPosition
(
offset:
0
)).
topLeft
,
);
);
expect
(
topLeft
.
dx
,
equals
(
398.5
));
expect
(
topLeft
.
dx
,
equals
(
401.0
));
await
tester
.
enterText
(
find
.
byType
(
TextField
),
'abcd'
);
await
tester
.
enterText
(
find
.
byType
(
TextField
),
'abcd'
);
await
tester
.
pump
();
await
tester
.
pump
();
...
@@ -1478,7 +1541,7 @@ void main() {
...
@@ -1478,7 +1541,7 @@ void main() {
editable
.
getLocalRectForCaret
(
const
TextPosition
(
offset:
2
)).
topLeft
,
editable
.
getLocalRectForCaret
(
const
TextPosition
(
offset:
2
)).
topLeft
,
);
);
expect
(
topLeft
.
dx
,
equals
(
398.5
));
expect
(
topLeft
.
dx
,
equals
(
401.0
));
});
});
testWidgets
(
'Can align to center within center'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Can align to center within center'
,
(
WidgetTester
tester
)
async
{
...
@@ -1501,7 +1564,7 @@ void main() {
...
@@ -1501,7 +1564,7 @@ void main() {
editable
.
getLocalRectForCaret
(
const
TextPosition
(
offset:
0
)).
topLeft
,
editable
.
getLocalRectForCaret
(
const
TextPosition
(
offset:
0
)).
topLeft
,
);
);
expect
(
topLeft
.
dx
,
equals
(
398.5
));
expect
(
topLeft
.
dx
,
equals
(
401.0
));
await
tester
.
enterText
(
find
.
byType
(
TextField
),
'abcd'
);
await
tester
.
enterText
(
find
.
byType
(
TextField
),
'abcd'
);
await
tester
.
pump
();
await
tester
.
pump
();
...
@@ -1510,7 +1573,7 @@ void main() {
...
@@ -1510,7 +1573,7 @@ void main() {
editable
.
getLocalRectForCaret
(
const
TextPosition
(
offset:
2
)).
topLeft
,
editable
.
getLocalRectForCaret
(
const
TextPosition
(
offset:
2
)).
topLeft
,
);
);
expect
(
topLeft
.
dx
,
equals
(
398.5
));
expect
(
topLeft
.
dx
,
equals
(
401.0
));
});
});
testWidgets
(
'Controller can update server'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Controller can update server'
,
(
WidgetTester
tester
)
async
{
...
@@ -1723,7 +1786,7 @@ void main() {
...
@@ -1723,7 +1786,7 @@ void main() {
scrollableState
=
tester
.
firstState
(
find
.
byType
(
Scrollable
));
scrollableState
=
tester
.
firstState
(
find
.
byType
(
Scrollable
));
// For a horizontal input, scrolls to the exact position of the caret.
// For a horizontal input, scrolls to the exact position of the caret.
expect
(
scrollableState
.
position
.
pixels
,
equals
(
22
2
.0
));
expect
(
scrollableState
.
position
.
pixels
,
equals
(
22
3
.0
));
});
});
testWidgets
(
'Multiline text field scrolls the caret into view'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Multiline text field scrolls the caret into view'
,
(
WidgetTester
tester
)
async
{
...
@@ -3130,7 +3193,7 @@ void main() {
...
@@ -3130,7 +3193,7 @@ void main() {
editable
.
getLocalRectForCaret
(
const
TextPosition
(
offset:
10
)).
topLeft
,
editable
.
getLocalRectForCaret
(
const
TextPosition
(
offset:
10
)).
topLeft
,
);
);
expect
(
topLeft
.
dx
,
equals
(
701.
0
));
expect
(
topLeft
.
dx
,
equals
(
701.
6666870117188
));
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
const
MaterialApp
(
const
MaterialApp
(
...
@@ -3150,7 +3213,7 @@ void main() {
...
@@ -3150,7 +3213,7 @@ void main() {
editable
.
getLocalRectForCaret
(
const
TextPosition
(
offset:
10
)).
topLeft
,
editable
.
getLocalRectForCaret
(
const
TextPosition
(
offset:
10
)).
topLeft
,
);
);
expect
(
topLeft
.
dx
,
equals
(
160.
0
));
expect
(
topLeft
.
dx
,
equals
(
160.
6666717529297
));
});
});
testWidgets
(
'TextField semantics'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'TextField semantics'
,
(
WidgetTester
tester
)
async
{
...
...
packages/flutter/test/rendering/editable_test.dart
View file @
d2a2a5cf
...
@@ -124,11 +124,14 @@ void main() {
...
@@ -124,11 +124,14 @@ void main() {
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
MaterialApp
(
MaterialApp
(
home:
Material
(
home:
Padding
(
child:
TextField
(
padding:
const
EdgeInsets
.
only
(
top:
0.25
),
controller:
controller
,
child:
Material
(
focusNode:
focusNode
,
child:
TextField
(
style:
textStyle
,
controller:
controller
,
focusNode:
focusNode
,
style:
textStyle
,
),
),
),
),
),
),
),
...
@@ -144,16 +147,32 @@ void main() {
...
@@ -144,16 +147,32 @@ void main() {
offset:
const
Offset
(
20
,
20
)));
offset:
const
Offset
(
20
,
20
)));
await
tester
.
pump
();
await
tester
.
pump
();
expect
(
find
.
byType
(
EditableText
),
paints
..
rrect
(
expect
(
editable
,
paints
rrect:
RRect
.
fromRectAndRadius
(
Rect
.
fromLTRB
(
464.5
,
0
,
467.5
,
16.0
),
const
Radius
.
circular
(
1.0
)),
color:
const
Color
(
0xff4285f4
))
..
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
))
);
);
// Moves the cursor right a few characters.
// Moves the cursor right a few characters.
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
editableTextState
.
updateFloatingCursor
(
offset:
const
Offset
(-
250
,
20
)));
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
Update
,
expect
(
find
.
byType
(
EditableText
),
paints
..
rrect
(
offset:
const
Offset
(-
250
,
20
)));
rrect:
RRect
.
fromRectAndRadius
(
Rect
.
fromLTRB
(
194.5
,
0
,
197.5
,
16.0
),
const
Radius
.
circular
(
1.0
)),
color:
const
Color
(
0xff4285f4
))
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
))
);
);
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
End
));
editableTextState
.
updateFloatingCursor
(
RawFloatingCursorPoint
(
state:
FloatingCursorDragState
.
End
));
...
...
packages/flutter/test/widgets/editable_text_show_on_screen_test.dart
View file @
d2a2a5cf
...
@@ -201,28 +201,33 @@ void main() {
...
@@ -201,28 +201,33 @@ void main() {
final
TextEditingController
textController
=
TextEditingController
();
final
TextEditingController
textController
=
TextEditingController
();
final
PageController
pageController
=
PageController
(
initialPage:
1
);
final
PageController
pageController
=
PageController
(
initialPage:
1
);
await
tester
.
pumpWidget
(
Directionality
(
await
tester
.
pumpWidget
(
textDirection:
TextDirection
.
ltr
,
MediaQuery
(
child:
Material
(
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
PageView
(
child:
Directionality
(
controller:
pageController
,
textDirection:
TextDirection
.
ltr
,
children:
<
Widget
>[
child:
Material
(
Container
(
child:
PageView
(
color:
Colors
.
red
,
controller:
pageController
,
children:
<
Widget
>[
Container
(
color:
Colors
.
red
,
),
Container
(
child:
TextField
(
controller:
textController
,
),
color:
Colors
.
green
,
),
Container
(
color:
Colors
.
red
,
),
],
),
),
Container
(
),
child:
TextField
(
controller:
textController
,
),
color:
Colors
.
green
,
),
Container
(
color:
Colors
.
red
,
),
],
),
),
),
),
)
)
;
);
await
tester
.
showKeyboard
(
find
.
byType
(
EditableText
));
await
tester
.
showKeyboard
(
find
.
byType
(
EditableText
));
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
...
...
packages/flutter/test/widgets/editable_text_test.dart
View file @
d2a2a5cf
...
@@ -11,6 +11,7 @@ import 'package:flutter/material.dart';
...
@@ -11,6 +11,7 @@ import 'package:flutter/material.dart';
import
'package:flutter/widgets.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter/services.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:flutter/foundation.dart'
;
import
'semantics_tester.dart'
;
import
'semantics_tester.dart'
;
...
@@ -36,18 +37,21 @@ void main() {
...
@@ -36,18 +37,21 @@ void main() {
String
serializedActionName
,
String
serializedActionName
,
})
async
{
})
async
{
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
Directionality
(
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
FocusScope
(
child:
Directionality
(
node:
focusScopeNode
,
textDirection:
TextDirection
.
ltr
,
autofocus:
true
,
child:
FocusScope
(
child:
EditableText
(
node:
focusScopeNode
,
backgroundCursorColor:
Colors
.
grey
,
autofocus:
true
,
controller:
controller
,
child:
EditableText
(
focusNode:
focusNode
,
backgroundCursorColor:
Colors
.
grey
,
textInputAction:
action
,
controller:
controller
,
style:
textStyle
,
focusNode:
focusNode
,
cursorColor:
cursorColor
,
textInputAction:
action
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
),
),
),
...
@@ -64,14 +68,17 @@ void main() {
...
@@ -64,14 +68,17 @@ void main() {
testWidgets
(
'has expected defaults'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'has expected defaults'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
Directionality
(
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
EditableText
(
child:
Directionality
(
controller:
controller
,
textDirection:
TextDirection
.
ltr
,
backgroundCursorColor:
Colors
.
grey
,
child:
EditableText
(
focusNode:
focusNode
,
controller:
controller
,
style:
textStyle
,
backgroundCursorColor:
Colors
.
grey
,
cursorColor:
cursorColor
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
),
);
);
...
@@ -86,7 +93,9 @@ void main() {
...
@@ -86,7 +93,9 @@ void main() {
testWidgets
(
'cursor has expected width and radius'
,
testWidgets
(
'cursor has expected width and radius'
,
(
WidgetTester
tester
)
async
{
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Directionality
(
await
tester
.
pumpWidget
(
MediaQuery
(
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
textDirection:
TextDirection
.
ltr
,
child:
EditableText
(
child:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
backgroundCursorColor:
Colors
.
grey
,
...
@@ -96,7 +105,7 @@ void main() {
...
@@ -96,7 +105,7 @@ void main() {
cursorColor:
cursorColor
,
cursorColor:
cursorColor
,
cursorWidth:
10.0
,
cursorWidth:
10.0
,
cursorRadius:
const
Radius
.
circular
(
2.0
),
cursorRadius:
const
Radius
.
circular
(
2.0
),
)));
)))
)
;
final
EditableText
editableText
=
final
EditableText
editableText
=
tester
.
firstWidget
(
find
.
byType
(
EditableText
));
tester
.
firstWidget
(
find
.
byType
(
EditableText
));
...
@@ -107,17 +116,20 @@ void main() {
...
@@ -107,17 +116,20 @@ void main() {
testWidgets
(
'text keyboard is requested when maxLines is default'
,
testWidgets
(
'text keyboard is requested when maxLines is default'
,
(
WidgetTester
tester
)
async
{
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
Directionality
(
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
FocusScope
(
child:
Directionality
(
node:
focusScopeNode
,
textDirection:
TextDirection
.
ltr
,
autofocus:
true
,
child:
FocusScope
(
child:
EditableText
(
node:
focusScopeNode
,
controller:
controller
,
autofocus:
true
,
backgroundCursorColor:
Colors
.
grey
,
child:
EditableText
(
focusNode:
focusNode
,
controller:
controller
,
style:
textStyle
,
backgroundCursorColor:
Colors
.
grey
,
cursorColor:
cursorColor
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
),
),
),
...
@@ -269,18 +281,21 @@ void main() {
...
@@ -269,18 +281,21 @@ void main() {
testWidgets
(
'multiline keyboard is requested when set explicitly'
,
testWidgets
(
'multiline keyboard is requested when set explicitly'
,
(
WidgetTester
tester
)
async
{
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
Directionality
(
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
FocusScope
(
child:
Directionality
(
node:
focusScopeNode
,
textDirection:
TextDirection
.
ltr
,
autofocus:
true
,
child:
FocusScope
(
child:
EditableText
(
node:
focusScopeNode
,
controller:
controller
,
autofocus:
true
,
backgroundCursorColor:
Colors
.
grey
,
child:
EditableText
(
focusNode:
focusNode
,
controller:
controller
,
keyboardType:
TextInputType
.
multiline
,
backgroundCursorColor:
Colors
.
grey
,
style:
textStyle
,
focusNode:
focusNode
,
cursorColor:
cursorColor
,
keyboardType:
TextInputType
.
multiline
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
),
),
),
...
@@ -299,18 +314,21 @@ void main() {
...
@@ -299,18 +314,21 @@ void main() {
testWidgets
(
'Multiline keyboard with newline action is requested when maxLines = null'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Multiline keyboard with newline action is requested when maxLines = null'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
Directionality
(
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
FocusScope
(
child:
Directionality
(
node:
focusScopeNode
,
textDirection:
TextDirection
.
ltr
,
autofocus:
true
,
child:
FocusScope
(
child:
EditableText
(
node:
focusScopeNode
,
controller:
controller
,
autofocus:
true
,
backgroundCursorColor:
Colors
.
grey
,
child:
EditableText
(
focusNode:
focusNode
,
controller:
controller
,
maxLines:
null
,
backgroundCursorColor:
Colors
.
grey
,
style:
textStyle
,
focusNode:
focusNode
,
cursorColor:
cursorColor
,
maxLines:
null
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
),
),
),
...
@@ -329,19 +347,22 @@ void main() {
...
@@ -329,19 +347,22 @@ void main() {
testWidgets
(
'Text keyboard is requested when explicitly set and maxLines = null'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Text keyboard is requested when explicitly set and maxLines = null'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
Directionality
(
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
FocusScope
(
child:
Directionality
(
node:
focusScopeNode
,
textDirection:
TextDirection
.
ltr
,
autofocus:
true
,
child:
FocusScope
(
child:
EditableText
(
node:
focusScopeNode
,
backgroundCursorColor:
Colors
.
grey
,
autofocus:
true
,
controller:
controller
,
child:
EditableText
(
focusNode:
focusNode
,
backgroundCursorColor:
Colors
.
grey
,
maxLines:
null
,
controller:
controller
,
keyboardType:
TextInputType
.
text
,
focusNode:
focusNode
,
style:
textStyle
,
maxLines:
null
,
cursorColor:
cursorColor
,
keyboardType:
TextInputType
.
text
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
),
),
),
...
@@ -362,19 +383,22 @@ void main() {
...
@@ -362,19 +383,22 @@ void main() {
'Correct keyboard is requested when set explicitly and maxLines > 1'
,
'Correct keyboard is requested when set explicitly and maxLines > 1'
,
(
WidgetTester
tester
)
async
{
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
Directionality
(
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
FocusScope
(
child:
Directionality
(
node:
focusScopeNode
,
textDirection:
TextDirection
.
ltr
,
autofocus:
true
,
child:
FocusScope
(
child:
EditableText
(
node:
focusScopeNode
,
backgroundCursorColor:
Colors
.
grey
,
autofocus:
true
,
controller:
controller
,
child:
EditableText
(
focusNode:
focusNode
,
backgroundCursorColor:
Colors
.
grey
,
keyboardType:
TextInputType
.
phone
,
controller:
controller
,
maxLines:
3
,
focusNode:
focusNode
,
style:
textStyle
,
keyboardType:
TextInputType
.
phone
,
cursorColor:
cursorColor
,
maxLines:
3
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
),
),
),
...
@@ -394,18 +418,21 @@ void main() {
...
@@ -394,18 +418,21 @@ void main() {
testWidgets
(
'multiline keyboard is requested when set implicitly'
,
testWidgets
(
'multiline keyboard is requested when set implicitly'
,
(
WidgetTester
tester
)
async
{
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
Directionality
(
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
FocusScope
(
child:
Directionality
(
node:
focusScopeNode
,
textDirection:
TextDirection
.
ltr
,
autofocus:
true
,
child:
FocusScope
(
child:
EditableText
(
node:
focusScopeNode
,
backgroundCursorColor:
Colors
.
grey
,
autofocus:
true
,
controller:
controller
,
child:
EditableText
(
focusNode:
focusNode
,
backgroundCursorColor:
Colors
.
grey
,
maxLines:
3
,
// Sets multiline keyboard implicitly.
controller:
controller
,
style:
textStyle
,
focusNode:
focusNode
,
cursorColor:
cursorColor
,
maxLines:
3
,
// Sets multiline keyboard implicitly.
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
),
),
),
...
@@ -425,18 +452,21 @@ void main() {
...
@@ -425,18 +452,21 @@ void main() {
testWidgets
(
'single line inputs have correct default keyboard'
,
testWidgets
(
'single line inputs have correct default keyboard'
,
(
WidgetTester
tester
)
async
{
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
Directionality
(
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
FocusScope
(
child:
Directionality
(
node:
focusScopeNode
,
textDirection:
TextDirection
.
ltr
,
autofocus:
true
,
child:
FocusScope
(
child:
EditableText
(
node:
focusScopeNode
,
backgroundCursorColor:
Colors
.
grey
,
autofocus:
true
,
controller:
controller
,
child:
EditableText
(
focusNode:
focusNode
,
backgroundCursorColor:
Colors
.
grey
,
maxLines:
1
,
// Sets text keyboard implicitly.
controller:
controller
,
style:
textStyle
,
focusNode:
focusNode
,
cursorColor:
cursorColor
,
maxLines:
1
,
// Sets text keyboard implicitly.
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
),
),
),
...
@@ -538,6 +568,8 @@ void main() {
...
@@ -538,6 +568,8 @@ void main() {
await
tester
.
tap
(
find
.
text
(
'PASTE'
));
await
tester
.
tap
(
find
.
text
(
'PASTE'
));
await
tester
.
pump
();
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
600
));
expect
(
changedValue
,
clipboardContent
);
expect
(
changedValue
,
clipboardContent
);
...
@@ -590,6 +622,8 @@ void main() {
...
@@ -590,6 +622,8 @@ void main() {
await
tester
.
tap
(
find
.
text
(
'PASTE'
));
await
tester
.
tap
(
find
.
text
(
'PASTE'
));
await
tester
.
pump
();
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
600
));
expect
(
changedValue
,
clipboardContent
);
expect
(
changedValue
,
clipboardContent
);
...
@@ -807,6 +841,118 @@ void main() {
...
@@ -807,6 +841,118 @@ void main() {
// and onSubmission callbacks.
// 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.'
,
'When "newline" action is called on a Editable text with maxLines != 1, onEditingComplete and onSubmitted callbacks are not invoked.'
,
(
WidgetTester
tester
)
async
{
(
WidgetTester
tester
)
async
{
...
@@ -867,22 +1013,25 @@ testWidgets(
...
@@ -867,22 +1013,25 @@ testWidgets(
return
StatefulBuilder
(
return
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setter
)
{
builder:
(
BuildContext
context
,
StateSetter
setter
)
{
setState
=
setter
;
setState
=
setter
;
return
Directionality
(
return
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Center
(
child:
Directionality
(
child:
Material
(
textDirection:
TextDirection
.
ltr
,
child:
EditableText
(
child:
Center
(
backgroundCursorColor:
Colors
.
grey
,
child:
Material
(
key:
editableTextKey
,
child:
EditableText
(
controller:
currentController
,
backgroundCursorColor:
Colors
.
grey
,
focusNode:
FocusNode
(),
key:
editableTextKey
,
style:
Typography
(
platform:
TargetPlatform
.
android
)
controller:
currentController
,
.
black
focusNode:
FocusNode
(),
.
subhead
,
style:
Typography
(
platform:
TargetPlatform
.
android
)
cursorColor:
Colors
.
blue
,
.
black
selectionControls:
materialTextSelectionControls
,
.
subhead
,
keyboardType:
TextInputType
.
text
,
cursorColor:
Colors
.
blue
,
onChanged:
(
String
value
)
{},
selectionControls:
materialTextSelectionControls
,
keyboardType:
TextInputType
.
text
,
onChanged:
(
String
value
)
{},
),
),
),
),
),
),
),
...
@@ -926,17 +1075,20 @@ testWidgets(
...
@@ -926,17 +1075,20 @@ testWidgets(
final
SemanticsTester
semantics
=
SemanticsTester
(
tester
);
final
SemanticsTester
semantics
=
SemanticsTester
(
tester
);
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
Directionality
(
MediaQuery
(
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
textDirection:
TextDirection
.
ltr
,
child:
FocusScope
(
child:
FocusScope
(
node:
focusScopeNode
,
node:
focusScopeNode
,
autofocus:
true
,
autofocus:
true
,
child:
EditableText
(
child:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
backgroundCursorColor:
Colors
.
grey
,
controller:
controller
,
controller:
controller
,
focusNode:
focusNode
,
focusNode:
focusNode
,
style:
textStyle
,
style:
textStyle
,
cursorColor:
cursorColor
,
cursorColor:
cursorColor
,
),
),
),
),
),
),
),
...
@@ -968,16 +1120,19 @@ testWidgets(
...
@@ -968,16 +1120,19 @@ testWidgets(
controller
.
text
=
value1
;
controller
.
text
=
value1
;
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
Directionality
(
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
FocusScope
(
child:
Directionality
(
node:
focusScopeNode
,
textDirection:
TextDirection
.
ltr
,
child:
EditableText
(
child:
FocusScope
(
backgroundCursorColor:
Colors
.
grey
,
node:
focusScopeNode
,
controller:
controller
,
child:
EditableText
(
focusNode:
focusNode
,
backgroundCursorColor:
Colors
.
grey
,
style:
textStyle
,
controller:
controller
,
cursorColor:
cursorColor
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
),
),
),
...
@@ -1767,21 +1922,26 @@ testWidgets(
...
@@ -1767,21 +1922,26 @@ testWidgets(
final
FocusNode
focusNode
=
FocusNode
();
final
FocusNode
focusNode
=
FocusNode
();
controller
.
text
=
text
;
controller
.
text
=
text
;
await
tester
.
pumpWidget
(
Directionality
(
await
tester
.
pumpWidget
(
textDirection:
TextDirection
.
ltr
,
MediaQuery
(
child:
FocusScope
(
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
node:
focusScopeNode
,
child:
Directionality
(
autofocus:
true
,
textDirection:
TextDirection
.
ltr
,
child:
EditableText
(
child:
FocusScope
(
backgroundCursorColor:
Colors
.
grey
,
node:
focusScopeNode
,
controller:
controller
,
autofocus:
true
,
focusNode:
focusNode
,
child:
EditableText
(
autofocus:
true
,
backgroundCursorColor:
Colors
.
grey
,
style:
textStyle
,
controller:
controller
,
cursorColor:
cursorColor
,
focusNode:
focusNode
,
autofocus:
true
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
),
),
)
)
;
);
expect
(
focusNode
.
hasFocus
,
true
);
expect
(
focusNode
.
hasFocus
,
true
);
expect
(
controller
.
selection
.
isCollapsed
,
true
);
expect
(
controller
.
selection
.
isCollapsed
,
true
);
...
@@ -1811,17 +1971,20 @@ testWidgets(
...
@@ -1811,17 +1971,20 @@ testWidgets(
final
FocusNode
focusNode
=
FocusNode
();
final
FocusNode
focusNode
=
FocusNode
();
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
Directionality
(
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
FocusScope
(
child:
Directionality
(
node:
focusScopeNode
,
textDirection:
TextDirection
.
ltr
,
autofocus:
true
,
child:
FocusScope
(
child:
EditableText
(
node:
focusScopeNode
,
backgroundCursorColor:
Colors
.
grey
,
autofocus:
true
,
controller:
controller
,
child:
EditableText
(
focusNode:
focusNode
,
backgroundCursorColor:
Colors
.
grey
,
style:
textStyle
,
controller:
controller
,
cursorColor:
cursorColor
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
),
),
),
...
@@ -1864,17 +2027,20 @@ testWidgets(
...
@@ -1864,17 +2027,20 @@ testWidgets(
final
FocusNode
focusNode
=
FocusNode
();
final
FocusNode
focusNode
=
FocusNode
();
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
Directionality
(
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
FocusScope
(
child:
Directionality
(
node:
focusScopeNode
,
textDirection:
TextDirection
.
ltr
,
autofocus:
true
,
child:
FocusScope
(
child:
EditableText
(
node:
focusScopeNode
,
backgroundCursorColor:
Colors
.
grey
,
autofocus:
true
,
controller:
controller
,
child:
EditableText
(
focusNode:
focusNode
,
backgroundCursorColor:
Colors
.
grey
,
style:
textStyle
,
controller:
controller
,
cursorColor:
cursorColor
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
),
),
),
),
),
...
@@ -1936,7 +2102,7 @@ testWidgets(
...
@@ -1936,7 +2102,7 @@ testWidgets(
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
baseOffset
,
1
1
);
expect
(
controller
.
selection
.
baseOffset
,
1
0
);
});
});
testWidgets
(
'Formatters are skipped if text has not changed'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Formatters are skipped if text has not changed'
,
(
WidgetTester
tester
)
async
{
...
@@ -1946,18 +2112,21 @@ testWidgets(
...
@@ -1946,18 +2112,21 @@ testWidgets(
return
newValue
;
return
newValue
;
});
});
final
TextEditingController
controller
=
TextEditingController
();
final
TextEditingController
controller
=
TextEditingController
();
final
EditableText
editableText
=
EditableText
(
final
MediaQuery
mediaQuery
=
MediaQuery
(
controller:
controller
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
backgroundCursorColor:
Colors
.
red
,
child:
EditableText
(
cursorColor:
Colors
.
red
,
controller:
controller
,
focusNode:
FocusNode
(),
backgroundCursorColor:
Colors
.
red
,
style:
textStyle
,
cursorColor:
Colors
.
red
,
inputFormatters:
<
TextInputFormatter
>[
focusNode:
FocusNode
(),
formatter
,
style:
textStyle
,
],
inputFormatters:
<
TextInputFormatter
>[
textDirection:
TextDirection
.
ltr
,
formatter
,
],
textDirection:
TextDirection
.
ltr
,
),
);
);
await
tester
.
pumpWidget
(
editableText
);
await
tester
.
pumpWidget
(
mediaQuery
);
final
EditableTextState
state
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
final
EditableTextState
state
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
state
.
updateEditingValue
(
const
TextEditingValue
(
state
.
updateEditingValue
(
const
TextEditingValue
(
text:
'a'
,
text:
'a'
,
...
@@ -1990,14 +2159,19 @@ testWidgets(
...
@@ -1990,14 +2159,19 @@ testWidgets(
final
TextEditingController
controller
=
TextEditingController
();
final
TextEditingController
controller
=
TextEditingController
();
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
Directionality
(
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
child:
EditableText
(
devicePixelRatio:
1.0
controller:
controller
,
),
focusNode:
FocusNode
(),
child:
Directionality
(
style:
Typography
(
platform:
TargetPlatform
.
android
).
black
.
subhead
,
textDirection:
TextDirection
.
ltr
,
cursorColor:
Colors
.
blue
,
child:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
controller:
controller
,
focusNode:
FocusNode
(),
style:
Typography
(
platform:
TargetPlatform
.
android
).
black
.
subhead
,
cursorColor:
Colors
.
blue
,
backgroundCursorColor:
Colors
.
grey
,
),
),
),
),
),
);
);
...
@@ -2018,15 +2192,20 @@ testWidgets(
...
@@ -2018,15 +2192,20 @@ testWidgets(
final
TextEditingController
controller
=
TextEditingController
();
final
TextEditingController
controller
=
TextEditingController
();
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
Directionality
(
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
child:
EditableText
(
devicePixelRatio:
1.0
controller:
controller
,
),
focusNode:
FocusNode
(),
child:
Directionality
(
style:
Typography
(
platform:
TargetPlatform
.
android
).
black
.
subhead
,
textDirection:
TextDirection
.
ltr
,
cursorColor:
Colors
.
blue
,
child:
EditableText
(
backgroundCursorColor:
Colors
.
grey
,
controller:
controller
,
keyboardAppearance:
Brightness
.
dark
,
focusNode:
FocusNode
(),
style:
Typography
(
platform:
TargetPlatform
.
android
).
black
.
subhead
,
cursorColor:
Colors
.
blue
,
backgroundCursorColor:
Colors
.
grey
,
keyboardAppearance:
Brightness
.
dark
,
),
),
),
),
),
);
);
...
...
packages/flutter/test/widgets/form_test.dart
View file @
d2a2a5cf
...
@@ -11,14 +11,17 @@ void main() {
...
@@ -11,14 +11,17 @@ void main() {
String
fieldValue
;
String
fieldValue
;
Widget
builder
()
{
Widget
builder
()
{
return
Directionality
(
return
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Center
(
child:
Directionality
(
child:
Material
(
textDirection:
TextDirection
.
ltr
,
child:
Form
(
child:
Center
(
key:
formKey
,
child:
Material
(
child:
TextFormField
(
child:
Form
(
onSaved:
(
String
value
)
{
fieldValue
=
value
;
},
key:
formKey
,
child:
TextFormField
(
onSaved:
(
String
value
)
{
fieldValue
=
value
;
},
),
),
),
),
),
),
),
...
@@ -45,13 +48,16 @@ void main() {
...
@@ -45,13 +48,16 @@ void main() {
String
fieldValue
;
String
fieldValue
;
Widget
builder
()
{
Widget
builder
()
{
return
Directionality
(
return
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Center
(
child:
Directionality
(
child:
Material
(
textDirection:
TextDirection
.
ltr
,
child:
Form
(
child:
Center
(
child:
TextField
(
child:
Material
(
onChanged:
(
String
value
)
{
fieldValue
=
value
;
},
child:
Form
(
child:
TextField
(
onChanged:
(
String
value
)
{
fieldValue
=
value
;
},
),
),
),
),
),
),
),
...
@@ -78,15 +84,18 @@ void main() {
...
@@ -78,15 +84,18 @@ void main() {
String
errorText
(
String
value
)
=>
value
+
'/error'
;
String
errorText
(
String
value
)
=>
value
+
'/error'
;
Widget
builder
(
bool
autovalidate
)
{
Widget
builder
(
bool
autovalidate
)
{
return
Directionality
(
return
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Center
(
child:
Directionality
(
child:
Material
(
textDirection:
TextDirection
.
ltr
,
child:
Form
(
child:
Center
(
key:
formKey
,
child:
Material
(
autovalidate:
autovalidate
,
child:
Form
(
child:
TextFormField
(
key:
formKey
,
validator:
errorText
,
autovalidate:
autovalidate
,
child:
TextFormField
(
validator:
errorText
,
),
),
),
),
),
),
),
...
@@ -129,22 +138,25 @@ void main() {
...
@@ -129,22 +138,25 @@ void main() {
String
errorText
(
String
input
)
=>
'
${fieldKey.currentState.value}
/error'
;
String
errorText
(
String
input
)
=>
'
${fieldKey.currentState.value}
/error'
;
Widget
builder
()
{
Widget
builder
()
{
return
Directionality
(
return
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Center
(
child:
Directionality
(
child:
Material
(
textDirection:
TextDirection
.
ltr
,
child:
Form
(
child:
Center
(
key:
formKey
,
child:
Material
(
autovalidate:
true
,
child:
Form
(
child:
ListView
(
key:
formKey
,
children:
<
Widget
>[
autovalidate:
true
,
TextFormField
(
child:
ListView
(
key:
fieldKey
,
children:
<
Widget
>[
),
TextFormField
(
TextFormField
(
key:
fieldKey
,
validator:
errorText
,
),
),
TextFormField
(
],
validator:
errorText
,
),
],
),
),
),
),
),
),
),
...
@@ -172,14 +184,17 @@ void main() {
...
@@ -172,14 +184,17 @@ void main() {
final
GlobalKey
<
FormFieldState
<
String
>>
inputKey
=
GlobalKey
<
FormFieldState
<
String
>>();
final
GlobalKey
<
FormFieldState
<
String
>>
inputKey
=
GlobalKey
<
FormFieldState
<
String
>>();
Widget
builder
()
{
Widget
builder
()
{
return
Directionality
(
return
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Center
(
child:
Directionality
(
child:
Material
(
textDirection:
TextDirection
.
ltr
,
child:
Form
(
child:
Center
(
child:
TextFormField
(
child:
Material
(
key:
inputKey
,
child:
Form
(
initialValue:
'hello'
,
child:
TextFormField
(
key:
inputKey
,
initialValue:
'hello'
,
),
),
),
),
),
),
),
...
@@ -212,14 +227,17 @@ void main() {
...
@@ -212,14 +227,17 @@ void main() {
final
GlobalKey
<
FormFieldState
<
String
>>
inputKey
=
GlobalKey
<
FormFieldState
<
String
>>();
final
GlobalKey
<
FormFieldState
<
String
>>
inputKey
=
GlobalKey
<
FormFieldState
<
String
>>();
Widget
builder
()
{
Widget
builder
()
{
return
Directionality
(
return
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Center
(
child:
Directionality
(
child:
Material
(
textDirection:
TextDirection
.
ltr
,
child:
Form
(
child:
Center
(
child:
TextFormField
(
child:
Material
(
key:
inputKey
,
child:
Form
(
controller:
controller
,
child:
TextFormField
(
key:
inputKey
,
controller:
controller
,
),
),
),
),
),
),
),
...
@@ -254,16 +272,19 @@ void main() {
...
@@ -254,16 +272,19 @@ void main() {
final
TextEditingController
controller
=
TextEditingController
(
text:
'Plover'
);
final
TextEditingController
controller
=
TextEditingController
(
text:
'Plover'
);
Widget
builder
()
{
Widget
builder
()
{
return
Directionality
(
return
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Center
(
child:
Directionality
(
child:
Material
(
textDirection:
TextDirection
.
ltr
,
child:
Form
(
child:
Center
(
key:
formKey
,
child:
Material
(
child:
TextFormField
(
child:
Form
(
key:
inputKey
,
key:
formKey
,
controller:
controller
,
child:
TextFormField
(
// initialValue is 'Plover'
key:
inputKey
,
controller:
controller
,
// initialValue is 'Plover'
),
),
),
),
),
),
),
...
@@ -301,14 +322,17 @@ void main() {
...
@@ -301,14 +322,17 @@ void main() {
return
StatefulBuilder
(
return
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setter
)
{
builder:
(
BuildContext
context
,
StateSetter
setter
)
{
setState
=
setter
;
setState
=
setter
;
return
Directionality
(
return
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Center
(
child:
Directionality
(
child:
Material
(
textDirection:
TextDirection
.
ltr
,
child:
Form
(
child:
Center
(
child:
TextFormField
(
child:
Material
(
key:
inputKey
,
child:
Form
(
controller:
currentController
,
child:
TextFormField
(
key:
inputKey
,
controller:
currentController
,
),
),
),
),
),
),
),
...
@@ -396,16 +420,19 @@ void main() {
...
@@ -396,16 +420,19 @@ void main() {
String
fieldValue
;
String
fieldValue
;
Widget
builder
(
bool
remove
)
{
Widget
builder
(
bool
remove
)
{
return
Directionality
(
return
MediaQuery
(
textDirection:
TextDirection
.
ltr
,
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Center
(
child:
Directionality
(
child:
Material
(
textDirection:
TextDirection
.
ltr
,
child:
Form
(
child:
Center
(
key:
formKey
,
child:
Material
(
child:
remove
?
Container
()
:
TextFormField
(
child:
Form
(
autofocus:
true
,
key:
formKey
,
onSaved:
(
String
value
)
{
fieldValue
=
value
;
},
child:
remove
?
Container
()
:
TextFormField
(
validator:
(
String
value
)
{
return
value
.
isEmpty
?
null
:
'yes'
;
}
autofocus:
true
,
onSaved:
(
String
value
)
{
fieldValue
=
value
;
},
validator:
(
String
value
)
{
return
value
.
isEmpty
?
null
:
'yes'
;
}
),
),
),
),
),
),
),
...
...
packages/flutter/test/widgets/physical_model_test.dart
View file @
d2a2a5cf
...
@@ -6,14 +6,18 @@ import 'package:flutter_test/flutter_test.dart';
...
@@ -6,14 +6,18 @@ import 'package:flutter_test/flutter_test.dart';
void
main
(
)
{
void
main
(
)
{
testWidgets
(
'PhysicalModel - creates a physical model layer when it needs compositing'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'PhysicalModel - creates a physical model layer when it needs compositing'
,
(
WidgetTester
tester
)
async
{
debugDisableShadows
=
false
;
debugDisableShadows
=
false
;
await
tester
.
pumpWidget
(
Directionality
(
await
tester
.
pumpWidget
(
textDirection:
TextDirection
.
ltr
,
MediaQuery
(
child:
PhysicalModel
(
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
shape:
BoxShape
.
rectangle
,
child:
Directionality
(
color:
Colors
.
grey
,
textDirection:
TextDirection
.
ltr
,
shadowColor:
Colors
.
red
,
child:
PhysicalModel
(
elevation:
1.0
,
shape:
BoxShape
.
rectangle
,
child:
Material
(
child:
TextField
(
controller:
TextEditingController
())),
color:
Colors
.
grey
,
shadowColor:
Colors
.
red
,
elevation:
1.0
,
child:
Material
(
child:
TextField
(
controller:
TextEditingController
())),
),
),
),
),
),
);
);
...
...
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