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
9470b9e2
Unverified
Commit
9470b9e2
authored
Jun 23, 2020
by
Tong Mu
Committed by
GitHub
Jun 23, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add material state mouse cursor to TextField (#59363)
parent
3406870d
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
143 additions
and
27 deletions
+143
-27
material_state.dart
packages/flutter/lib/src/material/material_state.dart
+39
-6
text_field.dart
packages/flutter/lib/src/material/text_field.dart
+45
-11
editable_text.dart
packages/flutter/lib/src/widgets/editable_text.dart
+3
-2
text_field_test.dart
packages/flutter/test/material/text_field_test.dart
+56
-8
No files found.
packages/flutter/lib/src/material/material_state.dart
View file @
9470b9e2
...
@@ -233,6 +233,14 @@ class _MaterialStateColor extends MaterialStateColor {
...
@@ -233,6 +233,14 @@ class _MaterialStateColor extends MaterialStateColor {
///
///
/// This should only be used as parameters when they are documented to take
/// This should only be used as parameters when they are documented to take
/// [MaterialStateMouseCursor], otherwise only the default state will be used.
/// [MaterialStateMouseCursor], otherwise only the default state will be used.
///
/// This class also predefines several kinds of material state mouse cursors.
///
/// See also:
///
/// * [MouseCursor] for introduction on the mouse cursor system.
/// * [SystemMouseCursors], which defines cursors that are supported by
/// native platforms.
abstract
class
MaterialStateMouseCursor
extends
MouseCursor
implements
MaterialStateProperty
<
MouseCursor
>
{
abstract
class
MaterialStateMouseCursor
extends
MouseCursor
implements
MaterialStateProperty
<
MouseCursor
>
{
/// Creates a [MaterialStateMouseCursor].
/// Creates a [MaterialStateMouseCursor].
const
MaterialStateMouseCursor
();
const
MaterialStateMouseCursor
();
...
@@ -257,22 +265,47 @@ abstract class MaterialStateMouseCursor extends MouseCursor implements MaterialS
...
@@ -257,22 +265,47 @@ abstract class MaterialStateMouseCursor extends MouseCursor implements MaterialS
/// disabled, the cursor resolves to [SystemMouseCursors.basic].
/// disabled, the cursor resolves to [SystemMouseCursors.basic].
///
///
/// This cursor is the default for many Material widgets.
/// This cursor is the default for many Material widgets.
static
const
MaterialStateMouseCursor
clickable
=
_ClickableMouseCursor
();
static
const
MaterialStateMouseCursor
clickable
=
_EnabledAndDisabledMouseCursor
(
enabledCursor:
SystemMouseCursors
.
click
,
disabledCursor:
SystemMouseCursors
.
basic
,
name:
'clickable'
,
);
/// A mouse cursor for material widgets related to text, which resolves differently
/// when the widget is disabled.
///
/// By default this cursor resolves to [SystemMouseCursors.text]. If the widget is
/// disabled, the cursor resolves to [SystemMouseCursors.basic].
///
/// This cursor is the default for many Material widgets.
static
const
MaterialStateMouseCursor
textable
=
_EnabledAndDisabledMouseCursor
(
enabledCursor:
SystemMouseCursors
.
text
,
disabledCursor:
SystemMouseCursors
.
basic
,
name:
'textable'
,
);
}
}
class
_ClickableMouseCursor
extends
MaterialStateMouseCursor
{
class
_EnabledAndDisabledMouseCursor
extends
MaterialStateMouseCursor
{
const
_ClickableMouseCursor
();
const
_EnabledAndDisabledMouseCursor
({
this
.
enabledCursor
,
this
.
disabledCursor
,
this
.
name
,
});
final
MouseCursor
enabledCursor
;
final
MouseCursor
disabledCursor
;
final
String
name
;
@override
@override
MouseCursor
resolve
(
Set
<
MaterialState
>
states
)
{
MouseCursor
resolve
(
Set
<
MaterialState
>
states
)
{
if
(
states
.
contains
(
MaterialState
.
disabled
))
{
if
(
states
.
contains
(
MaterialState
.
disabled
))
{
return
SystemMouseCursors
.
basic
;
return
disabledCursor
;
}
}
return
SystemMouseCursors
.
click
;
return
enabledCursor
;
}
}
@override
@override
String
get
debugDescription
=>
'MaterialStateMouseCursor(
clickabl
e)'
;
String
get
debugDescription
=>
'MaterialStateMouseCursor(
$nam
e
)'
;
}
}
/// Interface for classes that can return a value of type `T` based on a set of
/// Interface for classes that can return a value of type `T` based on a set of
...
...
packages/flutter/lib/src/material/text_field.dart
View file @
9470b9e2
...
@@ -18,6 +18,7 @@ import 'feedback.dart';
...
@@ -18,6 +18,7 @@ import 'feedback.dart';
import
'input_decorator.dart'
;
import
'input_decorator.dart'
;
import
'material.dart'
;
import
'material.dart'
;
import
'material_localizations.dart'
;
import
'material_localizations.dart'
;
import
'material_state.dart'
;
import
'selectable_text.dart'
show
iOSHorizontalOffset
;
import
'selectable_text.dart'
show
iOSHorizontalOffset
;
import
'text_selection.dart'
;
import
'text_selection.dart'
;
import
'theme.dart'
;
import
'theme.dart'
;
...
@@ -346,6 +347,7 @@ class TextField extends StatefulWidget {
...
@@ -346,6 +347,7 @@ class TextField extends StatefulWidget {
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
enableInteractiveSelection
=
true
,
this
.
enableInteractiveSelection
=
true
,
this
.
onTap
,
this
.
onTap
,
this
.
mouseCursor
,
this
.
buildCounter
,
this
.
buildCounter
,
this
.
scrollController
,
this
.
scrollController
,
this
.
scrollPhysics
,
this
.
scrollPhysics
,
...
@@ -685,6 +687,25 @@ class TextField extends StatefulWidget {
...
@@ -685,6 +687,25 @@ class TextField extends StatefulWidget {
/// {@endtemplate}
/// {@endtemplate}
final
GestureTapCallback
onTap
;
final
GestureTapCallback
onTap
;
/// The cursor for a mouse pointer when it enters or is hovering over the
/// widget.
///
/// If [mouseCursor] is a [MaterialStateProperty<MouseCursor>],
/// [MaterialStateProperty.resolve] is used for the following [MaterialState]s:
///
/// * [MaterialState.error].
/// * [MaterialState.hovered].
/// * [MaterialState.focused].
/// * [MaterialState.disabled].
///
/// If this property is null, [MaterialStateMouseCursor.textable] will be used.
///
/// The [mouseCursor] is the only property of [TextField] that controls the
/// appearance of the mouse pointer. All other properties related to "cursor"
/// stand for the text cursor, which is usually a blinking vertical line at
/// the editing position.
final
MouseCursor
mouseCursor
;
/// Callback that generates a custom [InputDecorator.counter] widget.
/// Callback that generates a custom [InputDecorator.counter] widget.
///
///
/// See [InputCounterWidgetBuilder] for an explanation of the passed in
/// See [InputCounterWidgetBuilder] for an explanation of the passed in
...
@@ -799,6 +820,10 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
...
@@ -799,6 +820,10 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
int
get
_currentLength
=>
_effectiveController
.
value
.
text
.
runes
.
length
;
int
get
_currentLength
=>
_effectiveController
.
value
.
text
.
runes
.
length
;
bool
get
_hasIntrinsicError
=>
widget
.
maxLength
!=
null
&&
widget
.
maxLength
>
0
&&
_effectiveController
.
value
.
text
.
runes
.
length
>
widget
.
maxLength
;
bool
get
_hasError
=>
widget
.
decoration
?.
errorText
!=
null
||
_hasIntrinsicError
;
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
);
...
@@ -849,9 +874,9 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
...
@@ -849,9 +874,9 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
counterText
+=
'/
${widget.maxLength}
'
;
counterText
+=
'/
${widget.maxLength}
'
;
final
int
remaining
=
(
widget
.
maxLength
-
currentLength
).
clamp
(
0
,
widget
.
maxLength
)
as
int
;
final
int
remaining
=
(
widget
.
maxLength
-
currentLength
).
clamp
(
0
,
widget
.
maxLength
)
as
int
;
semanticCounterText
=
localizations
.
remainingTextFieldCharacterCount
(
remaining
);
semanticCounterText
=
localizations
.
remainingTextFieldCharacterCount
(
remaining
);
}
// Handle length exceeds maxLength
if
(
_hasIntrinsicError
)
{
if
(
_effectiveController
.
value
.
text
.
runes
.
length
>
widget
.
maxLength
)
{
return
effectiveDecoration
.
copyWith
(
return
effectiveDecoration
.
copyWith
(
errorText:
effectiveDecoration
.
errorText
??
''
,
errorText:
effectiveDecoration
.
errorText
??
''
,
counterStyle:
effectiveDecoration
.
errorStyle
counterStyle:
effectiveDecoration
.
errorStyle
...
@@ -860,7 +885,6 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
...
@@ -860,7 +885,6 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
semanticCounterText:
semanticCounterText
,
semanticCounterText:
semanticCounterText
,
);
);
}
}
}
return
effectiveDecoration
.
copyWith
(
return
effectiveDecoration
.
copyWith
(
counterText:
counterText
,
counterText:
counterText
,
...
@@ -1113,10 +1137,20 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
...
@@ -1113,10 +1137,20 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
child:
child
,
child:
child
,
);
);
}
}
final
MouseCursor
effectiveMouseCursor
=
MaterialStateProperty
.
resolveAs
<
MouseCursor
>(
widget
.
mouseCursor
??
MaterialStateMouseCursor
.
textable
,
<
MaterialState
>{
if
(!
_isEnabled
)
MaterialState
.
disabled
,
if
(
_isHovering
)
MaterialState
.
hovered
,
if
(
focusNode
.
hasFocus
)
MaterialState
.
focused
,
if
(
_hasError
)
MaterialState
.
error
,
},
);
return
IgnorePointer
(
return
IgnorePointer
(
ignoring:
!
_isEnabled
,
ignoring:
!
_isEnabled
,
child:
MouseRegion
(
child:
MouseRegion
(
cursor:
SystemMouseCursors
.
text
,
cursor:
effectiveMouseCursor
,
onEnter:
(
PointerEnterEvent
event
)
=>
_handleHover
(
true
),
onEnter:
(
PointerEnterEvent
event
)
=>
_handleHover
(
true
),
onExit:
(
PointerExitEvent
event
)
=>
_handleHover
(
false
),
onExit:
(
PointerExitEvent
event
)
=>
_handleHover
(
false
),
child:
AnimatedBuilder
(
child:
AnimatedBuilder
(
...
...
packages/flutter/lib/src/widgets/editable_text.dart
View file @
9470b9e2
...
@@ -995,8 +995,9 @@ class EditableText extends StatefulWidget {
...
@@ -995,8 +995,9 @@ class EditableText extends StatefulWidget {
/// If this property is null, [SystemMouseCursors.text] will be used.
/// If this property is null, [SystemMouseCursors.text] will be used.
///
///
/// The [mouseCursor] is the only property of [EditableText] that controls the
/// The [mouseCursor] is the only property of [EditableText] that controls the
/// mouse pointer. All other properties related to "cursor" stands for the text
/// appearance of the mouse pointer. All other properties related to "cursor"
/// cursor, which is usually a blinking vertical line at the editing position.
/// stands for the text cursor, which is usually a blinking vertical line at
/// the editing position.
final
MouseCursor
mouseCursor
;
final
MouseCursor
mouseCursor
;
/// If true, the [RenderEditable] created by this widget will not handle
/// If true, the [RenderEditable] created by this widget will not handle
...
...
packages/flutter/test/material/text_field_test.dart
View file @
9470b9e2
...
@@ -7941,7 +7941,10 @@ void main() {
...
@@ -7941,7 +7941,10 @@ void main() {
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
const
MaterialApp
(
const
MaterialApp
(
home:
Material
(
home:
Material
(
child:
MouseRegion
(
cursor:
SystemMouseCursors
.
forbidden
,
child:
TextField
(
child:
TextField
(
mouseCursor:
SystemMouseCursors
.
grab
,
decoration:
InputDecoration
(
decoration:
InputDecoration
(
// Add an icon so that the left edge is not the text area
// Add an icon so that the left edge is not the text area
icon:
Icon
(
Icons
.
person
),
icon:
Icon
(
Icons
.
person
),
...
@@ -7949,18 +7952,63 @@ void main() {
...
@@ -7949,18 +7952,63 @@ void main() {
),
),
),
),
),
),
),
);
);
// Center, which is within the text area
final
Offset
center
=
tester
.
getCenter
(
find
.
byType
(
TextField
));
// Top left, which is not the text area
final
Offset
edge
=
tester
.
getTopLeft
(
find
.
byType
(
TextField
))
+
const
Offset
(
1
,
1
);
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
,
pointer:
1
);
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
,
pointer:
1
);
await
gesture
.
addPointer
(
location:
tester
.
getCenter
(
find
.
byType
(
TextField
))
);
await
gesture
.
addPointer
(
location:
center
);
addTearDown
(
gesture
.
removePointer
);
addTearDown
(
gesture
.
removePointer
);
await
tester
.
pump
();
await
tester
.
pump
();
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
SystemMouseCursors
.
text
);
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
SystemMouseCursors
.
grab
);
// Test default cursor
await
tester
.
pumpWidget
(
const
MaterialApp
(
home:
Material
(
child:
MouseRegion
(
cursor:
SystemMouseCursors
.
forbidden
,
child:
TextField
(
decoration:
InputDecoration
(
icon:
Icon
(
Icons
.
person
),
),
),
),
),
),
);
// Test top left, which is not the text area
await
gesture
.
moveTo
(
tester
.
getTopLeft
(
find
.
byType
(
TextField
))
+
const
Offset
(
1
,
1
));
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
SystemMouseCursors
.
text
);
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
SystemMouseCursors
.
text
);
await
gesture
.
moveTo
(
edge
);
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
SystemMouseCursors
.
text
);
await
gesture
.
moveTo
(
center
);
// Test default cursor when disabled
await
tester
.
pumpWidget
(
const
MaterialApp
(
home:
Material
(
child:
MouseRegion
(
cursor:
SystemMouseCursors
.
forbidden
,
child:
TextField
(
enabled:
false
,
decoration:
InputDecoration
(
icon:
Icon
(
Icons
.
person
),
),
),
),
),
),
);
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
SystemMouseCursors
.
basic
);
await
gesture
.
moveTo
(
edge
);
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
SystemMouseCursors
.
basic
);
await
gesture
.
moveTo
(
center
);
});
});
}
}
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