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
Hide 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 {
///
/// This should only be used as parameters when they are documented to take
/// [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
>
{
/// Creates a [MaterialStateMouseCursor].
const
MaterialStateMouseCursor
();
...
...
@@ -257,22 +265,47 @@ abstract class MaterialStateMouseCursor extends MouseCursor implements MaterialS
/// disabled, the cursor resolves to [SystemMouseCursors.basic].
///
/// 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
{
const
_ClickableMouseCursor
();
class
_EnabledAndDisabledMouseCursor
extends
MaterialStateMouseCursor
{
const
_EnabledAndDisabledMouseCursor
({
this
.
enabledCursor
,
this
.
disabledCursor
,
this
.
name
,
});
final
MouseCursor
enabledCursor
;
final
MouseCursor
disabledCursor
;
final
String
name
;
@override
MouseCursor
resolve
(
Set
<
MaterialState
>
states
)
{
if
(
states
.
contains
(
MaterialState
.
disabled
))
{
return
SystemMouseCursors
.
basic
;
return
disabledCursor
;
}
return
SystemMouseCursors
.
click
;
return
enabledCursor
;
}
@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
...
...
packages/flutter/lib/src/material/text_field.dart
View file @
9470b9e2
...
...
@@ -18,6 +18,7 @@ import 'feedback.dart';
import
'input_decorator.dart'
;
import
'material.dart'
;
import
'material_localizations.dart'
;
import
'material_state.dart'
;
import
'selectable_text.dart'
show
iOSHorizontalOffset
;
import
'text_selection.dart'
;
import
'theme.dart'
;
...
...
@@ -346,6 +347,7 @@ class TextField extends StatefulWidget {
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
enableInteractiveSelection
=
true
,
this
.
onTap
,
this
.
mouseCursor
,
this
.
buildCounter
,
this
.
scrollController
,
this
.
scrollPhysics
,
...
...
@@ -685,6 +687,25 @@ class TextField extends StatefulWidget {
/// {@endtemplate}
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.
///
/// See [InputCounterWidgetBuilder] for an explanation of the passed in
...
...
@@ -799,6 +820,10 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
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
()
{
final
MaterialLocalizations
localizations
=
MaterialLocalizations
.
of
(
context
);
final
ThemeData
themeData
=
Theme
.
of
(
context
);
...
...
@@ -849,17 +874,16 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
counterText
+=
'/
${widget.maxLength}
'
;
final
int
remaining
=
(
widget
.
maxLength
-
currentLength
).
clamp
(
0
,
widget
.
maxLength
)
as
int
;
semanticCounterText
=
localizations
.
remainingTextFieldCharacterCount
(
remaining
);
}
// Handle length exceeds maxLength
if
(
_effectiveController
.
value
.
text
.
runes
.
length
>
widget
.
maxLength
)
{
return
effectiveDecoration
.
copyWith
(
errorText:
effectiveDecoration
.
errorText
??
''
,
counterStyle:
effectiveDecoration
.
errorStyle
??
themeData
.
textTheme
.
caption
.
copyWith
(
color:
themeData
.
errorColor
),
counterText:
counterText
,
semanticCounterText:
semanticCounterText
,
);
}
if
(
_hasIntrinsicError
)
{
return
effectiveDecoration
.
copyWith
(
errorText:
effectiveDecoration
.
errorText
??
''
,
counterStyle:
effectiveDecoration
.
errorStyle
??
themeData
.
textTheme
.
caption
.
copyWith
(
color:
themeData
.
errorColor
),
counterText:
counterText
,
semanticCounterText:
semanticCounterText
,
);
}
return
effectiveDecoration
.
copyWith
(
...
...
@@ -1113,10 +1137,20 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
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
(
ignoring:
!
_isEnabled
,
child:
MouseRegion
(
cursor:
SystemMouseCursors
.
text
,
cursor:
effectiveMouseCursor
,
onEnter:
(
PointerEnterEvent
event
)
=>
_handleHover
(
true
),
onExit:
(
PointerExitEvent
event
)
=>
_handleHover
(
false
),
child:
AnimatedBuilder
(
...
...
packages/flutter/lib/src/widgets/editable_text.dart
View file @
9470b9e2
...
...
@@ -995,8 +995,9 @@ class EditableText extends StatefulWidget {
/// If this property is null, [SystemMouseCursors.text] will be used.
///
/// The [mouseCursor] is the only property of [EditableText] that controls the
/// mouse pointer. All other properties related to "cursor" stands for the text
/// cursor, which is usually a blinking vertical line at the editing position.
/// appearance of the mouse pointer. All other properties related to "cursor"
/// stands for the text cursor, which is usually a blinking vertical line at
/// the editing position.
final
MouseCursor
mouseCursor
;
/// If true, the [RenderEditable] created by this widget will not handle
...
...
packages/flutter/test/material/text_field_test.dart
View file @
9470b9e2
...
...
@@ -7941,26 +7941,74 @@ void main() {
await
tester
.
pumpWidget
(
const
MaterialApp
(
home:
Material
(
child:
TextField
(
decoration:
InputDecoration
(
// Add an icon so that the left edge is not the text area
icon:
Icon
(
Icons
.
person
),
child:
MouseRegion
(
cursor:
SystemMouseCursors
.
forbidden
,
child:
TextField
(
mouseCursor:
SystemMouseCursors
.
grab
,
decoration:
InputDecoration
(
// Add an icon so that the left edge is not the text area
icon:
Icon
(
Icons
.
person
),
),
),
),
),
),
);
// 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
);
await
gesture
.
addPointer
(
location:
tester
.
getCenter
(
find
.
byType
(
TextField
))
);
await
gesture
.
addPointer
(
location:
center
);
addTearDown
(
gesture
.
removePointer
);
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
);
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