mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Revert ActivateAction PR (#41945)
This commit is contained in:
parent
780bddcca5
commit
3e2dc8ca7b
@ -247,8 +247,8 @@ abstract class UndoableAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
class UndoableFocusActionBase extends UndoableAction {
|
||||
UndoableFocusActionBase(LocalKey name) : super(name);
|
||||
class SetFocusActionBase extends UndoableAction {
|
||||
SetFocusActionBase(LocalKey name) : super(name);
|
||||
|
||||
FocusNode _previousFocus;
|
||||
|
||||
@ -286,8 +286,10 @@ class UndoableFocusActionBase extends UndoableAction {
|
||||
}
|
||||
}
|
||||
|
||||
class UndoableRequestFocusAction extends UndoableFocusActionBase {
|
||||
UndoableRequestFocusAction() : super(RequestFocusAction.key);
|
||||
class SetFocusAction extends SetFocusActionBase {
|
||||
SetFocusAction() : super(key);
|
||||
|
||||
static const LocalKey key = ValueKey<Type>(SetFocusAction);
|
||||
|
||||
@override
|
||||
void invoke(FocusNode node, Intent intent) {
|
||||
@ -297,8 +299,10 @@ class UndoableRequestFocusAction extends UndoableFocusActionBase {
|
||||
}
|
||||
|
||||
/// Actions for manipulating focus.
|
||||
class UndoableNextFocusAction extends UndoableFocusActionBase {
|
||||
UndoableNextFocusAction() : super(NextFocusAction.key);
|
||||
class NextFocusAction extends SetFocusActionBase {
|
||||
NextFocusAction() : super(key);
|
||||
|
||||
static const LocalKey key = ValueKey<Type>(NextFocusAction);
|
||||
|
||||
@override
|
||||
void invoke(FocusNode node, Intent intent) {
|
||||
@ -307,8 +311,10 @@ class UndoableNextFocusAction extends UndoableFocusActionBase {
|
||||
}
|
||||
}
|
||||
|
||||
class UndoablePreviousFocusAction extends UndoableFocusActionBase {
|
||||
UndoablePreviousFocusAction() : super(PreviousFocusAction.key);
|
||||
class PreviousFocusAction extends SetFocusActionBase {
|
||||
PreviousFocusAction() : super(key);
|
||||
|
||||
static const LocalKey key = ValueKey<Type>(PreviousFocusAction);
|
||||
|
||||
@override
|
||||
void invoke(FocusNode node, Intent intent) {
|
||||
@ -317,8 +323,16 @@ class UndoablePreviousFocusAction extends UndoableFocusActionBase {
|
||||
}
|
||||
}
|
||||
|
||||
class UndoableDirectionalFocusAction extends UndoableFocusActionBase {
|
||||
UndoableDirectionalFocusAction() : super(DirectionalFocusAction.key);
|
||||
class DirectionalFocusIntent extends Intent {
|
||||
const DirectionalFocusIntent(this.direction) : super(DirectionalFocusAction.key);
|
||||
|
||||
final TraversalDirection direction;
|
||||
}
|
||||
|
||||
class DirectionalFocusAction extends SetFocusActionBase {
|
||||
DirectionalFocusAction() : super(key);
|
||||
|
||||
static const LocalKey key = ValueKey<Type>(DirectionalFocusAction);
|
||||
|
||||
TraversalDirection direction;
|
||||
|
||||
@ -352,7 +366,7 @@ class _DemoButtonState extends State<DemoButton> {
|
||||
void _handleOnPressed() {
|
||||
print('Button ${widget.name} pressed.');
|
||||
setState(() {
|
||||
Actions.invoke(context, const Intent(RequestFocusAction.key), focusNode: _focusNode);
|
||||
Actions.invoke(context, const Intent(SetFocusAction.key), focusNode: _focusNode);
|
||||
});
|
||||
}
|
||||
|
||||
@ -420,91 +434,101 @@ class _FocusDemoState extends State<FocusDemo> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final TextTheme textTheme = Theme.of(context).textTheme;
|
||||
return Actions(
|
||||
dispatcher: dispatcher,
|
||||
actions: <LocalKey, ActionFactory>{
|
||||
RequestFocusAction.key: () => UndoableRequestFocusAction(),
|
||||
NextFocusAction.key: () => UndoableNextFocusAction(),
|
||||
PreviousFocusAction.key: () => UndoablePreviousFocusAction(),
|
||||
DirectionalFocusAction.key: () => UndoableDirectionalFocusAction(),
|
||||
kUndoActionKey: () => kUndoAction,
|
||||
kRedoActionKey: () => kRedoAction,
|
||||
return Shortcuts(
|
||||
shortcuts: <LogicalKeySet, Intent>{
|
||||
LogicalKeySet(LogicalKeyboardKey.tab): const Intent(NextFocusAction.key),
|
||||
LogicalKeySet(LogicalKeyboardKey.shift, LogicalKeyboardKey.tab): const Intent(PreviousFocusAction.key),
|
||||
LogicalKeySet(LogicalKeyboardKey.arrowUp): const DirectionalFocusIntent(TraversalDirection.up),
|
||||
LogicalKeySet(LogicalKeyboardKey.arrowDown): const DirectionalFocusIntent(TraversalDirection.down),
|
||||
LogicalKeySet(LogicalKeyboardKey.arrowLeft): const DirectionalFocusIntent(TraversalDirection.left),
|
||||
LogicalKeySet(LogicalKeyboardKey.arrowRight): const DirectionalFocusIntent(TraversalDirection.right),
|
||||
},
|
||||
child: DefaultFocusTraversal(
|
||||
policy: ReadingOrderTraversalPolicy(),
|
||||
child: Shortcuts(
|
||||
shortcuts: <LogicalKeySet, Intent>{
|
||||
LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.shift, LogicalKeyboardKey.keyZ): kRedoIntent,
|
||||
LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyZ): kUndoIntent,
|
||||
},
|
||||
child: FocusScope(
|
||||
debugLabel: 'Scope',
|
||||
autofocus: true,
|
||||
child: DefaultTextStyle(
|
||||
style: textTheme.display1,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Actions Demo'),
|
||||
),
|
||||
body: Center(
|
||||
child: Builder(builder: (BuildContext context) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: const <Widget>[
|
||||
DemoButton(name: 'One'),
|
||||
DemoButton(name: 'Two'),
|
||||
DemoButton(name: 'Three'),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: const <Widget>[
|
||||
DemoButton(name: 'Four'),
|
||||
DemoButton(name: 'Five'),
|
||||
DemoButton(name: 'Six'),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: const <Widget>[
|
||||
DemoButton(name: 'Seven'),
|
||||
DemoButton(name: 'Eight'),
|
||||
DemoButton(name: 'Nine'),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: RaisedButton(
|
||||
child: const Text('UNDO'),
|
||||
onPressed: canUndo
|
||||
? () {
|
||||
Actions.invoke(context, kUndoIntent);
|
||||
}
|
||||
: null,
|
||||
child: Actions(
|
||||
dispatcher: dispatcher,
|
||||
actions: <LocalKey, ActionFactory>{
|
||||
SetFocusAction.key: () => SetFocusAction(),
|
||||
NextFocusAction.key: () => NextFocusAction(),
|
||||
PreviousFocusAction.key: () => PreviousFocusAction(),
|
||||
DirectionalFocusAction.key: () => DirectionalFocusAction(),
|
||||
kUndoActionKey: () => kUndoAction,
|
||||
kRedoActionKey: () => kRedoAction,
|
||||
},
|
||||
child: DefaultFocusTraversal(
|
||||
policy: ReadingOrderTraversalPolicy(),
|
||||
child: Shortcuts(
|
||||
shortcuts: <LogicalKeySet, Intent>{
|
||||
LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.shift, LogicalKeyboardKey.keyZ): kRedoIntent,
|
||||
LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyZ): kUndoIntent,
|
||||
},
|
||||
child: FocusScope(
|
||||
debugLabel: 'Scope',
|
||||
autofocus: true,
|
||||
child: DefaultTextStyle(
|
||||
style: textTheme.display1,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Actions Demo'),
|
||||
),
|
||||
body: Center(
|
||||
child: Builder(builder: (BuildContext context) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: const <Widget>[
|
||||
DemoButton(name: 'One'),
|
||||
DemoButton(name: 'Two'),
|
||||
DemoButton(name: 'Three'),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: const <Widget>[
|
||||
DemoButton(name: 'Four'),
|
||||
DemoButton(name: 'Five'),
|
||||
DemoButton(name: 'Six'),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: const <Widget>[
|
||||
DemoButton(name: 'Seven'),
|
||||
DemoButton(name: 'Eight'),
|
||||
DemoButton(name: 'Nine'),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: RaisedButton(
|
||||
child: const Text('UNDO'),
|
||||
onPressed: canUndo
|
||||
? () {
|
||||
Actions.invoke(context, kUndoIntent);
|
||||
}
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: RaisedButton(
|
||||
child: const Text('REDO'),
|
||||
onPressed: canRedo
|
||||
? () {
|
||||
Actions.invoke(context, kRedoIntent);
|
||||
}
|
||||
: null,
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: RaisedButton(
|
||||
child: const Text('REDO'),
|
||||
onPressed: canRedo
|
||||
? () {
|
||||
Actions.invoke(context, kRedoIntent);
|
||||
}
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -474,43 +474,45 @@ class _BottomNavigationTile extends StatelessWidget {
|
||||
child: Semantics(
|
||||
container: true,
|
||||
selected: selected,
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
InkResponse(
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: topPadding, bottom: bottomPadding),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
_TileIcon(
|
||||
colorTween: colorTween,
|
||||
animation: animation,
|
||||
iconSize: iconSize,
|
||||
selected: selected,
|
||||
item: item,
|
||||
selectedIconTheme: selectedIconTheme,
|
||||
unselectedIconTheme: unselectedIconTheme,
|
||||
),
|
||||
_Label(
|
||||
colorTween: colorTween,
|
||||
animation: animation,
|
||||
item: item,
|
||||
selectedLabelStyle: selectedLabelStyle,
|
||||
unselectedLabelStyle: unselectedLabelStyle,
|
||||
showSelectedLabels: showSelectedLabels,
|
||||
showUnselectedLabels: showUnselectedLabels,
|
||||
),
|
||||
],
|
||||
child: Focus(
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
InkResponse(
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: topPadding, bottom: bottomPadding),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
_TileIcon(
|
||||
colorTween: colorTween,
|
||||
animation: animation,
|
||||
iconSize: iconSize,
|
||||
selected: selected,
|
||||
item: item,
|
||||
selectedIconTheme: selectedIconTheme,
|
||||
unselectedIconTheme: unselectedIconTheme,
|
||||
),
|
||||
_Label(
|
||||
colorTween: colorTween,
|
||||
animation: animation,
|
||||
item: item,
|
||||
selectedLabelStyle: selectedLabelStyle,
|
||||
unselectedLabelStyle: unselectedLabelStyle,
|
||||
showSelectedLabels: showSelectedLabels,
|
||||
showUnselectedLabels: showUnselectedLabels,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Semantics(
|
||||
label: indexLabel,
|
||||
),
|
||||
],
|
||||
Semantics(
|
||||
label: indexLabel,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -330,37 +330,39 @@ class _RawMaterialButtonState extends State<RawMaterialButton> {
|
||||
final Color effectiveTextColor = MaterialStateProperty.resolveAs<Color>(widget.textStyle?.color, _states);
|
||||
final ShapeBorder effectiveShape = MaterialStateProperty.resolveAs<ShapeBorder>(widget.shape, _states);
|
||||
|
||||
final Widget result = ConstrainedBox(
|
||||
constraints: widget.constraints,
|
||||
child: Material(
|
||||
elevation: _effectiveElevation,
|
||||
textStyle: widget.textStyle?.copyWith(color: effectiveTextColor),
|
||||
shape: effectiveShape,
|
||||
color: widget.fillColor,
|
||||
type: widget.fillColor == null ? MaterialType.transparency : MaterialType.button,
|
||||
animationDuration: widget.animationDuration,
|
||||
clipBehavior: widget.clipBehavior,
|
||||
child: InkWell(
|
||||
focusNode: widget.focusNode,
|
||||
canRequestFocus: widget.enabled,
|
||||
onFocusChange: _handleFocusedChanged,
|
||||
autofocus: widget.autofocus,
|
||||
onHighlightChanged: _handleHighlightChanged,
|
||||
splashColor: widget.splashColor,
|
||||
highlightColor: widget.highlightColor,
|
||||
focusColor: widget.focusColor,
|
||||
hoverColor: widget.hoverColor,
|
||||
onHover: _handleHoveredChanged,
|
||||
onTap: widget.onPressed,
|
||||
customBorder: effectiveShape,
|
||||
child: IconTheme.merge(
|
||||
data: IconThemeData(color: effectiveTextColor),
|
||||
child: Container(
|
||||
padding: widget.padding,
|
||||
child: Center(
|
||||
widthFactor: 1.0,
|
||||
heightFactor: 1.0,
|
||||
child: widget.child,
|
||||
final Widget result = Focus(
|
||||
focusNode: widget.focusNode,
|
||||
canRequestFocus: widget.enabled,
|
||||
onFocusChange: _handleFocusedChanged,
|
||||
autofocus: widget.autofocus,
|
||||
child: ConstrainedBox(
|
||||
constraints: widget.constraints,
|
||||
child: Material(
|
||||
elevation: _effectiveElevation,
|
||||
textStyle: widget.textStyle?.copyWith(color: effectiveTextColor),
|
||||
shape: effectiveShape,
|
||||
color: widget.fillColor,
|
||||
type: widget.fillColor == null ? MaterialType.transparency : MaterialType.button,
|
||||
animationDuration: widget.animationDuration,
|
||||
clipBehavior: widget.clipBehavior,
|
||||
child: InkWell(
|
||||
onHighlightChanged: _handleHighlightChanged,
|
||||
splashColor: widget.splashColor,
|
||||
highlightColor: widget.highlightColor,
|
||||
focusColor: widget.focusColor,
|
||||
hoverColor: widget.hoverColor,
|
||||
onHover: _handleHoveredChanged,
|
||||
onTap: widget.onPressed,
|
||||
customBorder: effectiveShape,
|
||||
child: IconTheme.merge(
|
||||
data: IconThemeData(color: effectiveTextColor),
|
||||
child: Container(
|
||||
padding: widget.padding,
|
||||
child: Center(
|
||||
widthFactor: 1.0,
|
||||
heightFactor: 1.0,
|
||||
child: widget.child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -1774,71 +1774,73 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
|
||||
final Color resolvedLabelColor = MaterialStateProperty.resolveAs<Color>(effectiveLabelStyle?.color, _states);
|
||||
final TextStyle resolvedLabelStyle = effectiveLabelStyle?.copyWith(color: resolvedLabelColor);
|
||||
|
||||
Widget result = Material(
|
||||
elevation: isTapping ? pressElevation : elevation,
|
||||
shadowColor: widget.selected ? selectedShadowColor : shadowColor,
|
||||
animationDuration: pressedAnimationDuration,
|
||||
shape: shape,
|
||||
clipBehavior: widget.clipBehavior,
|
||||
child: InkWell(
|
||||
onFocusChange: _handleFocus,
|
||||
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,
|
||||
customBorder: shape,
|
||||
child: AnimatedBuilder(
|
||||
animation: Listenable.merge(<Listenable>[selectController, enableController]),
|
||||
builder: (BuildContext context, Widget child) {
|
||||
return Container(
|
||||
decoration: ShapeDecoration(
|
||||
shape: shape,
|
||||
color: getBackgroundColor(chipTheme),
|
||||
Widget result = Focus(
|
||||
onFocusChange: _handleFocus,
|
||||
focusNode: widget.focusNode,
|
||||
autofocus: widget.autofocus,
|
||||
canRequestFocus: widget.isEnabled,
|
||||
child: Material(
|
||||
elevation: isTapping ? pressElevation : elevation,
|
||||
shadowColor: widget.selected ? selectedShadowColor : shadowColor,
|
||||
animationDuration: pressedAnimationDuration,
|
||||
shape: shape,
|
||||
clipBehavior: widget.clipBehavior,
|
||||
child: InkWell(
|
||||
onTap: canTap ? _handleTap : null,
|
||||
onTapDown: canTap ? _handleTapDown : null,
|
||||
onTapCancel: canTap ? _handleTapCancel : null,
|
||||
onHover: canTap ? _handleHover : null,
|
||||
customBorder: shape,
|
||||
child: AnimatedBuilder(
|
||||
animation: Listenable.merge(<Listenable>[selectController, enableController]),
|
||||
builder: (BuildContext context, Widget child) {
|
||||
return Container(
|
||||
decoration: ShapeDecoration(
|
||||
shape: shape,
|
||||
color: getBackgroundColor(chipTheme),
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: _wrapWithTooltip(
|
||||
widget.tooltip,
|
||||
widget.onPressed,
|
||||
_ChipRenderWidget(
|
||||
theme: _ChipRenderTheme(
|
||||
label: DefaultTextStyle(
|
||||
overflow: TextOverflow.fade,
|
||||
textAlign: TextAlign.start,
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
style: resolvedLabelStyle,
|
||||
child: widget.label,
|
||||
),
|
||||
avatar: AnimatedSwitcher(
|
||||
child: widget.avatar,
|
||||
duration: _kDrawerDuration,
|
||||
switchInCurve: Curves.fastOutSlowIn,
|
||||
),
|
||||
deleteIcon: AnimatedSwitcher(
|
||||
child: _buildDeleteIcon(context, theme, chipTheme),
|
||||
duration: _kDrawerDuration,
|
||||
switchInCurve: Curves.fastOutSlowIn,
|
||||
),
|
||||
brightness: chipTheme.brightness,
|
||||
padding: (widget.padding ?? chipTheme.padding).resolve(textDirection),
|
||||
labelPadding: (widget.labelPadding ?? chipTheme.labelPadding).resolve(textDirection),
|
||||
showAvatar: hasAvatar,
|
||||
showCheckmark: showCheckmark,
|
||||
checkmarkColor: checkmarkColor,
|
||||
canTapBody: canTap,
|
||||
),
|
||||
value: widget.selected,
|
||||
checkmarkAnimation: checkmarkAnimation,
|
||||
enableAnimation: enableAnimation,
|
||||
avatarDrawerAnimation: avatarDrawerAnimation,
|
||||
deleteDrawerAnimation: deleteDrawerAnimation,
|
||||
isEnabled: widget.isEnabled,
|
||||
avatarBorder: widget.avatarBorder,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: _wrapWithTooltip(
|
||||
widget.tooltip,
|
||||
widget.onPressed,
|
||||
_ChipRenderWidget(
|
||||
theme: _ChipRenderTheme(
|
||||
label: DefaultTextStyle(
|
||||
overflow: TextOverflow.fade,
|
||||
textAlign: TextAlign.start,
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
style: resolvedLabelStyle,
|
||||
child: widget.label,
|
||||
),
|
||||
avatar: AnimatedSwitcher(
|
||||
child: widget.avatar,
|
||||
duration: _kDrawerDuration,
|
||||
switchInCurve: Curves.fastOutSlowIn,
|
||||
),
|
||||
deleteIcon: AnimatedSwitcher(
|
||||
child: _buildDeleteIcon(context, theme, chipTheme),
|
||||
duration: _kDrawerDuration,
|
||||
switchInCurve: Curves.fastOutSlowIn,
|
||||
),
|
||||
brightness: chipTheme.brightness,
|
||||
padding: (widget.padding ?? chipTheme.padding).resolve(textDirection),
|
||||
labelPadding: (widget.labelPadding ?? chipTheme.labelPadding).resolve(textDirection),
|
||||
showAvatar: hasAvatar,
|
||||
showCheckmark: showCheckmark,
|
||||
checkmarkColor: checkmarkColor,
|
||||
canTapBody: canTap,
|
||||
),
|
||||
value: widget.selected,
|
||||
checkmarkAnimation: checkmarkAnimation,
|
||||
enableAnimation: enableAnimation,
|
||||
avatarDrawerAnimation: avatarDrawerAnimation,
|
||||
deleteDrawerAnimation: deleteDrawerAnimation,
|
||||
isEnabled: widget.isEnabled,
|
||||
avatarBorder: widget.avatarBorder,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -309,20 +309,22 @@ class IconButton extends StatelessWidget {
|
||||
return Semantics(
|
||||
button: true,
|
||||
enabled: onPressed != null,
|
||||
child: InkResponse(
|
||||
child: Focus(
|
||||
focusNode: focusNode,
|
||||
autofocus: autofocus,
|
||||
canRequestFocus: onPressed != null,
|
||||
onTap: onPressed,
|
||||
child: result,
|
||||
focusColor: focusColor ?? Theme.of(context).focusColor,
|
||||
hoverColor: hoverColor ?? Theme.of(context).hoverColor,
|
||||
highlightColor: highlightColor ?? Theme.of(context).highlightColor,
|
||||
splashColor: splashColor ?? Theme.of(context).splashColor,
|
||||
radius: math.max(
|
||||
Material.defaultSplashRadius,
|
||||
(iconSize + math.min(padding.horizontal, padding.vertical)) * 0.7,
|
||||
// x 0.5 for diameter -> radius and + 40% overflow derived from other Material apps.
|
||||
child: InkResponse(
|
||||
onTap: onPressed,
|
||||
child: result,
|
||||
focusColor: focusColor ?? Theme.of(context).focusColor,
|
||||
hoverColor: hoverColor ?? Theme.of(context).hoverColor,
|
||||
highlightColor: highlightColor ?? Theme.of(context).highlightColor,
|
||||
splashColor: splashColor ?? Theme.of(context).splashColor,
|
||||
radius: math.max(
|
||||
Material.defaultSplashRadius,
|
||||
(iconSize + math.min(padding.horizontal, padding.vertical)) * 0.7,
|
||||
// x 0.5 for diameter -> radius and + 40% overflow derived from other Material apps.
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -210,16 +210,10 @@ class InkResponse extends StatefulWidget {
|
||||
this.splashFactory,
|
||||
this.enableFeedback = true,
|
||||
this.excludeFromSemantics = false,
|
||||
this.focusNode,
|
||||
this.canRequestFocus = true,
|
||||
this.onFocusChange,
|
||||
this.autofocus = false,
|
||||
}) : assert(containedInkWell != null),
|
||||
assert(highlightShape != null),
|
||||
assert(enableFeedback != null),
|
||||
assert(excludeFromSemantics != null),
|
||||
assert(autofocus != null),
|
||||
assert(canRequestFocus != null),
|
||||
super(key: key);
|
||||
|
||||
/// The widget below this widget in the tree.
|
||||
@ -406,21 +400,6 @@ class InkResponse extends StatefulWidget {
|
||||
/// duplication of information.
|
||||
final bool excludeFromSemantics;
|
||||
|
||||
/// Handler called when the focus changes.
|
||||
///
|
||||
/// Called with true if this widget's node gains focus, and false if it loses
|
||||
/// focus.
|
||||
final ValueChanged<bool> onFocusChange;
|
||||
|
||||
/// {@macro flutter.widgets.Focus.autofocus}
|
||||
final bool autofocus;
|
||||
|
||||
/// {@macro flutter.widgets.Focus.focusNode}
|
||||
final FocusNode focusNode;
|
||||
|
||||
/// {@template flutter.widgets.Focus.canRequestFocus}
|
||||
final bool canRequestFocus;
|
||||
|
||||
/// The rectangle to use for the highlight effect and for clipping
|
||||
/// the splash effects if [containedInkWell] is true.
|
||||
///
|
||||
@ -483,6 +462,7 @@ enum _HighlightType {
|
||||
class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKeepAliveClientMixin<T> {
|
||||
Set<InteractiveInkFeature> _splashes;
|
||||
InteractiveInkFeature _currentSplash;
|
||||
FocusNode _focusNode;
|
||||
bool _hovering = false;
|
||||
final Map<_HighlightType, InkHighlight> _highlights = <_HighlightType, InkHighlight>{};
|
||||
|
||||
@ -494,18 +474,27 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
|
||||
WidgetsBinding.instance.focusManager.addHighlightModeListener(_handleFocusHighlightModeChange);
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
_focusNode?.removeListener(_handleFocusUpdate);
|
||||
_focusNode = Focus.of(context, nullOk: true);
|
||||
_focusNode?.addListener(_handleFocusUpdate);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(InkResponse oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (_isWidgetEnabled(widget) != _isWidgetEnabled(oldWidget)) {
|
||||
_handleHoverChange(_hovering);
|
||||
_updateFocusHighlights();
|
||||
_handleFocusUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.focusManager.removeHighlightModeListener(_handleFocusHighlightModeChange);
|
||||
_focusNode?.removeListener(_handleFocusUpdate);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@ -571,7 +560,7 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
|
||||
}
|
||||
assert(value == (_highlights[type] != null && _highlights[type].active));
|
||||
|
||||
switch (type) {
|
||||
switch(type) {
|
||||
case _HighlightType.pressed:
|
||||
if (widget.onHighlightChanged != null)
|
||||
widget.onHighlightChanged(value);
|
||||
@ -585,10 +574,10 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
|
||||
}
|
||||
}
|
||||
|
||||
InteractiveInkFeature _createInkFeature(Offset globalPosition) {
|
||||
InteractiveInkFeature _createInkFeature(TapDownDetails details) {
|
||||
final MaterialInkController inkController = Material.of(context);
|
||||
final RenderBox referenceBox = context.findRenderObject();
|
||||
final Offset position = referenceBox.globalToLocal(globalPosition);
|
||||
final Offset position = referenceBox.globalToLocal(details.globalPosition);
|
||||
final Color color = widget.splashColor ?? Theme.of(context).splashColor;
|
||||
final RectCallback rectCallback = widget.containedInkWell ? widget.getRectCallback(referenceBox) : null;
|
||||
final BorderRadius borderRadius = widget.borderRadius;
|
||||
@ -627,54 +616,31 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_updateFocusHighlights();
|
||||
_handleFocusUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
void _updateFocusHighlights() {
|
||||
void _handleFocusUpdate() {
|
||||
bool showFocus;
|
||||
switch (WidgetsBinding.instance.focusManager.highlightMode) {
|
||||
case FocusHighlightMode.touch:
|
||||
showFocus = false;
|
||||
break;
|
||||
case FocusHighlightMode.traditional:
|
||||
showFocus = enabled && _hasFocus;
|
||||
showFocus = enabled && (Focus.of(context, nullOk: true)?.hasPrimaryFocus ?? false);
|
||||
break;
|
||||
}
|
||||
updateHighlight(_HighlightType.focus, value: showFocus);
|
||||
}
|
||||
|
||||
bool _hasFocus = false;
|
||||
void _handleFocusUpdate(bool hasFocus) {
|
||||
_hasFocus = hasFocus;
|
||||
_updateFocusHighlights();
|
||||
if (widget.onFocusChange != null) {
|
||||
widget.onFocusChange(hasFocus);
|
||||
}
|
||||
}
|
||||
|
||||
void _handleTapDown(TapDownDetails details) {
|
||||
_startSplash(details: details);
|
||||
if (widget.onTapDown != null) {
|
||||
widget.onTapDown(details);
|
||||
}
|
||||
}
|
||||
|
||||
void _startSplash({TapDownDetails details, BuildContext context}) {
|
||||
assert(details != null || context != null);
|
||||
|
||||
Offset globalPosition;
|
||||
if (context != null) {
|
||||
final RenderBox referenceBox = context.findRenderObject();
|
||||
assert(referenceBox.hasSize, 'InkResponse must be done with layout before starting a splash.');
|
||||
globalPosition = referenceBox.localToGlobal(referenceBox.paintBounds.center);
|
||||
} else {
|
||||
globalPosition = details.globalPosition;
|
||||
}
|
||||
final InteractiveInkFeature splash = _createInkFeature(globalPosition);
|
||||
final InteractiveInkFeature splash = _createInkFeature(details);
|
||||
_splashes ??= HashSet<InteractiveInkFeature>();
|
||||
_splashes.add(splash);
|
||||
_currentSplash = splash;
|
||||
if (widget.onTapDown != null) {
|
||||
widget.onTapDown(details);
|
||||
}
|
||||
updateKeepAlive();
|
||||
updateHighlight(_HighlightType.pressed, value: true);
|
||||
}
|
||||
@ -756,37 +722,18 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
|
||||
_highlights[type]?.color = getHighlightColorForType(type);
|
||||
}
|
||||
_currentSplash?.color = widget.splashColor ?? Theme.of(context).splashColor;
|
||||
return Actions(
|
||||
actions: <LocalKey, ActionFactory>{
|
||||
ActivateAction.key: () {
|
||||
return CallbackAction(
|
||||
ActivateAction.key,
|
||||
onInvoke: (FocusNode node, Intent intent) {
|
||||
_startSplash(context: node.context);
|
||||
_handleTap(node.context);
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
child: Focus(
|
||||
focusNode: widget.focusNode,
|
||||
canRequestFocus: widget.canRequestFocus,
|
||||
onFocusChange: _handleFocusUpdate,
|
||||
autofocus: widget.autofocus,
|
||||
child: MouseRegion(
|
||||
onEnter: enabled ? _handleMouseEnter : null,
|
||||
onExit: enabled ? _handleMouseExit : null,
|
||||
child: GestureDetector(
|
||||
onTapDown: enabled ? _handleTapDown : null,
|
||||
onTap: enabled ? () => _handleTap(context) : null,
|
||||
onTapCancel: enabled ? _handleTapCancel : null,
|
||||
onDoubleTap: widget.onDoubleTap != null ? _handleDoubleTap : null,
|
||||
onLongPress: widget.onLongPress != null ? () => _handleLongPress(context) : null,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
excludeFromSemantics: widget.excludeFromSemantics,
|
||||
child: widget.child,
|
||||
),
|
||||
),
|
||||
return MouseRegion(
|
||||
onEnter: enabled ? _handleMouseEnter : null,
|
||||
onExit: enabled ? _handleMouseExit : null,
|
||||
child: GestureDetector(
|
||||
onTapDown: enabled ? _handleTapDown : null,
|
||||
onTap: enabled ? () => _handleTap(context) : null,
|
||||
onTapCancel: enabled ? _handleTapCancel : null,
|
||||
onDoubleTap: widget.onDoubleTap != null ? _handleDoubleTap : null,
|
||||
onLongPress: widget.onLongPress != null ? () => _handleLongPress(context) : null,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: widget.child,
|
||||
excludeFromSemantics: widget.excludeFromSemantics,
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -907,10 +854,6 @@ class InkWell extends InkResponse {
|
||||
ShapeBorder customBorder,
|
||||
bool enableFeedback = true,
|
||||
bool excludeFromSemantics = false,
|
||||
FocusNode focusNode,
|
||||
bool canRequestFocus = true,
|
||||
ValueChanged<bool> onFocusChange,
|
||||
bool autofocus = false,
|
||||
}) : super(
|
||||
key: key,
|
||||
child: child,
|
||||
@ -933,9 +876,5 @@ class InkWell extends InkResponse {
|
||||
customBorder: customBorder,
|
||||
enableFeedback: enableFeedback ?? true,
|
||||
excludeFromSemantics: excludeFromSemantics ?? false,
|
||||
focusNode: focusNode,
|
||||
canRequestFocus: canRequestFocus ?? true,
|
||||
onFocusChange: onFocusChange,
|
||||
autofocus: autofocus ?? false,
|
||||
);
|
||||
}
|
||||
|
@ -344,20 +344,6 @@ class Actions extends InheritedWidget {
|
||||
return oldWidget.dispatcher != dispatcher || oldWidget.actions != actions;
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
if (other.runtimeType != runtimeType) {
|
||||
return false;
|
||||
}
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
return !updateShouldNotify(other);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => hashValues(dispatcher, actions);
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
@ -382,16 +368,3 @@ class DoNothingAction extends Action {
|
||||
@override
|
||||
void invoke(FocusNode node, Intent intent) { }
|
||||
}
|
||||
|
||||
/// An action that invokes the currently focused control.
|
||||
///
|
||||
/// This is an abstract class that serves as a base class for actions that
|
||||
/// activate a control. It is bound to [LogicalKeyboardKey.enter] in the default
|
||||
/// keyboard map in [WidgetsApp].
|
||||
abstract class ActivateAction extends Action {
|
||||
/// Creates a [ActivateAction] with a fixed [key];
|
||||
const ActivateAction() : super(key);
|
||||
|
||||
/// The [LocalKey] that uniquely identifies this action.
|
||||
static const LocalKey key = ValueKey<Type>(ActivateAction);
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import 'dart:collection' show HashMap;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'actions.dart';
|
||||
import 'banner.dart';
|
||||
@ -21,7 +20,6 @@ import 'navigator.dart';
|
||||
import 'pages.dart';
|
||||
import 'performance_overlay.dart';
|
||||
import 'semantics_debugger.dart';
|
||||
import 'shortcuts.dart';
|
||||
import 'text.dart';
|
||||
import 'title.dart';
|
||||
import 'widget_inspector.dart';
|
||||
@ -1197,33 +1195,18 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
|
||||
|
||||
assert(_debugCheckLocalizations(appLocale));
|
||||
|
||||
return Shortcuts(
|
||||
shortcuts: <LogicalKeySet, Intent>{
|
||||
LogicalKeySet(LogicalKeyboardKey.tab): const Intent(NextFocusAction.key),
|
||||
LogicalKeySet(LogicalKeyboardKey.shift, LogicalKeyboardKey.tab): const Intent(PreviousFocusAction.key),
|
||||
LogicalKeySet(LogicalKeyboardKey.arrowLeft): const DirectionalFocusIntent(TraversalDirection.left),
|
||||
LogicalKeySet(LogicalKeyboardKey.arrowRight): const DirectionalFocusIntent(TraversalDirection.right),
|
||||
LogicalKeySet(LogicalKeyboardKey.arrowDown): const DirectionalFocusIntent(TraversalDirection.down),
|
||||
LogicalKeySet(LogicalKeyboardKey.arrowUp): const DirectionalFocusIntent(TraversalDirection.up),
|
||||
LogicalKeySet(LogicalKeyboardKey.enter): const Intent(ActivateAction.key),
|
||||
return Actions(
|
||||
actions: <LocalKey, ActionFactory>{
|
||||
DoNothingAction.key: () => const DoNothingAction(),
|
||||
},
|
||||
child: Actions(
|
||||
actions: <LocalKey, ActionFactory>{
|
||||
DoNothingAction.key: () => const DoNothingAction(),
|
||||
RequestFocusAction.key: () => RequestFocusAction(),
|
||||
NextFocusAction.key: () => NextFocusAction(),
|
||||
PreviousFocusAction.key: () => PreviousFocusAction(),
|
||||
DirectionalFocusAction.key: () => DirectionalFocusAction(),
|
||||
},
|
||||
child: DefaultFocusTraversal(
|
||||
policy: ReadingOrderTraversalPolicy(),
|
||||
child: MediaQuery(
|
||||
data: MediaQueryData.fromWindow(WidgetsBinding.instance.window),
|
||||
child: Localizations(
|
||||
locale: appLocale,
|
||||
delegates: _localizationsDelegates.toList(),
|
||||
child: title,
|
||||
),
|
||||
child: DefaultFocusTraversal(
|
||||
policy: ReadingOrderTraversalPolicy(),
|
||||
child: MediaQuery(
|
||||
data: MediaQueryData.fromWindow(WidgetsBinding.instance.window),
|
||||
child: Localizations(
|
||||
locale: appLocale,
|
||||
delegates: _localizationsDelegates.toList(),
|
||||
child: title,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -146,11 +146,10 @@ class Focus extends StatefulWidget {
|
||||
this.onFocusChange,
|
||||
this.onKey,
|
||||
this.debugLabel,
|
||||
this.canRequestFocus = true,
|
||||
this.canRequestFocus,
|
||||
this.skipTraversal,
|
||||
}) : assert(child != null),
|
||||
assert(autofocus != null),
|
||||
assert(canRequestFocus != null),
|
||||
super(key: key);
|
||||
|
||||
/// A debug label for this widget.
|
||||
@ -187,7 +186,7 @@ class Focus extends StatefulWidget {
|
||||
|
||||
/// Handler called when the focus changes.
|
||||
///
|
||||
/// Called with true if this widget's node gains focus, and false if it loses
|
||||
/// Called with true if this node gains focus, and false if it loses
|
||||
/// focus.
|
||||
final ValueChanged<bool> onFocusChange;
|
||||
|
||||
@ -231,7 +230,6 @@ class Focus extends StatefulWidget {
|
||||
/// still be focused explicitly.
|
||||
final bool skipTraversal;
|
||||
|
||||
/// {@template flutter.widgets.Focus.canRequestFocus}
|
||||
/// If true, this widget may request the primary focus.
|
||||
///
|
||||
/// Defaults to true. Set to false if you want the [FocusNode] this widget
|
||||
@ -251,7 +249,6 @@ class Focus extends StatefulWidget {
|
||||
/// its descendants.
|
||||
/// - [FocusTraversalPolicy], a class that can be extended to describe a
|
||||
/// traversal policy.
|
||||
/// {@endtemplate}
|
||||
final bool canRequestFocus;
|
||||
|
||||
/// Returns the [focusNode] of the [Focus] that most tightly encloses the
|
||||
|
@ -2,12 +2,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/painting.dart';
|
||||
|
||||
import 'actions.dart';
|
||||
import 'basic.dart';
|
||||
import 'binding.dart';
|
||||
import 'focus_manager.dart';
|
||||
@ -793,138 +790,3 @@ class DefaultFocusTraversal extends InheritedWidget {
|
||||
@override
|
||||
bool updateShouldNotify(DefaultFocusTraversal oldWidget) => policy != oldWidget.policy;
|
||||
}
|
||||
|
||||
// A base class for all of the default actions that request focus for a node.
|
||||
class _RequestFocusActionBase extends Action {
|
||||
_RequestFocusActionBase(LocalKey name) : super(name);
|
||||
|
||||
FocusNode _previousFocus;
|
||||
|
||||
@override
|
||||
void invoke(FocusNode node, Intent tag) {
|
||||
_previousFocus = WidgetsBinding.instance.focusManager.primaryFocus;
|
||||
node.requestFocus();
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties.add(DiagnosticsProperty<FocusNode>('previous', _previousFocus));
|
||||
}
|
||||
}
|
||||
|
||||
/// An [Action] that requests the focus on the node it is invoked on.
|
||||
///
|
||||
/// This action can be used to request focus for a particular node, by calling
|
||||
/// [Action.invoke] like so:
|
||||
///
|
||||
/// ```dart
|
||||
/// Actions.invoke(context, const Intent(RequestFocusAction.key), focusNode: _focusNode);
|
||||
/// ```
|
||||
///
|
||||
/// Where the `_focusNode` is the node for which the focus will be requested.
|
||||
///
|
||||
/// The difference between requesting focus in this way versus calling
|
||||
/// [_focusNode.requestFocus] directly is that it will use the [Action]
|
||||
/// registered in the nearest [Actions] widget associated with [key] to make the
|
||||
/// request, rather than just requesting focus directly. This allows the action
|
||||
/// to have additional side effects, like logging, or undo and redo
|
||||
/// functionality.
|
||||
///
|
||||
/// However, this [RequestFocusAction] is the default action associated with the
|
||||
/// [key] in the [WidgetsApp], and it simply requests focus and has no side
|
||||
/// effects.
|
||||
class RequestFocusAction extends _RequestFocusActionBase {
|
||||
/// Creates a [RequestFocusAction] with a fixed [key].
|
||||
RequestFocusAction() : super(key);
|
||||
|
||||
/// The [LocalKey] that uniquely identifies this action to an [Intent].
|
||||
static const LocalKey key = ValueKey<Type>(RequestFocusAction);
|
||||
|
||||
@override
|
||||
void invoke(FocusNode node, Intent tag) {
|
||||
super.invoke(node, tag);
|
||||
node.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
/// An [Action] that moves the focus to the next focusable node in the focus
|
||||
/// order.
|
||||
///
|
||||
/// This action is the default action registered for the [key], and by default
|
||||
/// is bound to the [LogicalKeyboardKey.tab] key in the [WidgetsApp].
|
||||
class NextFocusAction extends _RequestFocusActionBase {
|
||||
/// Creates a [NextFocusAction] with a fixed [key];
|
||||
NextFocusAction() : super(key);
|
||||
|
||||
/// The [LocalKey] that uniquely identifies this action to an [Intent].
|
||||
static const LocalKey key = ValueKey<Type>(NextFocusAction);
|
||||
|
||||
@override
|
||||
void invoke(FocusNode node, Intent tag) {
|
||||
super.invoke(node, tag);
|
||||
node.nextFocus();
|
||||
}
|
||||
}
|
||||
|
||||
/// An [Action] that moves the focus to the previous focusable node in the focus
|
||||
/// order.
|
||||
///
|
||||
/// This action is the default action registered for the [key], and by default
|
||||
/// is bound to a combination of the [LogicalKeyboardKey.tab] key and the
|
||||
/// [LogicalKeyboardKey.shift] key in the [WidgetsApp].
|
||||
class PreviousFocusAction extends _RequestFocusActionBase {
|
||||
/// Creates a [PreviousFocusAction] with a fixed [key];
|
||||
PreviousFocusAction() : super(key);
|
||||
|
||||
/// The [LocalKey] that uniquely identifies this action to an [Intent].
|
||||
static const LocalKey key = ValueKey<Type>(PreviousFocusAction);
|
||||
|
||||
@override
|
||||
void invoke(FocusNode node, Intent tag) {
|
||||
super.invoke(node, tag);
|
||||
node.previousFocus();
|
||||
}
|
||||
}
|
||||
|
||||
/// An [Intent] that represents moving to the next focusable node in the given
|
||||
/// [direction].
|
||||
///
|
||||
/// This is the [Intent] bound by default to the [LogicalKeyboardKey.arrowUp],
|
||||
/// [LogicalKeyboardKey.arrowDown], [LogicalKeyboardKey.arrowLeft], and
|
||||
/// [LogicalKeyboardKey.arrowRight] keys in the [WidgetsApp], with the
|
||||
/// appropriate associated directions.
|
||||
class DirectionalFocusIntent extends Intent {
|
||||
/// Creates a [DirectionalFocusIntent] with a fixed [key], and the given
|
||||
/// [direction].
|
||||
const DirectionalFocusIntent(this.direction) : super(DirectionalFocusAction.key);
|
||||
|
||||
/// The direction in which to look for the next focusable node when the
|
||||
/// associated [DirectionalFocusAction] is invoked.
|
||||
final TraversalDirection direction;
|
||||
}
|
||||
|
||||
/// An [Action] that moves the focus to the focusable node in the given
|
||||
/// [direction] configured by the associated [DirectionalFocusIntent].
|
||||
///
|
||||
/// This is the [Action] associated with the [key] and bound by default to the
|
||||
/// [LogicalKeyboardKey.arrowUp], [LogicalKeyboardKey.arrowDown],
|
||||
/// [LogicalKeyboardKey.arrowLeft], and [LogicalKeyboardKey.arrowRight] keys in
|
||||
/// the [WidgetsApp], with the appropriate associated directions.
|
||||
class DirectionalFocusAction extends _RequestFocusActionBase {
|
||||
/// Creates a [DirectionalFocusAction] with a fixed [key];
|
||||
DirectionalFocusAction() : super(key);
|
||||
|
||||
/// The [LocalKey] that uniquely identifies this action to [DirectionalFocusIntent].
|
||||
static const LocalKey key = ValueKey<Type>(DirectionalFocusAction);
|
||||
|
||||
/// The direction in which to look for the next focusable node when invoked.
|
||||
TraversalDirection direction;
|
||||
|
||||
@override
|
||||
void invoke(FocusNode node, DirectionalFocusIntent tag) {
|
||||
super.invoke(node, tag);
|
||||
final DirectionalFocusIntent args = tag;
|
||||
node.focusInDirection(args.direction);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../rendering/mock_canvas.dart';
|
||||
@ -128,21 +127,17 @@ void main() {
|
||||
..translate(x: 0.0, y: 0.0)
|
||||
..translate(x: tapDownOffset.dx, y: tapDownOffset.dy)
|
||||
..something((Symbol method, List<dynamic> arguments) {
|
||||
if (method != #drawCircle) {
|
||||
if (method != #drawCircle)
|
||||
return false;
|
||||
}
|
||||
final Offset center = arguments[0];
|
||||
final double radius = arguments[1];
|
||||
final Paint paint = arguments[2];
|
||||
if (offsetsAreClose(center, expectedCenter) &&
|
||||
radiiAreClose(radius, expectedRadius) &&
|
||||
paint.color.alpha == expectedAlpha) {
|
||||
if (offsetsAreClose(center, expectedCenter) && radiiAreClose(radius, expectedRadius) && paint.color.alpha == expectedAlpha)
|
||||
return true;
|
||||
}
|
||||
throw '''
|
||||
Expected: center == $expectedCenter, radius == $expectedRadius, alpha == $expectedAlpha
|
||||
Found: center == $center radius == $radius alpha == ${paint.color.alpha}''';
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@ -256,102 +251,6 @@ void main() {
|
||||
await gesture.up();
|
||||
}, skip: isBrowser);
|
||||
|
||||
testWidgets('The InkWell widget renders an ActivateAction-induced ink ripple', (WidgetTester tester) async {
|
||||
const Color highlightColor = Color(0xAAFF0000);
|
||||
const Color splashColor = Color(0xB40000FF);
|
||||
final BorderRadius borderRadius = BorderRadius.circular(6.0);
|
||||
|
||||
final FocusNode focusNode = FocusNode(debugLabel: 'Test Node');
|
||||
await tester.pumpWidget(
|
||||
Shortcuts(
|
||||
shortcuts: <LogicalKeySet, Intent>{
|
||||
LogicalKeySet(LogicalKeyboardKey.enter): const Intent(ActivateAction.key),
|
||||
},
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Material(
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 100.0,
|
||||
height: 100.0,
|
||||
child: InkWell(
|
||||
borderRadius: borderRadius,
|
||||
highlightColor: highlightColor,
|
||||
splashColor: splashColor,
|
||||
focusNode: focusNode,
|
||||
onTap: () { },
|
||||
radius: 100.0,
|
||||
splashFactory: InkRipple.splashFactory,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final Offset topLeft = tester.getTopLeft(find.byType(InkWell));
|
||||
final Offset inkWellCenter = tester.getCenter(find.byType(InkWell)) - topLeft;
|
||||
|
||||
// Now activate it with a keypress.
|
||||
focusNode.requestFocus();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.enter);
|
||||
await tester.pump();
|
||||
|
||||
final RenderBox box = Material.of(tester.element(find.byType(InkWell))) as dynamic;
|
||||
|
||||
bool offsetsAreClose(Offset a, Offset b) => (a - b).distance < 1.0;
|
||||
bool radiiAreClose(double a, double b) => (a - b).abs() < 1.0;
|
||||
|
||||
PaintPattern ripplePattern(double expectedRadius, int expectedAlpha) {
|
||||
return paints
|
||||
..translate(x: 0.0, y: 0.0)
|
||||
..translate(x: topLeft.dx, y: topLeft.dy)
|
||||
..something((Symbol method, List<dynamic> arguments) {
|
||||
if (method != #drawCircle) {
|
||||
return false;
|
||||
}
|
||||
final Offset center = arguments[0];
|
||||
final double radius = arguments[1];
|
||||
final Paint paint = arguments[2];
|
||||
if (offsetsAreClose(center, inkWellCenter) &&
|
||||
radiiAreClose(radius, expectedRadius) &&
|
||||
paint.color.alpha == expectedAlpha) {
|
||||
return true;
|
||||
}
|
||||
throw '''
|
||||
Expected: center == $inkWellCenter, radius == $expectedRadius, alpha == $expectedAlpha
|
||||
Found: center == $center radius == $radius alpha == ${paint.color.alpha}''';
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// ripplePattern always add a translation of topLeft.
|
||||
expect(box, ripplePattern(30.0, 0));
|
||||
|
||||
// The ripple fades in for 75ms. During that time its alpha is eased from
|
||||
// 0 to the splashColor's alpha value.
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
expect(box, ripplePattern(56.0, 120));
|
||||
|
||||
// At 75ms the ripple has faded in: it's alpha matches the splashColor's
|
||||
// alpha.
|
||||
await tester.pump(const Duration(milliseconds: 25));
|
||||
expect(box, ripplePattern(73.0, 180));
|
||||
|
||||
// At this point the splash radius has expanded to its limit: 5 past the
|
||||
// ink well's radius parameter. The fade-out is about to start.
|
||||
// The fade-out begins at 225ms = 50ms + 25ms + 150ms.
|
||||
await tester.pump(const Duration(milliseconds: 150));
|
||||
expect(box, ripplePattern(105.0, 180));
|
||||
|
||||
// After another 150ms the fade-out is complete.
|
||||
await tester.pump(const Duration(milliseconds: 150));
|
||||
expect(box, ripplePattern(105.0, 0));
|
||||
});
|
||||
|
||||
testWidgets('Cancel an InkRipple that was disposed when its animation ended', (WidgetTester tester) async {
|
||||
// Regression test for https://github.com/flutter/flutter/issues/14391
|
||||
await tester.pumpWidget(
|
||||
@ -432,4 +331,5 @@ void main() {
|
||||
throw 'Expected: paint.color.alpha == 0, found: ${paint.color.alpha}';
|
||||
}));
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -103,9 +103,9 @@ void main() {
|
||||
splashColor: const Color(0xffff0000),
|
||||
focusColor: const Color(0xff0000ff),
|
||||
highlightColor: const Color(0xf00fffff),
|
||||
onTap: () { },
|
||||
onLongPress: () { },
|
||||
onHover: (bool hover) { },
|
||||
onTap: () {},
|
||||
onLongPress: () {},
|
||||
onHover: (bool hover) {},
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -123,29 +123,29 @@ void main() {
|
||||
testWidgets('ink response changes color on focus', (WidgetTester tester) async {
|
||||
WidgetsBinding.instance.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
|
||||
final FocusNode focusNode = FocusNode(debugLabel: 'Ink Focus');
|
||||
await tester.pumpWidget(
|
||||
Material(
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Center(
|
||||
await tester.pumpWidget(Material(
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Center(
|
||||
child: Focus(
|
||||
focusNode: focusNode,
|
||||
child: Container(
|
||||
width: 100,
|
||||
height: 100,
|
||||
child: InkWell(
|
||||
focusNode: focusNode,
|
||||
hoverColor: const Color(0xff00ff00),
|
||||
splashColor: const Color(0xffff0000),
|
||||
focusColor: const Color(0xff0000ff),
|
||||
highlightColor: const Color(0xf00fffff),
|
||||
onTap: () { },
|
||||
onLongPress: () { },
|
||||
onHover: (bool hover) { },
|
||||
onTap: () {},
|
||||
onLongPress: () {},
|
||||
onHover: (bool hover) {},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
));
|
||||
await tester.pumpAndSettle();
|
||||
final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
|
||||
expect(inkFeatures, paintsExactlyCountTimes(#rect, 0));
|
||||
@ -172,9 +172,9 @@ void main() {
|
||||
splashColor: const Color(0xffff0000),
|
||||
focusColor: const Color(0xff0000ff),
|
||||
highlightColor: const Color(0xf00fffff),
|
||||
onTap: () { },
|
||||
onLongPress: () { },
|
||||
onHover: (bool hover) { },
|
||||
onTap: () {},
|
||||
onLongPress: () {},
|
||||
onHover: (bool hover) {},
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -206,8 +206,8 @@ void main() {
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Center(
|
||||
child: InkWell(
|
||||
onTap: () { },
|
||||
onLongPress: () { },
|
||||
onTap: () {},
|
||||
onLongPress: () {},
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -234,8 +234,8 @@ void main() {
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Center(
|
||||
child: InkWell(
|
||||
onTap: () { },
|
||||
onLongPress: () { },
|
||||
onTap: () {},
|
||||
onLongPress: () {},
|
||||
enableFeedback: false,
|
||||
),
|
||||
),
|
||||
@ -301,7 +301,7 @@ void main() {
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Material(
|
||||
child: InkWell(
|
||||
onTap: () { },
|
||||
onTap: () {},
|
||||
child: const Text('Button'),
|
||||
),
|
||||
),
|
||||
@ -312,7 +312,7 @@ void main() {
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Material(
|
||||
child: InkWell(
|
||||
onTap: () { },
|
||||
onTap: () {},
|
||||
child: const Text('Button'),
|
||||
excludeFromSemantics: true,
|
||||
),
|
||||
|
@ -5,77 +5,12 @@
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/src/services/keyboard_key.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../rendering/mock_canvas.dart';
|
||||
import '../widgets/semantics_tester.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('RawMaterialButton responds when tapped', (WidgetTester tester) async {
|
||||
bool pressed = false;
|
||||
const Color splashColor = Color(0xff00ff00);
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Center(
|
||||
child: RawMaterialButton(
|
||||
splashColor: splashColor,
|
||||
onPressed: () { pressed = true; },
|
||||
child: const Text('BUTTON'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('BUTTON'));
|
||||
await tester.pump(const Duration(milliseconds: 10));
|
||||
|
||||
final RenderBox splash = Material.of(tester.element(find.byType(InkWell))) as dynamic;
|
||||
expect(splash, paints..circle(color: splashColor));
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(pressed, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('RawMaterialButton responds to shortcut when activated', (WidgetTester tester) async {
|
||||
bool pressed = false;
|
||||
final FocusNode focusNode = FocusNode(debugLabel: 'Test Button');
|
||||
const Color splashColor = Color(0xff00ff00);
|
||||
await tester.pumpWidget(
|
||||
Shortcuts(
|
||||
shortcuts: <LogicalKeySet, Intent>{
|
||||
LogicalKeySet(LogicalKeyboardKey.enter): const Intent(ActivateAction.key),
|
||||
},
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Center(
|
||||
child: RawMaterialButton(
|
||||
splashColor: splashColor,
|
||||
focusNode: focusNode,
|
||||
onPressed: () { pressed = true; },
|
||||
child: const Text('BUTTON'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
focusNode.requestFocus();
|
||||
await tester.pump();
|
||||
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.enter);
|
||||
await tester.pump(const Duration(milliseconds: 10));
|
||||
|
||||
final RenderBox splash = Material.of(tester.element(find.byType(InkWell))) as dynamic;
|
||||
expect(splash, paints..circle(color: splashColor));
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(pressed, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('materialTapTargetSize.padded expands hit test area', (WidgetTester tester) async {
|
||||
int pressed = 0;
|
||||
|
||||
|
@ -324,11 +324,11 @@ void main() {
|
||||
).debugFillProperties(builder);
|
||||
|
||||
final List<String> description = builder.properties
|
||||
.where((DiagnosticsNode node) {
|
||||
return !node.isFiltered(DiagnosticLevel.info);
|
||||
})
|
||||
.map((DiagnosticsNode node) => node.toString())
|
||||
.toList();
|
||||
.where((DiagnosticsNode node) {
|
||||
return !node.isFiltered(DiagnosticLevel.info);
|
||||
})
|
||||
.map((DiagnosticsNode node) => node.toString())
|
||||
.toList();
|
||||
|
||||
expect(description[0], equalsIgnoringHashCodes('dispatcher: ActionDispatcher#00000'));
|
||||
expect(description[1], equals('actions: {[<\'bar\'>]: Closure: () => TestAction}'));
|
||||
|
@ -2,8 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/painting.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
@ -916,112 +914,5 @@ void main() {
|
||||
expect(focusCenter.hasFocus, isFalse);
|
||||
expect(focusTop.hasFocus, isTrue);
|
||||
});
|
||||
testWidgets('Focus traversal actions are invoked when shortcuts are used.', (WidgetTester tester) async {
|
||||
final GlobalKey upperLeftKey = GlobalKey(debugLabel: 'upperLeftKey');
|
||||
final GlobalKey upperRightKey = GlobalKey(debugLabel: 'upperRightKey');
|
||||
final GlobalKey lowerLeftKey = GlobalKey(debugLabel: 'lowerLeftKey');
|
||||
final GlobalKey lowerRightKey = GlobalKey(debugLabel: 'lowerRightKey');
|
||||
|
||||
await tester.pumpWidget(
|
||||
WidgetsApp(
|
||||
color: const Color(0xFFFFFFFF),
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return TestRoute(
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: FocusScope(
|
||||
debugLabel: 'scope',
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Focus(
|
||||
autofocus: true,
|
||||
debugLabel: 'upperLeft',
|
||||
child: Container(width: 100, height: 100, key: upperLeftKey),
|
||||
),
|
||||
Focus(
|
||||
debugLabel: 'upperRight',
|
||||
child: Container(width: 100, height: 100, key: upperRightKey),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Focus(
|
||||
debugLabel: 'lowerLeft',
|
||||
child: Container(width: 100, height: 100, key: lowerLeftKey),
|
||||
),
|
||||
Focus(
|
||||
debugLabel: 'lowerRight',
|
||||
child: Container(width: 100, height: 100, key: lowerRightKey),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Initial focus happens.
|
||||
expect(Focus.of(upperLeftKey.currentContext).hasPrimaryFocus, isTrue);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
|
||||
expect(Focus.of(upperRightKey.currentContext).hasPrimaryFocus, isTrue);
|
||||
// Initial focus happens.
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
|
||||
expect(Focus.of(lowerLeftKey.currentContext).hasPrimaryFocus, isTrue);
|
||||
// Initial focus happens.
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
|
||||
expect(Focus.of(lowerRightKey.currentContext).hasPrimaryFocus, isTrue);
|
||||
// Initial focus happens.
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
|
||||
expect(Focus.of(upperLeftKey.currentContext).hasPrimaryFocus, isTrue);
|
||||
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
expect(Focus.of(lowerRightKey.currentContext).hasPrimaryFocus, isTrue);
|
||||
// Initial focus happens.
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
expect(Focus.of(lowerLeftKey.currentContext).hasPrimaryFocus, isTrue);
|
||||
// Initial focus happens.
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
expect(Focus.of(upperRightKey.currentContext).hasPrimaryFocus, isTrue);
|
||||
// Initial focus happens.
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
expect(Focus.of(upperLeftKey.currentContext).hasPrimaryFocus, isTrue);
|
||||
|
||||
// Traverse in a direction
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
|
||||
expect(Focus.of(upperRightKey.currentContext).hasPrimaryFocus, isTrue);
|
||||
// Initial focus happens.
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
|
||||
expect(Focus.of(lowerRightKey.currentContext).hasPrimaryFocus, isTrue);
|
||||
// Initial focus happens.
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft);
|
||||
expect(Focus.of(lowerLeftKey.currentContext).hasPrimaryFocus, isTrue);
|
||||
// Initial focus happens.
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
|
||||
expect(Focus.of(upperLeftKey.currentContext).hasPrimaryFocus, isTrue);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class TestRoute extends PageRouteBuilder<void> {
|
||||
TestRoute({Widget child})
|
||||
: super(
|
||||
pageBuilder: (BuildContext _, Animation<double> __, Animation<double> ___) {
|
||||
return child;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user