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
02b46b0d
Unverified
Commit
02b46b0d
authored
Jun 10, 2021
by
Craig Labenz
Committed by
GitHub
Jun 10, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactor: Add MaterialStateMixin (#82843)
parent
b5fc79f9
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
378 additions
and
126 deletions
+378
-126
material.dart
packages/flutter/lib/material.dart
+1
-0
button.dart
packages/flutter/lib/src/material/button.dart
+16
-50
button_style_button.dart
packages/flutter/lib/src/material/button_style_button.dart
+10
-43
chip.dart
packages/flutter/lib/src/material/chip.dart
+16
-33
material_state_mixin.dart
packages/flutter/lib/src/material/material_state_mixin.dart
+167
-0
material_state_mixin_test.dart
...ages/flutter/test/material/material_state_mixin_test.dart
+168
-0
No files found.
packages/flutter/lib/material.dart
View file @
02b46b0d
...
...
@@ -96,6 +96,7 @@ export 'src/material/material.dart';
export
'src/material/material_button.dart'
;
export
'src/material/material_localizations.dart'
;
export
'src/material/material_state.dart'
;
export
'src/material/material_state_mixin.dart'
;
export
'src/material/mergeable_material.dart'
;
export
'src/material/navigation_rail.dart'
;
export
'src/material/navigation_rail_theme.dart'
;
...
...
packages/flutter/lib/src/material/button.dart
View file @
02b46b0d
...
...
@@ -13,6 +13,7 @@ import 'constants.dart';
import
'ink_well.dart'
;
import
'material.dart'
;
import
'material_state.dart'
;
import
'material_state_mixin.dart'
;
import
'theme.dart'
;
import
'theme_data.dart'
;
...
...
@@ -314,75 +315,40 @@ class RawMaterialButton extends StatefulWidget {
State
<
RawMaterialButton
>
createState
()
=>
_RawMaterialButtonState
();
}
class
_RawMaterialButtonState
extends
State
<
RawMaterialButton
>
{
final
Set
<
MaterialState
>
_states
=
<
MaterialState
>{};
bool
get
_hovered
=>
_states
.
contains
(
MaterialState
.
hovered
);
bool
get
_focused
=>
_states
.
contains
(
MaterialState
.
focused
);
bool
get
_pressed
=>
_states
.
contains
(
MaterialState
.
pressed
);
bool
get
_disabled
=>
_states
.
contains
(
MaterialState
.
disabled
);
void
_updateState
(
MaterialState
state
,
bool
value
)
{
value
?
_states
.
add
(
state
)
:
_states
.
remove
(
state
);
}
void
_handleHighlightChanged
(
bool
value
)
{
if
(
_pressed
!=
value
)
{
setState
(()
{
_updateState
(
MaterialState
.
pressed
,
value
);
widget
.
onHighlightChanged
?.
call
(
value
);
});
}
}
void
_handleHoveredChanged
(
bool
value
)
{
if
(
_hovered
!=
value
)
{
setState
(()
{
_updateState
(
MaterialState
.
hovered
,
value
);
});
}
}
void
_handleFocusedChanged
(
bool
value
)
{
if
(
_focused
!=
value
)
{
setState
(()
{
_updateState
(
MaterialState
.
focused
,
value
);
});
}
}
class
_RawMaterialButtonState
extends
State
<
RawMaterialButton
>
with
MaterialStateMixin
{
@override
void
initState
()
{
super
.
initState
();
_update
State
(
MaterialState
.
disabled
,
!
widget
.
enabled
);
setMaterial
State
(
MaterialState
.
disabled
,
!
widget
.
enabled
);
}
@override
void
didUpdateWidget
(
RawMaterialButton
oldWidget
)
{
super
.
didUpdateWidget
(
oldWidget
);
_update
State
(
MaterialState
.
disabled
,
!
widget
.
enabled
);
setMaterial
State
(
MaterialState
.
disabled
,
!
widget
.
enabled
);
// If the button is disabled while a press gesture is currently ongoing,
// InkWell makes a call to handleHighlightChanged. This causes an exception
// because it calls setState in the middle of a build. To preempt this, we
// manually update pressed to false when this situation occurs.
if
(
_disabled
&&
_p
ressed
)
{
_handleHighlightChanged
(
false
);
if
(
isDisabled
&&
isP
ressed
)
{
removeMaterialState
(
MaterialState
.
pressed
);
}
}
double
get
_effectiveElevation
{
// These conditionals are in order of precedence, so be careful about
// reorganizing them.
if
(
_d
isabled
)
{
if
(
isD
isabled
)
{
return
widget
.
disabledElevation
;
}
if
(
_p
ressed
)
{
if
(
isP
ressed
)
{
return
widget
.
highlightElevation
;
}
if
(
_h
overed
)
{
if
(
isH
overed
)
{
return
widget
.
hoverElevation
;
}
if
(
_f
ocused
)
{
if
(
isF
ocused
)
{
return
widget
.
focusElevation
;
}
return
widget
.
elevation
;
...
...
@@ -390,13 +356,13 @@ class _RawMaterialButtonState extends State<RawMaterialButton> {
@override
Widget
build
(
BuildContext
context
)
{
final
Color
?
effectiveTextColor
=
MaterialStateProperty
.
resolveAs
<
Color
?>(
widget
.
textStyle
?.
color
,
_s
tates
);
final
ShapeBorder
?
effectiveShape
=
MaterialStateProperty
.
resolveAs
<
ShapeBorder
?>(
widget
.
shape
,
_s
tates
);
final
Color
?
effectiveTextColor
=
MaterialStateProperty
.
resolveAs
<
Color
?>(
widget
.
textStyle
?.
color
,
materialS
tates
);
final
ShapeBorder
?
effectiveShape
=
MaterialStateProperty
.
resolveAs
<
ShapeBorder
?>(
widget
.
shape
,
materialS
tates
);
final
Offset
densityAdjustment
=
widget
.
visualDensity
.
baseSizeAdjustment
;
final
BoxConstraints
effectiveConstraints
=
widget
.
visualDensity
.
effectiveConstraints
(
widget
.
constraints
);
final
MouseCursor
?
effectiveMouseCursor
=
MaterialStateProperty
.
resolveAs
<
MouseCursor
?>(
widget
.
mouseCursor
??
MaterialStateMouseCursor
.
clickable
,
_s
tates
,
materialS
tates
,
);
final
EdgeInsetsGeometry
padding
=
widget
.
padding
.
add
(
EdgeInsets
.
only
(
...
...
@@ -421,14 +387,14 @@ class _RawMaterialButtonState extends State<RawMaterialButton> {
child:
InkWell
(
focusNode:
widget
.
focusNode
,
canRequestFocus:
widget
.
enabled
,
onFocusChange:
_handleFocusedChanged
,
onFocusChange:
updateMaterialState
(
MaterialState
.
focused
)
,
autofocus:
widget
.
autofocus
,
onHighlightChanged:
_handleHighlightChanged
,
onHighlightChanged:
updateMaterialState
(
MaterialState
.
pressed
,
onChanged:
widget
.
onHighlightChanged
)
,
splashColor:
widget
.
splashColor
,
highlightColor:
widget
.
highlightColor
,
focusColor:
widget
.
focusColor
,
hoverColor:
widget
.
hoverColor
,
onHover:
_handleHoveredChanged
,
onHover:
updateMaterialState
(
MaterialState
.
hovered
)
,
onTap:
widget
.
onPressed
,
onLongPress:
widget
.
onLongPress
,
enableFeedback:
widget
.
enableFeedback
,
...
...
packages/flutter/lib/src/material/button_style_button.dart
View file @
02b46b0d
...
...
@@ -14,6 +14,7 @@ import 'constants.dart';
import
'ink_well.dart'
;
import
'material.dart'
;
import
'material_state.dart'
;
import
'material_state_mixin.dart'
;
import
'theme_data.dart'
;
/// The base [StatefulWidget] class for buttons whose style is defined by a [ButtonStyle] object.
...
...
@@ -176,49 +177,15 @@ abstract class ButtonStyleButton extends StatefulWidget {
/// * [TextButton], a simple button without a shadow.
/// * [ElevatedButton], a filled button whose material elevates when pressed.
/// * [OutlinedButton], similar to [TextButton], but with an outline.
class
_ButtonStyleState
extends
State
<
ButtonStyleButton
>
with
TickerProviderStateMixin
{
class
_ButtonStyleState
extends
State
<
ButtonStyleButton
>
with
MaterialStateMixin
,
TickerProviderStateMixin
{
AnimationController
?
_controller
;
double
?
_elevation
;
Color
?
_backgroundColor
;
final
Set
<
MaterialState
>
_states
=
<
MaterialState
>{};
bool
get
_hovered
=>
_states
.
contains
(
MaterialState
.
hovered
);
bool
get
_focused
=>
_states
.
contains
(
MaterialState
.
focused
);
bool
get
_pressed
=>
_states
.
contains
(
MaterialState
.
pressed
);
bool
get
_disabled
=>
_states
.
contains
(
MaterialState
.
disabled
);
void
_updateState
(
MaterialState
state
,
bool
value
)
{
value
?
_states
.
add
(
state
)
:
_states
.
remove
(
state
);
}
void
_handleHighlightChanged
(
bool
value
)
{
if
(
_pressed
!=
value
)
{
setState
(()
{
_updateState
(
MaterialState
.
pressed
,
value
);
});
}
}
void
_handleHoveredChanged
(
bool
value
)
{
if
(
_hovered
!=
value
)
{
setState
(()
{
_updateState
(
MaterialState
.
hovered
,
value
);
});
}
}
void
_handleFocusedChanged
(
bool
value
)
{
if
(
_focused
!=
value
)
{
setState
(()
{
_updateState
(
MaterialState
.
focused
,
value
);
});
}
}
@override
void
initState
()
{
super
.
initState
();
_update
State
(
MaterialState
.
disabled
,
!
widget
.
enabled
);
setMaterial
State
(
MaterialState
.
disabled
,
!
widget
.
enabled
);
}
@override
...
...
@@ -230,13 +197,13 @@ class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStat
@override
void
didUpdateWidget
(
ButtonStyleButton
oldWidget
)
{
super
.
didUpdateWidget
(
oldWidget
);
_update
State
(
MaterialState
.
disabled
,
!
widget
.
enabled
);
setMaterial
State
(
MaterialState
.
disabled
,
!
widget
.
enabled
);
// If the button is disabled while a press gesture is currently ongoing,
// InkWell makes a call to handleHighlightChanged. This causes an exception
// because it calls setState in the middle of a build. To preempt this, we
// manually update pressed to false when this situation occurs.
if
(
_disabled
&&
_p
ressed
)
{
_handleHighlightChanged
(
false
);
if
(
isDisabled
&&
isP
ressed
)
{
removeMaterialState
(
MaterialState
.
pressed
);
}
}
...
...
@@ -256,7 +223,7 @@ class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStat
T
?
resolve
<
T
>(
MaterialStateProperty
<
T
>?
Function
(
ButtonStyle
?
style
)
getProperty
)
{
return
effectiveValue
(
(
ButtonStyle
?
style
)
=>
getProperty
(
style
)?.
resolve
(
_s
tates
),
(
ButtonStyle
?
style
)
=>
getProperty
(
style
)?.
resolve
(
materialS
tates
),
);
}
...
...
@@ -367,13 +334,13 @@ class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStat
child:
InkWell
(
onTap:
widget
.
onPressed
,
onLongPress:
widget
.
onLongPress
,
onHighlightChanged:
_handleHighlightChanged
,
onHover:
_handleHoveredChanged
,
onHighlightChanged:
updateMaterialState
(
MaterialState
.
pressed
)
,
onHover:
updateMaterialState
(
MaterialState
.
hovered
)
,
mouseCursor:
resolvedMouseCursor
,
enableFeedback:
resolvedEnableFeedback
,
focusNode:
widget
.
focusNode
,
canRequestFocus:
widget
.
enabled
,
onFocusChange:
_handleFocusedChanged
,
onFocusChange:
updateMaterialState
(
MaterialState
.
focused
)
,
autofocus:
widget
.
autofocus
,
splashFactory:
resolvedSplashFactory
,
overlayColor:
overlayColor
,
...
...
packages/flutter/lib/src/material/chip.dart
View file @
02b46b0d
...
...
@@ -17,6 +17,7 @@ import 'ink_well.dart';
import
'material.dart'
;
import
'material_localizations.dart'
;
import
'material_state.dart'
;
import
'material_state_mixin.dart'
;
import
'theme.dart'
;
import
'theme_data.dart'
;
import
'tooltip.dart'
;
...
...
@@ -1631,7 +1632,7 @@ class RawChip extends StatefulWidget
State
<
RawChip
>
createState
()
=>
_RawChipState
();
}
class
_RawChipState
extends
State
<
RawChip
>
with
TickerProviderStateMixin
<
RawChip
>
{
class
_RawChipState
extends
State
<
RawChip
>
with
MaterialStateMixin
,
TickerProviderStateMixin
<
RawChip
>
{
static
const
Duration
pressedAnimationDuration
=
Duration
(
milliseconds:
75
);
late
AnimationController
selectController
;
...
...
@@ -1644,8 +1645,6 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
late
Animation
<
double
>
enableAnimation
;
late
Animation
<
double
>
selectionFade
;
final
Set
<
MaterialState
>
_states
=
<
MaterialState
>{};
final
GlobalKey
deleteIconKey
=
GlobalKey
();
bool
get
hasDeleteButton
=>
widget
.
onDeleted
!=
null
;
...
...
@@ -1664,8 +1663,8 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
void
initState
()
{
assert
(
widget
.
onSelected
==
null
||
widget
.
onPressed
==
null
);
super
.
initState
();
_update
State
(
MaterialState
.
disabled
,
!
widget
.
isEnabled
);
_update
State
(
MaterialState
.
selected
,
widget
.
selected
);
setMaterial
State
(
MaterialState
.
disabled
,
!
widget
.
isEnabled
);
setMaterial
State
(
MaterialState
.
selected
,
widget
.
selected
);
selectController
=
AnimationController
(
duration:
_kSelectDuration
,
value:
widget
.
selected
==
true
?
1.0
:
0.0
,
...
...
@@ -1736,17 +1735,13 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
super
.
dispose
();
}
void
_updateState
(
MaterialState
state
,
bool
value
)
{
value
?
_states
.
add
(
state
)
:
_states
.
remove
(
state
);
}
void
_handleTapDown
(
TapDownDetails
details
)
{
if
(!
canTap
)
{
return
;
}
setMaterialState
(
MaterialState
.
pressed
,
true
);
setState
(()
{
_isTapping
=
true
;
_updateState
(
MaterialState
.
pressed
,
true
);
});
}
...
...
@@ -1754,9 +1749,9 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
if
(!
canTap
)
{
return
;
}
setMaterialState
(
MaterialState
.
pressed
,
false
);
setState
(()
{
_isTapping
=
false
;
_updateState
(
MaterialState
.
pressed
,
false
);
});
}
...
...
@@ -1764,32 +1759,20 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
if
(!
canTap
)
{
return
;
}
setMaterialState
(
MaterialState
.
pressed
,
false
);
setState
(()
{
_isTapping
=
false
;
_updateState
(
MaterialState
.
pressed
,
false
);
});
// Only one of these can be set, so only one will be called.
widget
.
onSelected
?.
call
(!
widget
.
selected
);
widget
.
onPressed
?.
call
();
}
void
_handleFocus
(
bool
isFocused
)
{
setState
(()
{
_updateState
(
MaterialState
.
focused
,
isFocused
);
});
}
void
_handleHover
(
bool
isHovered
)
{
setState
(()
{
_updateState
(
MaterialState
.
hovered
,
isHovered
);
});
}
OutlinedBorder
_getShape
(
ChipThemeData
theme
)
{
final
BorderSide
?
resolvedSide
=
MaterialStateProperty
.
resolveAs
<
BorderSide
?>(
widget
.
side
,
_s
tates
)
??
MaterialStateProperty
.
resolveAs
<
BorderSide
?>(
theme
.
side
,
_s
tates
);
final
OutlinedBorder
resolvedShape
=
MaterialStateProperty
.
resolveAs
<
OutlinedBorder
?>(
widget
.
shape
,
_s
tates
)
??
MaterialStateProperty
.
resolveAs
<
OutlinedBorder
?>(
theme
.
shape
,
_s
tates
)
final
BorderSide
?
resolvedSide
=
MaterialStateProperty
.
resolveAs
<
BorderSide
?>(
widget
.
side
,
materialS
tates
)
??
MaterialStateProperty
.
resolveAs
<
BorderSide
?>(
theme
.
side
,
materialS
tates
);
final
OutlinedBorder
resolvedShape
=
MaterialStateProperty
.
resolveAs
<
OutlinedBorder
?>(
widget
.
shape
,
materialS
tates
)
??
MaterialStateProperty
.
resolveAs
<
OutlinedBorder
?>(
theme
.
shape
,
materialS
tates
)
??
const
StadiumBorder
();
return
resolvedShape
.
copyWith
(
side:
resolvedSide
);
}
...
...
@@ -1813,7 +1796,7 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
super
.
didUpdateWidget
(
oldWidget
);
if
(
oldWidget
.
isEnabled
!=
widget
.
isEnabled
)
{
setState
(()
{
_update
State
(
MaterialState
.
disabled
,
!
widget
.
isEnabled
);
setMaterial
State
(
MaterialState
.
disabled
,
!
widget
.
isEnabled
);
if
(
widget
.
isEnabled
)
{
enableController
.
forward
();
}
else
{
...
...
@@ -1832,7 +1815,7 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
}
if
(
oldWidget
.
selected
!=
widget
.
selected
)
{
setState
(()
{
_update
State
(
MaterialState
.
selected
,
widget
.
selected
);
setMaterial
State
(
MaterialState
.
selected
,
widget
.
selected
);
if
(
widget
.
selected
==
true
)
{
selectController
.
forward
();
}
else
{
...
...
@@ -1932,7 +1915,7 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
final
bool
showCheckmark
=
widget
.
showCheckmark
??
chipTheme
.
showCheckmark
??
true
;
final
TextStyle
effectiveLabelStyle
=
chipTheme
.
labelStyle
.
merge
(
widget
.
labelStyle
);
final
Color
?
resolvedLabelColor
=
MaterialStateProperty
.
resolveAs
<
Color
?>(
effectiveLabelStyle
.
color
,
_s
tates
);
final
Color
?
resolvedLabelColor
=
MaterialStateProperty
.
resolveAs
<
Color
?>(
effectiveLabelStyle
.
color
,
materialS
tates
);
final
TextStyle
resolvedLabelStyle
=
effectiveLabelStyle
.
copyWith
(
color:
resolvedLabelColor
);
final
EdgeInsetsGeometry
labelPadding
=
widget
.
labelPadding
??
chipTheme
.
labelPadding
??
_defaultLabelPadding
;
...
...
@@ -1943,14 +1926,14 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
shape:
resolvedShape
,
clipBehavior:
widget
.
clipBehavior
,
child:
InkWell
(
onFocusChange:
_handleFocus
,
onFocusChange:
updateMaterialState
(
MaterialState
.
focused
)
,
focusNode:
widget
.
focusNode
,
autofocus:
widget
.
autofocus
,
canRequestFocus:
widget
.
isEnabled
,
onTap:
canTap
?
_handleTap
:
null
,
onTapDown:
canTap
?
_handleTapDown
:
null
,
onTapCancel:
canTap
?
_handleTapCancel
:
null
,
onHover:
canTap
?
_handleHover
:
null
,
onHover:
canTap
?
updateMaterialState
(
MaterialState
.
hovered
)
:
null
,
splashFactory:
_LocationAwareInkRippleFactory
(
hasDeleteButton
,
context
,
...
...
packages/flutter/lib/src/material/material_state_mixin.dart
0 → 100644
View file @
02b46b0d
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/foundation.dart'
;
import
'package:flutter/widgets.dart'
;
import
'material_state.dart'
;
/// Mixin for [State] classes that require knowledge of changing [MaterialState]
/// values for their child widgets.
///
/// This mixin does nothing by mere application to a [State] class, but is
/// helpful when writing `build` methods that include child [InkWell],
/// [GestureDetector], [MouseRegion], or [Focus] widgets. Instead of manually
/// creating handlers for each type of user interaction, such [State] classes can
/// instead provide a `ValueChanged<bool>` function and allow [MaterialStateMixin]
/// to manage the set of active [MaterialState]s, and the calling of [setState]
/// as necessary.
///
/// {@tool snippet}
/// This example shows how to write a [StatefulWidget] that uses the
/// [MaterialStateMixin] class to watch [MaterialState] values.
///
/// ```dart
/// class MyWidget extends StatefulWidget {
/// const MyWidget({required this.color, required this.child, Key? key}) : super(key: key);
///
/// final MaterialStateColor color;
/// final Widget child;
///
/// @override
/// State<MyWidget> createState() => MyWidgetState();
/// }
///
/// class MyWidgetState extends State<MyWidget> with MaterialStateMixin<MyWidget> {
/// @override
/// Widget build(BuildContext context) {
/// return InkWell(
/// onFocusChange: updateMaterialState(MaterialState.focused),
/// child: Container(
/// color: widget.color.resolve(materialStates),
/// child: widget.child,
/// ),
/// );
/// }
/// }
/// ```
/// {@end-tool}
@optionalTypeArgs
mixin
MaterialStateMixin
<
T
extends
StatefulWidget
>
on
State
<
T
>
{
/// Managed set of active [MaterialState] values; designed to be passed to
/// [MaterialStateProperty.resolve] methods.
///
/// To mutate and have [setState] called automatically for you, use
/// [setMaterialState], [addMaterialState], or [removeMaterialState]. Directly
/// mutating the set is possible, and may be necessary if you need to alter its
/// list without calling [setState] (and thus triggering a re-render).
///
/// To check for a single condition, convenience getters [isPressed], [isHovered],
/// [isFocused], etc, are available for each [MaterialState] value.
@protected
Set
<
MaterialState
>
materialStates
=
<
MaterialState
>{};
/// Callback factory which accepts a [MaterialState] value and returns a
/// closure to mutate [materialStates] and call [setState].
///
/// Accepts an optional second named parameter, `onChanged`, which allows
/// arbitrary functionality to be wired through the [MaterialStateMixin].
/// If supplied, the [onChanged] function is only called when child widgets
/// report events that make changes to the current set of [MaterialState]s.
///
/// {@tool snippet}
/// This example shows how to use the [updateMaterialState] callback factory
/// in other widgets, including the optional [onChanged] callback.
///
/// ```dart
/// class MyWidget extends StatefulWidget {
/// const MyWidget({this.onPressed, Key? key}) : super(key: key);
///
/// /// Something important this widget must do when pressed.
/// final VoidCallback? onPressed;
///
/// @override
/// State<MyWidget> createState() => MyWidgetState();
/// }
///
/// class MyWidgetState extends State<MyWidget> with MaterialStateMixin<MyWidget> {
/// @override
/// Widget build(BuildContext context) {
/// return Container(
/// color: isPressed ? Colors.black : Colors.white,
/// child: InkWell(
/// onHighlightChanged: updateMaterialState(
/// MaterialState.pressed,
/// onChanged: (bool val) {
/// if (val) {
/// widget.onPressed?.call();
/// }
/// },
/// ),
/// ),
/// );
/// }
/// }
/// ```
/// {@end-tool}
@protected
ValueChanged
<
bool
>
updateMaterialState
(
MaterialState
key
,
{
ValueChanged
<
bool
>?
onChanged
})
{
return
(
bool
value
)
{
if
(
materialStates
.
contains
(
key
)
==
value
)
return
;
setMaterialState
(
key
,
value
);
onChanged
?.
call
(
value
);
};
}
/// Mutator to mark a [MaterialState] value as either active or inactive.
@protected
void
setMaterialState
(
MaterialState
_state
,
bool
isSet
)
{
return
isSet
?
addMaterialState
(
_state
)
:
removeMaterialState
(
_state
);
}
/// Mutator to mark a [MaterialState] value as active.
@protected
void
addMaterialState
(
MaterialState
_state
)
{
if
(
materialStates
.
add
(
_state
))
setState
((){});
}
/// Mutator to mark a [MaterialState] value as inactive.
@protected
void
removeMaterialState
(
MaterialState
_state
)
{
if
(
materialStates
.
remove
(
_state
))
setState
((){});
}
/// Getter for whether this class considers [MaterialState.disabled] to be active.
bool
get
isDisabled
=>
materialStates
.
contains
(
MaterialState
.
disabled
);
/// Getter for whether this class considers [MaterialState.dragged] to be active.
bool
get
isDragged
=>
materialStates
.
contains
(
MaterialState
.
dragged
);
/// Getter for whether this class considers [MaterialState.error] to be active.
bool
get
isErrored
=>
materialStates
.
contains
(
MaterialState
.
error
);
/// Getter for whether this class considers [MaterialState.focused] to be active.
bool
get
isFocused
=>
materialStates
.
contains
(
MaterialState
.
focused
);
/// Getter for whether this class considers [MaterialState.hovered] to be active.
bool
get
isHovered
=>
materialStates
.
contains
(
MaterialState
.
hovered
);
/// Getter for whether this class considers [MaterialState.pressed] to be active.
bool
get
isPressed
=>
materialStates
.
contains
(
MaterialState
.
pressed
);
/// Getter for whether this class considers [MaterialState.scrolledUnder] to be active.
bool
get
isScrolledUnder
=>
materialStates
.
contains
(
MaterialState
.
scrolledUnder
);
/// Getter for whether this class considers [MaterialState.selected] to be active.
bool
get
isSelected
=>
materialStates
.
contains
(
MaterialState
.
selected
);
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
super
.
debugFillProperties
(
properties
);
properties
.
add
(
DiagnosticsProperty
<
Set
<
MaterialState
>>(
'materialStates'
,
materialStates
,
defaultValue:
<
MaterialState
>{}));
}
}
packages/flutter/test/material/material_state_mixin_test.dart
0 → 100644
View file @
02b46b0d
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:async'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
const
Key
key
=
Key
(
'testContainer'
);
const
Color
trueColor
=
Colors
.
red
;
const
Color
falseColor
=
Colors
.
green
;
/// Mock widget which plays the role of a button -- it can emit notifications
/// that [MaterialState] values are now in or out of play.
class
_InnerWidget
extends
StatefulWidget
{
const
_InnerWidget
({
required
this
.
onValueChanged
,
required
this
.
controller
,
Key
?
key
})
:
super
(
key:
key
);
final
ValueChanged
<
bool
>
onValueChanged
;
final
StreamController
<
bool
>
controller
;
@override
_InnerWidgetState
createState
()
=>
_InnerWidgetState
();
}
class
_InnerWidgetState
extends
State
<
_InnerWidget
>
{
@override
void
initState
()
{
super
.
initState
();
widget
.
controller
.
stream
.
listen
((
bool
val
)
=>
widget
.
onValueChanged
(
val
));
}
@override
Widget
build
(
BuildContext
context
)
=>
Container
();
}
class
_MyWidget
extends
StatefulWidget
{
const
_MyWidget
({
required
this
.
controller
,
required
this
.
evaluator
,
required
this
.
materialState
,
Key
?
key
,
})
:
super
(
key:
key
);
/// Wrapper around `MaterialStateMixin.isPressed/isHovered/isFocused/etc`.
final
bool
Function
(
_MyWidgetState
state
)
evaluator
;
/// Stream passed down to the child [_InnerWidget] to begin the process.
/// This plays the role of an actual user interaction in the wild, but allows
/// us to engage the system without mocking pointers/hovers etc.
final
StreamController
<
bool
>
controller
;
/// The value we're watching in the given test.
final
MaterialState
materialState
;
@override
State
createState
()
=>
_MyWidgetState
();
}
class
_MyWidgetState
extends
State
<
_MyWidget
>
with
MaterialStateMixin
{
@override
Widget
build
(
BuildContext
context
)
{
return
Container
(
key:
key
,
color:
widget
.
evaluator
(
this
)
?
trueColor
:
falseColor
,
child:
_InnerWidget
(
onValueChanged:
updateMaterialState
(
widget
.
materialState
),
controller:
widget
.
controller
,
),
);
}
}
void
main
(
)
{
Future
<
void
>
_verify
(
WidgetTester
tester
,
Widget
widget
,
StreamController
<
bool
>
controller
,)
async
{
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
body:
widget
)));
// Set the value to True
controller
.
sink
.
add
(
true
);
await
tester
.
pumpAndSettle
();
expect
(
tester
.
widget
<
Container
>(
find
.
byKey
(
key
)).
color
,
trueColor
);
// Set the value to False
controller
.
sink
.
add
(
false
);
await
tester
.
pumpAndSettle
();
expect
(
tester
.
widget
<
Container
>(
find
.
byKey
(
key
)).
color
,
falseColor
);
}
testWidgets
(
'MaterialState.pressed is tracked'
,
(
WidgetTester
tester
)
async
{
final
StreamController
<
bool
>
controller
=
StreamController
<
bool
>();
final
_MyWidget
widget
=
_MyWidget
(
controller:
controller
,
evaluator:
(
_MyWidgetState
state
)
=>
state
.
isPressed
,
materialState:
MaterialState
.
pressed
,
);
await
_verify
(
tester
,
widget
,
controller
);
});
testWidgets
(
'MaterialState.focused is tracked'
,
(
WidgetTester
tester
)
async
{
final
StreamController
<
bool
>
controller
=
StreamController
<
bool
>();
final
_MyWidget
widget
=
_MyWidget
(
controller:
controller
,
evaluator:
(
_MyWidgetState
state
)
=>
state
.
isFocused
,
materialState:
MaterialState
.
focused
,
);
await
_verify
(
tester
,
widget
,
controller
);
});
testWidgets
(
'MaterialState.hovered is tracked'
,
(
WidgetTester
tester
)
async
{
final
StreamController
<
bool
>
controller
=
StreamController
<
bool
>();
final
_MyWidget
widget
=
_MyWidget
(
controller:
controller
,
evaluator:
(
_MyWidgetState
state
)
=>
state
.
isHovered
,
materialState:
MaterialState
.
hovered
,
);
await
_verify
(
tester
,
widget
,
controller
);
});
testWidgets
(
'MaterialState.disabled is tracked'
,
(
WidgetTester
tester
)
async
{
final
StreamController
<
bool
>
controller
=
StreamController
<
bool
>();
final
_MyWidget
widget
=
_MyWidget
(
controller:
controller
,
evaluator:
(
_MyWidgetState
state
)
=>
state
.
isDisabled
,
materialState:
MaterialState
.
disabled
,
);
await
_verify
(
tester
,
widget
,
controller
);
});
testWidgets
(
'MaterialState.selected is tracked'
,
(
WidgetTester
tester
)
async
{
final
StreamController
<
bool
>
controller
=
StreamController
<
bool
>();
final
_MyWidget
widget
=
_MyWidget
(
controller:
controller
,
evaluator:
(
_MyWidgetState
state
)
=>
state
.
isSelected
,
materialState:
MaterialState
.
selected
,
);
await
_verify
(
tester
,
widget
,
controller
);
});
testWidgets
(
'MaterialState.scrolledUnder is tracked'
,
(
WidgetTester
tester
)
async
{
final
StreamController
<
bool
>
controller
=
StreamController
<
bool
>();
final
_MyWidget
widget
=
_MyWidget
(
controller:
controller
,
evaluator:
(
_MyWidgetState
state
)
=>
state
.
isScrolledUnder
,
materialState:
MaterialState
.
scrolledUnder
,
);
await
_verify
(
tester
,
widget
,
controller
);
});
testWidgets
(
'MaterialState.dragged is tracked'
,
(
WidgetTester
tester
)
async
{
final
StreamController
<
bool
>
controller
=
StreamController
<
bool
>();
final
_MyWidget
widget
=
_MyWidget
(
controller:
controller
,
evaluator:
(
_MyWidgetState
state
)
=>
state
.
isDragged
,
materialState:
MaterialState
.
dragged
,
);
await
_verify
(
tester
,
widget
,
controller
);
});
testWidgets
(
'MaterialState.error is tracked'
,
(
WidgetTester
tester
)
async
{
final
StreamController
<
bool
>
controller
=
StreamController
<
bool
>();
final
_MyWidget
widget
=
_MyWidget
(
controller:
controller
,
evaluator:
(
_MyWidgetState
state
)
=>
state
.
isErrored
,
materialState:
MaterialState
.
error
,
);
await
_verify
(
tester
,
widget
,
controller
);
});
}
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