diff --git a/packages/flutter/lib/src/material/checkbox.dart b/packages/flutter/lib/src/material/checkbox.dart index 2d4124aeba..7577366603 100644 --- a/packages/flutter/lib/src/material/checkbox.dart +++ b/packages/flutter/lib/src/material/checkbox.dart @@ -4,8 +4,6 @@ import 'dart:math' as math; -import 'package:flutter/foundation.dart'; -import 'package:flutter/gestures.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; @@ -54,7 +52,7 @@ class Checkbox extends StatefulWidget { /// * [onChanged], which is called when the value of the checkbox should /// change. It can be set to null to disable the checkbox. /// - /// The values of [tristate] and [autofocus] must not be null. + /// The value of [tristate] must not be null. const Checkbox({ Key key, @required this.value, @@ -62,14 +60,9 @@ class Checkbox extends StatefulWidget { @required this.onChanged, this.activeColor, this.checkColor, - this.focusColor, - this.hoverColor, this.materialTapTargetSize, - this.focusNode, - this.autofocus = false, }) : assert(tristate != null), assert(tristate || value != null), - assert(autofocus != null), super(key: key); /// Whether this checkbox is checked. @@ -120,10 +113,10 @@ class Checkbox extends StatefulWidget { /// /// Checkbox displays a dash when its value is null. /// - /// When a tri-state checkbox ([tristate] is true) is tapped, its [onChanged] - /// callback will be applied to true if the current value is false, to null if - /// value is true, and to false if value is null (i.e. it cycles through false - /// => true => null => false when tapped). + /// When a tri-state checkbox is tapped its [onChanged] callback will be + /// applied to true if the current value is null or false, false otherwise. + /// Typically tri-state checkboxes are disabled (the onChanged callback is + /// null) so they don't respond to taps. /// /// If tristate is false (the default), [value] must not be null. final bool tristate; @@ -137,18 +130,6 @@ class Checkbox extends StatefulWidget { /// * [MaterialTapTargetSize], for a description of how this affects tap targets. final MaterialTapTargetSize materialTapTargetSize; - /// The color for the checkbox's [Material] when it has the input focus. - final Color focusColor; - - /// The color for the checkbox's [Material] when a pointer is hovering over it. - final Color hoverColor; - - /// {@macro flutter.widgets.Focus.focusNode} - final FocusNode focusNode; - - /// {@macro flutter.widgets.Focus.autofocus} - final bool autofocus; - /// The width of a checkbox widget. static const double width = 18.0; @@ -157,68 +138,6 @@ class Checkbox extends StatefulWidget { } class _CheckboxState extends State with TickerProviderStateMixin { - bool get enabled => widget.onChanged != null; - Map _actionMap; - bool _showHighlight = false; - - @override - void initState() { - super.initState(); - _actionMap = { - SelectAction.key: _createAction, - if (!kIsWeb) ActivateAction.key: _createAction, - }; - _updateHighlightMode(WidgetsBinding.instance.focusManager.highlightMode); - WidgetsBinding.instance.focusManager.addHighlightModeListener(_handleFocusHighlightModeChange); - } - - void _actionHandler(FocusNode node, Intent intent){ - if (widget.onChanged != null) { - switch (widget.value) { - case false: - widget.onChanged(true); - break; - case true: - widget.onChanged(widget.tristate ? null : false); - break; - default: // case null: - widget.onChanged(false); - break; - } - } - final RenderObject renderObject = node.context.findRenderObject(); - renderObject.sendSemanticsEvent(const TapSemanticEvent()); - } - - Action _createAction() { - return CallbackAction( - SelectAction.key, - onInvoke: _actionHandler, - ); - } - - void _updateHighlightMode(FocusHighlightMode mode) { - switch (WidgetsBinding.instance.focusManager.highlightMode) { - case FocusHighlightMode.touch: - _showHighlight = false; - break; - case FocusHighlightMode.traditional: - _showHighlight = true; - break; - } - } - - void _handleFocusHighlightModeChange(FocusHighlightMode mode) { - if (!mounted) { - return; - } - setState(() { _updateHighlightMode(mode); }); - } - - bool hovering = false; - void _handleMouseEnter(PointerEnterEvent event) => setState(() { hovering = true; }); - void _handleMouseExit(PointerExitEvent event) => setState(() { hovering = false; }); - @override Widget build(BuildContext context) { assert(debugCheckHasMaterial(context)); @@ -233,36 +152,15 @@ class _CheckboxState extends State with TickerProviderStateMixin { break; } final BoxConstraints additionalConstraints = BoxConstraints.tight(size); - return MouseRegion( - onEnter: enabled ? _handleMouseEnter : null, - onExit: enabled ? _handleMouseExit : null, - child: Actions( - actions: _actionMap, - child: Focus( - focusNode: widget.focusNode, - autofocus: widget.autofocus, - canRequestFocus: enabled, - debugLabel: '${describeIdentity(widget)}(${widget.value})', - child: Builder( - builder: (BuildContext context) { - return _CheckboxRenderObjectWidget( - value: widget.value, - tristate: widget.tristate, - activeColor: widget.activeColor ?? themeData.toggleableActiveColor, - checkColor: widget.checkColor ?? const Color(0xFFFFFFFF), - inactiveColor: enabled ? themeData.unselectedWidgetColor : themeData.disabledColor, - focusColor: widget.focusColor ?? themeData.focusColor, - hoverColor: widget.hoverColor ?? themeData.hoverColor, - onChanged: widget.onChanged, - additionalConstraints: additionalConstraints, - vsync: this, - hasFocus: enabled && _showHighlight && Focus.of(context).hasFocus, - hovering: enabled && _showHighlight && hovering, - ); - }, - ), - ), - ), + return _CheckboxRenderObjectWidget( + value: widget.value, + tristate: widget.tristate, + activeColor: widget.activeColor ?? themeData.toggleableActiveColor, + checkColor: widget.checkColor ?? const Color(0xFFFFFFFF), + inactiveColor: widget.onChanged != null ? themeData.unselectedWidgetColor : themeData.disabledColor, + onChanged: widget.onChanged, + additionalConstraints: additionalConstraints, + vsync: this, ); } } @@ -275,13 +173,9 @@ class _CheckboxRenderObjectWidget extends LeafRenderObjectWidget { @required this.activeColor, @required this.checkColor, @required this.inactiveColor, - @required this.focusColor, - @required this.hoverColor, @required this.onChanged, @required this.vsync, @required this.additionalConstraints, - @required this.hasFocus, - @required this.hovering, }) : assert(tristate != null), assert(tristate || value != null), assert(activeColor != null), @@ -291,13 +185,9 @@ class _CheckboxRenderObjectWidget extends LeafRenderObjectWidget { final bool value; final bool tristate; - final bool hasFocus; - final bool hovering; final Color activeColor; final Color checkColor; final Color inactiveColor; - final Color focusColor; - final Color hoverColor; final ValueChanged onChanged; final TickerProvider vsync; final BoxConstraints additionalConstraints; @@ -309,13 +199,9 @@ class _CheckboxRenderObjectWidget extends LeafRenderObjectWidget { activeColor: activeColor, checkColor: checkColor, inactiveColor: inactiveColor, - focusColor: focusColor, - hoverColor: hoverColor, onChanged: onChanged, vsync: vsync, additionalConstraints: additionalConstraints, - hasFocus: hasFocus, - hovering: hovering, ); @override @@ -326,13 +212,9 @@ class _CheckboxRenderObjectWidget extends LeafRenderObjectWidget { ..activeColor = activeColor ..checkColor = checkColor ..inactiveColor = inactiveColor - ..focusColor = focusColor - ..hoverColor = hoverColor ..onChanged = onChanged ..additionalConstraints = additionalConstraints - ..vsync = vsync - ..hasFocus = hasFocus - ..hovering = hovering; + ..vsync = vsync; } } @@ -347,12 +229,8 @@ class _RenderCheckbox extends RenderToggleable { Color activeColor, this.checkColor, Color inactiveColor, - Color focusColor, - Color hoverColor, BoxConstraints additionalConstraints, ValueChanged onChanged, - bool hasFocus, - bool hovering, @required TickerProvider vsync, }) : _oldValue = value, super( @@ -360,13 +238,9 @@ class _RenderCheckbox extends RenderToggleable { tristate: tristate, activeColor: activeColor, inactiveColor: inactiveColor, - focusColor: focusColor, - hoverColor: hoverColor, onChanged: onChanged, additionalConstraints: additionalConstraints, vsync: vsync, - hasFocus: hasFocus, - hovering: hovering, ); bool _oldValue; diff --git a/packages/flutter/lib/src/material/ink_well.dart b/packages/flutter/lib/src/material/ink_well.dart index 3acf8be51d..99edbec94c 100644 --- a/packages/flutter/lib/src/material/ink_well.dart +++ b/packages/flutter/lib/src/material/ink_well.dart @@ -489,25 +489,20 @@ class _InkResponseState extends State with AutomaticKe bool get highlightsExist => _highlights.values.where((InkHighlight highlight) => highlight != null).isNotEmpty; - void _handleAction(FocusNode node, Intent intent) { - _startSplash(context: node.context); - _handleTap(node.context); - } - Action _createAction() { return CallbackAction( ActivateAction.key, - onInvoke: _handleAction, + onInvoke: (FocusNode node, Intent intent) { + _startSplash(context: node.context); + _handleTap(node.context); + }, ); } @override void initState() { super.initState(); - _actionMap = { - SelectAction.key: _createAction, - if (!kIsWeb) ActivateAction.key: _createAction, - }; + _actionMap = { ActivateAction.key: _createAction }; WidgetsBinding.instance.focusManager.addHighlightModeListener(_handleFocusHighlightModeChange); } diff --git a/packages/flutter/lib/src/material/radio.dart b/packages/flutter/lib/src/material/radio.dart index 457df4cfa3..81a378f48b 100644 --- a/packages/flutter/lib/src/material/radio.dart +++ b/packages/flutter/lib/src/material/radio.dart @@ -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/foundation.dart'; -import 'package:flutter/gestures.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; @@ -109,13 +107,8 @@ class Radio extends StatefulWidget { @required this.groupValue, @required this.onChanged, this.activeColor, - this.focusColor, - this.hoverColor, this.materialTapTargetSize, - this.focusNode, - this.autofocus = false, - }) : assert(autofocus != null), - super(key: key); + }) : super(key: key); /// The value represented by this radio button. final T value; @@ -168,77 +161,15 @@ class Radio extends StatefulWidget { /// * [MaterialTapTargetSize], for a description of how this affects tap targets. final MaterialTapTargetSize materialTapTargetSize; - /// The color for the radio's [Material] when it has the input focus. - final Color focusColor; - - /// The color for the radio's [Material] when a pointer is hovering over it. - final Color hoverColor; - - /// {@macro flutter.widgets.Focus.focusNode} - final FocusNode focusNode; - - /// {@macro flutter.widgets.Focus.autofocus} - final bool autofocus; - @override _RadioState createState() => _RadioState(); } class _RadioState extends State> with TickerProviderStateMixin { - bool get enabled => widget.onChanged != null; - Map _actionMap; - bool _showHighlight = false; - - @override - void initState() { - super.initState(); - _actionMap = { - SelectAction.key: _createAction, - if (!kIsWeb) ActivateAction.key: _createAction, - }; - _updateHighlightMode(WidgetsBinding.instance.focusManager.highlightMode); - WidgetsBinding.instance.focusManager.addHighlightModeListener(_handleFocusHighlightModeChange); - } - - void _actionHandler(FocusNode node, Intent intent){ - if (widget.onChanged != null) { - widget.onChanged(widget.value); - } - final RenderObject renderObject = node.context.findRenderObject(); - renderObject.sendSemanticsEvent(const TapSemanticEvent()); - } - - Action _createAction() { - return CallbackAction( - SelectAction.key, - onInvoke: _actionHandler, - ); - } - - void _updateHighlightMode(FocusHighlightMode mode) { - switch (WidgetsBinding.instance.focusManager.highlightMode) { - case FocusHighlightMode.touch: - _showHighlight = false; - break; - case FocusHighlightMode.traditional: - _showHighlight = true; - break; - } - } - - void _handleFocusHighlightModeChange(FocusHighlightMode mode) { - if (!mounted) { - return; - } - setState(() { _updateHighlightMode(mode); }); - } - - bool hovering = false; - void _handleMouseEnter(PointerEnterEvent event) => setState(() { hovering = true; }); - void _handleMouseExit(PointerExitEvent event) => setState(() { hovering = false; }); + bool get _enabled => widget.onChanged != null; Color _getInactiveColor(ThemeData themeData) { - return enabled ? themeData.unselectedWidgetColor : themeData.disabledColor; + return _enabled ? themeData.unselectedWidgetColor : themeData.disabledColor; } void _handleChanged(bool selected) { @@ -260,34 +191,13 @@ class _RadioState extends State> with TickerProviderStateMixin { break; } final BoxConstraints additionalConstraints = BoxConstraints.tight(size); - return MouseRegion( - onEnter: enabled ? _handleMouseEnter : null, - onExit: enabled ? _handleMouseExit : null, - child: Actions( - actions: _actionMap, - child: Focus( - focusNode: widget.focusNode, - autofocus: widget.autofocus, - canRequestFocus: enabled, - debugLabel: '${describeIdentity(widget)}(${widget.value})', - child: Builder( - builder: (BuildContext context) { - return _RadioRenderObjectWidget( - selected: widget.value == widget.groupValue, - activeColor: widget.activeColor ?? themeData.toggleableActiveColor, - inactiveColor: _getInactiveColor(themeData), - focusColor: widget.focusColor ?? themeData.focusColor, - hoverColor: widget.hoverColor ?? themeData.hoverColor, - onChanged: enabled ? _handleChanged : null, - additionalConstraints: additionalConstraints, - vsync: this, - hasFocus: enabled && _showHighlight && Focus.of(context).hasFocus, - hovering: enabled && _showHighlight && hovering, - ); - }, - ), - ), - ), + return _RadioRenderObjectWidget( + selected: widget.value == widget.groupValue, + activeColor: widget.activeColor ?? themeData.toggleableActiveColor, + inactiveColor: _getInactiveColor(themeData), + onChanged: _enabled ? _handleChanged : null, + additionalConstraints: additionalConstraints, + vsync: this, ); } } @@ -298,13 +208,9 @@ class _RadioRenderObjectWidget extends LeafRenderObjectWidget { @required this.selected, @required this.activeColor, @required this.inactiveColor, - @required this.focusColor, - @required this.hoverColor, @required this.additionalConstraints, this.onChanged, @required this.vsync, - @required this.hasFocus, - @required this.hovering, }) : assert(selected != null), assert(activeColor != null), assert(inactiveColor != null), @@ -312,12 +218,8 @@ class _RadioRenderObjectWidget extends LeafRenderObjectWidget { super(key: key); final bool selected; - final bool hasFocus; - final bool hovering; final Color inactiveColor; final Color activeColor; - final Color focusColor; - final Color hoverColor; final ValueChanged onChanged; final TickerProvider vsync; final BoxConstraints additionalConstraints; @@ -327,13 +229,9 @@ class _RadioRenderObjectWidget extends LeafRenderObjectWidget { value: selected, activeColor: activeColor, inactiveColor: inactiveColor, - focusColor: focusColor, - hoverColor: hoverColor, onChanged: onChanged, vsync: vsync, additionalConstraints: additionalConstraints, - hasFocus: hasFocus, - hovering: hovering, ); @override @@ -342,13 +240,9 @@ class _RadioRenderObjectWidget extends LeafRenderObjectWidget { ..value = selected ..activeColor = activeColor ..inactiveColor = inactiveColor - ..focusColor = focusColor - ..hoverColor = hoverColor ..onChanged = onChanged ..additionalConstraints = additionalConstraints - ..vsync = vsync - ..hasFocus = hasFocus - ..hovering = hovering; + ..vsync = vsync; } } @@ -357,25 +251,17 @@ class _RenderRadio extends RenderToggleable { bool value, Color activeColor, Color inactiveColor, - Color focusColor, - Color hoverColor, ValueChanged onChanged, BoxConstraints additionalConstraints, @required TickerProvider vsync, - bool hasFocus, - bool hovering, }) : super( value: value, tristate: false, activeColor: activeColor, inactiveColor: inactiveColor, - focusColor: focusColor, - hoverColor: hoverColor, onChanged: onChanged, additionalConstraints: additionalConstraints, vsync: vsync, - hasFocus: hasFocus, - hovering: hovering, ); @override diff --git a/packages/flutter/lib/src/material/switch.dart b/packages/flutter/lib/src/material/switch.dart index 64d31ec2ed..060ebe39c5 100644 --- a/packages/flutter/lib/src/material/switch.dart +++ b/packages/flutter/lib/src/material/switch.dart @@ -74,13 +74,9 @@ class Switch extends StatefulWidget { this.inactiveThumbImage, this.materialTapTargetSize, this.dragStartBehavior = DragStartBehavior.start, - this.focusColor, - this.hoverColor, - this.focusNode, - this.autofocus = false, - }) : _switchType = _SwitchType.material, - assert(dragStartBehavior != null), - super(key: key); + }) : _switchType = _SwitchType.material, + assert(dragStartBehavior != null), + super(key: key); /// Creates a [CupertinoSwitch] if the target platform is iOS, creates a /// material design switch otherwise. @@ -102,13 +98,8 @@ class Switch extends StatefulWidget { this.inactiveThumbImage, this.materialTapTargetSize, this.dragStartBehavior = DragStartBehavior.start, - this.focusColor, - this.hoverColor, - this.focusNode, - this.autofocus = false, - }) : assert(autofocus != null), - _switchType = _SwitchType.adaptive, - super(key: key); + }) : _switchType = _SwitchType.adaptive, + super(key: key); /// Whether this switch is on or off. /// @@ -189,18 +180,6 @@ class Switch extends StatefulWidget { /// {@macro flutter.cupertino.switch.dragStartBehavior} final DragStartBehavior dragStartBehavior; - /// The color for the button's [Material] when it has the input focus. - final Color focusColor; - - /// The color for the button's [Material] when a pointer is hovering over it. - final Color hoverColor; - - /// {@macro flutter.widgets.Focus.focusNode} - final FocusNode focusNode; - - /// {@macro flutter.widgets.Focus.autofocus} - final bool autofocus; - @override _SwitchState createState() => _SwitchState(); @@ -213,53 +192,6 @@ class Switch extends StatefulWidget { } class _SwitchState extends State with TickerProviderStateMixin { - Map _actionMap; - bool _showHighlight = false; - - @override - void initState() { - super.initState(); - _actionMap = { - SelectAction.key: _createAction, - if (!kIsWeb) ActivateAction.key: _createAction, - }; - _updateHighlightMode(WidgetsBinding.instance.focusManager.highlightMode); - WidgetsBinding.instance.focusManager.addHighlightModeListener(_handleFocusHighlightModeChange); - } - - void _actionHandler(FocusNode node, Intent intent){ - if (widget.onChanged != null) { - widget.onChanged(!widget.value); - } - final RenderObject renderObject = node.context.findRenderObject(); - renderObject.sendSemanticsEvent(const TapSemanticEvent()); - } - - Action _createAction() { - return CallbackAction( - SelectAction.key, - onInvoke: _actionHandler, - ); - } - - void _updateHighlightMode(FocusHighlightMode mode) { - switch (WidgetsBinding.instance.focusManager.highlightMode) { - case FocusHighlightMode.touch: - _showHighlight = false; - break; - case FocusHighlightMode.traditional: - _showHighlight = true; - break; - } - } - - void _handleFocusHighlightModeChange(FocusHighlightMode mode) { - if (!mounted) { - return; - } - setState(() { _updateHighlightMode(mode); }); - } - Size getSwitchSize(ThemeData theme) { switch (widget.materialTapTargetSize ?? theme.materialTapTargetSize) { case MaterialTapTargetSize.padded: @@ -273,12 +205,6 @@ class _SwitchState extends State with TickerProviderStateMixin { return null; } - bool get enabled => widget.onChanged != null; - - bool hovering = false; - void _handleMouseEnter(PointerEnterEvent event) => setState(() { hovering = true; }); - void _handleMouseExit(PointerExitEvent event) => setState(() { hovering = false; }); - Widget buildMaterialSwitch(BuildContext context) { assert(debugCheckHasMaterial(context)); final ThemeData theme = Theme.of(context); @@ -286,12 +212,10 @@ class _SwitchState extends State with TickerProviderStateMixin { final Color activeThumbColor = widget.activeColor ?? theme.toggleableActiveColor; final Color activeTrackColor = widget.activeTrackColor ?? activeThumbColor.withAlpha(0x80); - final Color hoverColor = widget.hoverColor ?? theme.hoverColor; - final Color focusColor = widget.focusColor ?? theme.focusColor; Color inactiveThumbColor; Color inactiveTrackColor; - if (enabled) { + if (widget.onChanged != null) { const Color black32 = Color(0x52000000); // Black with 32% opacity inactiveThumbColor = widget.inactiveThumbColor ?? (isDark ? Colors.grey.shade400 : Colors.grey.shade50); inactiveTrackColor = widget.inactiveTrackColor ?? (isDark ? Colors.white30 : black32); @@ -300,59 +224,33 @@ class _SwitchState extends State with TickerProviderStateMixin { inactiveTrackColor = widget.inactiveTrackColor ?? (isDark ? Colors.white10 : Colors.black12); } - return MouseRegion( - onEnter: enabled ? _handleMouseEnter : null, - onExit: enabled ? _handleMouseExit : null, - child: Actions( - actions: _actionMap, - child: Focus( - focusNode: widget.focusNode, - autofocus: widget.autofocus, - canRequestFocus: enabled, - debugLabel: '${describeIdentity(widget)}({$widget.value})', - child: Builder( - builder: (BuildContext context) { - final bool hasFocus = Focus.of(context).hasFocus; - return _SwitchRenderObjectWidget( - dragStartBehavior: widget.dragStartBehavior, - value: widget.value, - activeColor: activeThumbColor, - inactiveColor: inactiveThumbColor, - hoverColor: hoverColor, - focusColor: focusColor, - activeThumbImage: widget.activeThumbImage, - inactiveThumbImage: widget.inactiveThumbImage, - activeTrackColor: activeTrackColor, - inactiveTrackColor: inactiveTrackColor, - configuration: createLocalImageConfiguration(context), - onChanged: widget.onChanged, - additionalConstraints: BoxConstraints.tight(getSwitchSize(theme)), - hasFocus: enabled && _showHighlight && hasFocus, - hovering: enabled && _showHighlight && hovering, - vsync: this, - ); - }, - ), - ), - ), + return _SwitchRenderObjectWidget( + dragStartBehavior: widget.dragStartBehavior, + value: widget.value, + activeColor: activeThumbColor, + inactiveColor: inactiveThumbColor, + activeThumbImage: widget.activeThumbImage, + inactiveThumbImage: widget.inactiveThumbImage, + activeTrackColor: activeTrackColor, + inactiveTrackColor: inactiveTrackColor, + configuration: createLocalImageConfiguration(context), + onChanged: widget.onChanged, + additionalConstraints: BoxConstraints.tight(getSwitchSize(theme)), + vsync: this, ); } Widget buildCupertinoSwitch(BuildContext context) { final Size size = getSwitchSize(Theme.of(context)); - return Focus( - focusNode: widget.focusNode, - autofocus: widget.autofocus, - child: Container( - width: size.width, // Same size as the Material switch. - height: size.height, - alignment: Alignment.center, - child: CupertinoSwitch( - dragStartBehavior: widget.dragStartBehavior, - value: widget.value, - onChanged: widget.onChanged, - activeColor: widget.activeColor, - ), + return Container( + width: size.width, // Same size as the Material switch. + height: size.height, + alignment: Alignment.center, + child: CupertinoSwitch( + dragStartBehavior: widget.dragStartBehavior, + value: widget.value, + onChanged: widget.onChanged, + activeColor: widget.activeColor, ), ); } @@ -386,8 +284,6 @@ class _SwitchRenderObjectWidget extends LeafRenderObjectWidget { this.value, this.activeColor, this.inactiveColor, - this.hoverColor, - this.focusColor, this.activeThumbImage, this.inactiveThumbImage, this.activeTrackColor, @@ -397,15 +293,11 @@ class _SwitchRenderObjectWidget extends LeafRenderObjectWidget { this.vsync, this.additionalConstraints, this.dragStartBehavior, - this.hasFocus, - this.hovering, }) : super(key: key); final bool value; final Color activeColor; final Color inactiveColor; - final Color hoverColor; - final Color focusColor; final ImageProvider activeThumbImage; final ImageProvider inactiveThumbImage; final Color activeTrackColor; @@ -415,8 +307,6 @@ class _SwitchRenderObjectWidget extends LeafRenderObjectWidget { final TickerProvider vsync; final BoxConstraints additionalConstraints; final DragStartBehavior dragStartBehavior; - final bool hasFocus; - final bool hovering; @override _RenderSwitch createRenderObject(BuildContext context) { @@ -425,8 +315,6 @@ class _SwitchRenderObjectWidget extends LeafRenderObjectWidget { value: value, activeColor: activeColor, inactiveColor: inactiveColor, - hoverColor: hoverColor, - focusColor: focusColor, activeThumbImage: activeThumbImage, inactiveThumbImage: inactiveThumbImage, activeTrackColor: activeTrackColor, @@ -435,8 +323,6 @@ class _SwitchRenderObjectWidget extends LeafRenderObjectWidget { onChanged: onChanged, textDirection: Directionality.of(context), additionalConstraints: additionalConstraints, - hasFocus: hasFocus, - hovering: hovering, vsync: vsync, ); } @@ -447,8 +333,6 @@ class _SwitchRenderObjectWidget extends LeafRenderObjectWidget { ..value = value ..activeColor = activeColor ..inactiveColor = inactiveColor - ..hoverColor = hoverColor - ..focusColor = focusColor ..activeThumbImage = activeThumbImage ..inactiveThumbImage = inactiveThumbImage ..activeTrackColor = activeTrackColor @@ -458,8 +342,6 @@ class _SwitchRenderObjectWidget extends LeafRenderObjectWidget { ..textDirection = Directionality.of(context) ..additionalConstraints = additionalConstraints ..dragStartBehavior = dragStartBehavior - ..hasFocus = hasFocus - ..hovering = hovering ..vsync = vsync; } } @@ -469,8 +351,6 @@ class _RenderSwitch extends RenderToggleable { bool value, Color activeColor, Color inactiveColor, - Color hoverColor, - Color focusColor, ImageProvider activeThumbImage, ImageProvider inactiveThumbImage, Color activeTrackColor, @@ -479,10 +359,8 @@ class _RenderSwitch extends RenderToggleable { BoxConstraints additionalConstraints, @required TextDirection textDirection, ValueChanged onChanged, - DragStartBehavior dragStartBehavior, - bool hasFocus, - bool hovering, @required TickerProvider vsync, + DragStartBehavior dragStartBehavior, }) : assert(textDirection != null), _activeThumbImage = activeThumbImage, _inactiveThumbImage = inactiveThumbImage, @@ -495,12 +373,8 @@ class _RenderSwitch extends RenderToggleable { tristate: false, activeColor: activeColor, inactiveColor: inactiveColor, - hoverColor: hoverColor, - focusColor: focusColor, onChanged: onChanged, additionalConstraints: additionalConstraints, - hasFocus: hasFocus, - hovering: hovering, vsync: vsync, ) { _drag = HorizontalDragGestureRecognizer() diff --git a/packages/flutter/lib/src/material/toggleable.dart b/packages/flutter/lib/src/material/toggleable.dart index 22b497bed4..571b5fb68b 100644 --- a/packages/flutter/lib/src/material/toggleable.dart +++ b/packages/flutter/lib/src/material/toggleable.dart @@ -10,15 +10,9 @@ import 'package:flutter/scheduler.dart'; import 'constants.dart'; -// Duration of the animation that moves the toggle from one state to another. const Duration _kToggleDuration = Duration(milliseconds: 200); - -// Radius of the radial reaction over time. final Animatable _kRadialReactionRadiusTween = Tween(begin: 0.0, end: kRadialReactionRadius); -// Duration of the fade animation for the reaction when focus and hover occur. -const Duration _kReactionFadeDuration = Duration(milliseconds: 50); - /// A base class for material style toggleable controls with toggle animations. /// /// This class handles storing the current value, dispatching ValueChanged on a @@ -34,13 +28,9 @@ abstract class RenderToggleable extends RenderConstrainedBox { bool tristate = false, @required Color activeColor, @required Color inactiveColor, - Color hoverColor, - Color focusColor, ValueChanged onChanged, BoxConstraints additionalConstraints, @required TickerProvider vsync, - bool hasFocus = false, - bool hovering = false, }) : assert(tristate != null), assert(tristate || value != null), assert(activeColor != null), @@ -50,11 +40,7 @@ abstract class RenderToggleable extends RenderConstrainedBox { _tristate = tristate, _activeColor = activeColor, _inactiveColor = inactiveColor, - _hoverColor = hoverColor ?? activeColor.withAlpha(kRadialReactionAlpha), - _focusColor = focusColor ?? activeColor.withAlpha(kRadialReactionAlpha), _onChanged = onChanged, - _hasFocus = hasFocus, - _hovering = hovering, _vsync = vsync, super(additionalConstraints: additionalConstraints) { _tap = TapGestureRecognizer() @@ -80,24 +66,6 @@ abstract class RenderToggleable extends RenderConstrainedBox { parent: _reactionController, curve: Curves.fastOutSlowIn, )..addListener(markNeedsPaint); - _reactionHoverFadeController = AnimationController( - duration: _kReactionFadeDuration, - value: hovering || hasFocus ? 1.0 : 0.0, - vsync: vsync, - ); - _reactionHoverFade = CurvedAnimation( - parent: _reactionHoverFadeController, - curve: Curves.fastOutSlowIn, - )..addListener(markNeedsPaint); - _reactionFocusFadeController = AnimationController( - duration: _kReactionFadeDuration, - value: hovering || hasFocus ? 1.0 : 0.0, - vsync: vsync, - ); - _reactionFocusFade = CurvedAnimation( - parent: _reactionFocusFadeController, - curve: Curves.fastOutSlowIn, - )..addListener(markNeedsPaint); } /// Used by subclasses to manipulate the visual value of the control. @@ -134,66 +102,6 @@ abstract class RenderToggleable extends RenderConstrainedBox { AnimationController _reactionController; Animation _reaction; - /// Used by subclasses to control the radial reaction's opacity animation for - /// [hasFocus] changes. - /// - /// Some controls have a radial ink reaction to focus. This animation - /// controller can be used to start or stop these ink reaction fade-ins and - /// fade-outs. - /// - /// Subclasses should call [paintRadialReaction] to actually paint the radial - /// reaction. - @protected - AnimationController get reactionFocusFadeController => _reactionFocusFadeController; - AnimationController _reactionFocusFadeController; - Animation _reactionFocusFade; - - /// Used by subclasses to control the radial reaction's opacity animation for - /// [hovering] changes. - /// - /// Some controls have a radial ink reaction to pointer hover. This animation - /// controller can be used to start or stop these ink reaction fade-ins and - /// fade-outs. - /// - /// Subclasses should call [paintRadialReaction] to actually paint the radial - /// reaction. - @protected - AnimationController get reactionHoverFadeController => _reactionHoverFadeController; - AnimationController _reactionHoverFadeController; - Animation _reactionHoverFade; - - /// True if this toggleable has the input focus. - bool get hasFocus => _hasFocus; - bool _hasFocus; - set hasFocus(bool value) { - assert(value != null); - if (value == _hasFocus) - return; - _hasFocus = value; - if (_hasFocus) { - _reactionFocusFadeController.forward(); - } else { - _reactionFocusFadeController.reverse(); - } - markNeedsPaint(); - } - - /// True if this toggleable is being hovered over by a pointer. - bool get hovering => _hovering; - bool _hovering; - set hovering(bool value) { - assert(value != null); - if (value == _hovering) - return; - _hovering = value; - if (_hovering) { - _reactionHoverFadeController.forward(); - } else { - _reactionHoverFadeController.reverse(); - } - markNeedsPaint(); - } - /// The [TickerProvider] for the [AnimationController]s that run the animations. TickerProvider get vsync => _vsync; TickerProvider _vsync; @@ -284,54 +192,6 @@ abstract class RenderToggleable extends RenderConstrainedBox { markNeedsPaint(); } - /// The color that should be used for the reaction when [hovering] is true. - /// - /// Used when the toggleable needs to change the reaction color/transparency, - /// when it is being hovered over. - /// - /// Defaults to the [activeColor] at alpha [kRadialReactionAlpha]. - Color get hoverColor => _hoverColor; - Color _hoverColor; - set hoverColor(Color value) { - assert(value != null); - if (value == _hoverColor) - return; - _hoverColor = value; - markNeedsPaint(); - } - - /// The color that should be used for the reaction when [hasFocus] is true. - /// - /// Used when the toggleable needs to change the reaction color/transparency, - /// when it has focus. - /// - /// Defaults to the [activeColor] at alpha [kRadialReactionAlpha]. - Color get focusColor => _focusColor; - Color _focusColor; - set focusColor(Color value) { - assert(value != null); - if (value == _focusColor) - return; - _focusColor = value; - markNeedsPaint(); - } - - /// The color that should be used for the reaction when drawn. - /// - /// Used when the toggleable needs to change the reaction color/transparency - /// that is displayed when the toggleable is toggled by a tap. - /// - /// Defaults to the [activeColor] at alpha [kRadialReactionAlpha]. - Color get reactionColor => _reactionColor; - Color _reactionColor; - set reactionColor(Color value) { - assert(value != null); - if (value == _reactionColor) - return; - _reactionColor = value; - markNeedsPaint(); - } - /// Called when the control changes value. /// /// If the control is tapped, [onChanged] is called immediately with the new @@ -463,18 +323,12 @@ abstract class RenderToggleable extends RenderConstrainedBox { /// point at which the user interacted with the control, which is handled /// automatically). void paintRadialReaction(Canvas canvas, Offset offset, Offset origin) { - if (!_reaction.isDismissed || !_reactionFocusFade.isDismissed || !_reactionHoverFade.isDismissed) { - final Paint reactionPaint = Paint() - ..color = Color.lerp( - Color.lerp(activeColor.withAlpha(kRadialReactionAlpha), hoverColor, _reactionHoverFade.value), - focusColor, - _reactionFocusFade.value, - ); + if (!_reaction.isDismissed) { + // TODO(abarth): We should have a different reaction color when position is zero. + final Paint reactionPaint = Paint()..color = activeColor.withAlpha(kRadialReactionAlpha); final Offset center = Offset.lerp(_downPosition ?? origin, origin, _reaction.value); - final double reactionRadius = hasFocus || hovering - ? kRadialReactionRadius - : _kRadialReactionRadiusTween.evaluate(_reaction); - canvas.drawCircle(center + offset, reactionRadius, reactionPaint); + final double radius = _kRadialReactionRadiusTween.evaluate(_reaction); + canvas.drawCircle(center + offset, radius, reactionPaint); } } diff --git a/packages/flutter/lib/src/widgets/actions.dart b/packages/flutter/lib/src/widgets/actions.dart index 9d1fed90f0..1509f9d14d 100644 --- a/packages/flutter/lib/src/widgets/actions.dart +++ b/packages/flutter/lib/src/widgets/actions.dart @@ -376,8 +376,8 @@ class DoNothingAction extends Action { /// 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. By default, is bound to [LogicalKeyboardKey.enter] in -/// the default keyboard map in [WidgetsApp]. +/// 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); @@ -385,16 +385,3 @@ abstract class ActivateAction extends Action { /// The [LocalKey] that uniquely identifies this action. static const LocalKey key = ValueKey(ActivateAction); } - -/// An action that selects the currently focused control. -/// -/// This is an abstract class that serves as a base class for actions that -/// select something, like a checkbox or a radio button. By default, it is bound -/// to [LogicalKeyboardKey.space] in the default keyboard map in [WidgetsApp]. -abstract class SelectAction extends Action { - /// Creates a [SelectAction] with a fixed [key]; - const SelectAction() : super(key); - - /// The [LocalKey] that uniquely identifies this action. - static const LocalKey key = ValueKey(SelectAction); -} diff --git a/packages/flutter/lib/src/widgets/app.dart b/packages/flutter/lib/src/widgets/app.dart index 7af61b1c17..63f76beced 100644 --- a/packages/flutter/lib/src/widgets/app.dart +++ b/packages/flutter/lib/src/widgets/app.dart @@ -1046,7 +1046,6 @@ class _WidgetsAppState extends State with WidgetsBindingObserver { LogicalKeySet(LogicalKeyboardKey.arrowDown): const DirectionalFocusIntent(TraversalDirection.down), LogicalKeySet(LogicalKeyboardKey.arrowUp): const DirectionalFocusIntent(TraversalDirection.up), LogicalKeySet(LogicalKeyboardKey.enter): const Intent(ActivateAction.key), - LogicalKeySet(LogicalKeyboardKey.space): const Intent(SelectAction.key), }; final Map _actionMap = { diff --git a/packages/flutter/lib/src/widgets/focus_manager.dart b/packages/flutter/lib/src/widgets/focus_manager.dart index 00481ac97c..97a098d0db 100644 --- a/packages/flutter/lib/src/widgets/focus_manager.dart +++ b/packages/flutter/lib/src/widgets/focus_manager.dart @@ -897,8 +897,8 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier { super.debugFillProperties(properties); properties.add(DiagnosticsProperty('context', context, defaultValue: null)); properties.add(FlagProperty('canRequestFocus', value: canRequestFocus, ifFalse: 'NOT FOCUSABLE', defaultValue: true)); - properties.add(FlagProperty('hasFocus', value: hasFocus && !hasPrimaryFocus, ifTrue: 'IN FOCUS PATH', defaultValue: false)); - properties.add(FlagProperty('hasPrimaryFocus', value: hasPrimaryFocus, ifTrue: 'PRIMARY FOCUS', defaultValue: false)); + properties.add(FlagProperty('hasFocus', value: hasFocus, ifTrue: 'FOCUSED', defaultValue: false)); + properties.add(StringProperty('debugLabel', debugLabel, defaultValue: null)); } @override diff --git a/packages/flutter/test/material/checkbox_test.dart b/packages/flutter/test/material/checkbox_test.dart index 6850a6dac0..e4720c1188 100644 --- a/packages/flutter/test/material/checkbox_test.dart +++ b/packages/flutter/test/material/checkbox_test.dart @@ -67,12 +67,11 @@ void main() { ), )); - expect(tester.getSemantics(find.byType(Focus)), matchesSemantics( + expect(tester.getSemantics(find.byType(Checkbox)), matchesSemantics( hasCheckedState: true, hasEnabledState: true, isEnabled: true, hasTapAction: true, - isFocusable: true, )); await tester.pumpWidget(Material( @@ -82,13 +81,12 @@ void main() { ), )); - expect(tester.getSemantics(find.byType(Focus)), matchesSemantics( + expect(tester.getSemantics(find.byType(Checkbox)), matchesSemantics( hasCheckedState: true, hasEnabledState: true, isChecked: true, isEnabled: true, hasTapAction: true, - isFocusable: true, )); await tester.pumpWidget(const Material( @@ -98,10 +96,9 @@ void main() { ), )); - expect(tester.getSemantics(find.byType(Focus)), matchesSemantics( + expect(tester.getSemantics(find.byType(Checkbox)), matchesSemantics( hasCheckedState: true, hasEnabledState: true, - isFocusable: true, )); await tester.pumpWidget(const Material( @@ -111,7 +108,7 @@ void main() { ), )); - expect(tester.getSemantics(find.byType(Focus)), matchesSemantics( + expect(tester.getSemantics(find.byType(Checkbox)), matchesSemantics( hasCheckedState: true, hasEnabledState: true, isChecked: true, @@ -133,14 +130,13 @@ void main() { ), )); - expect(tester.getSemantics(find.byType(Focus)), matchesSemantics( + expect(tester.getSemantics(find.byType(Checkbox)), matchesSemantics( label: 'foo', textDirection: TextDirection.ltr, hasCheckedState: true, hasEnabledState: true, isEnabled: true, hasTapAction: true, - isFocusable: true, )); handle.dispose(); }); @@ -206,7 +202,6 @@ void main() { SemanticsFlag.hasCheckedState, SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled, - SemanticsFlag.isFocusable, ], actions: [SemanticsAction.tap], ), hasLength(1)); @@ -227,7 +222,6 @@ void main() { SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled, SemanticsFlag.isChecked, - SemanticsFlag.isFocusable, ], actions: [SemanticsAction.tap], ), hasLength(1)); @@ -247,7 +241,6 @@ void main() { SemanticsFlag.hasCheckedState, SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled, - SemanticsFlag.isFocusable, ], actions: [SemanticsAction.tap], ), hasLength(1)); @@ -281,7 +274,7 @@ void main() { ); await tester.tap(find.byType(Checkbox)); - final RenderObject object = tester.firstRenderObject(find.byType(Focus)); + final RenderObject object = tester.firstRenderObject(find.byType(Checkbox)); expect(checkboxValue, true); expect(semanticEvent, { @@ -311,9 +304,7 @@ void main() { } RenderToggleable getCheckboxRenderer() { - return tester.renderObject(find.byWidgetPredicate((Widget widget) { - return widget.runtimeType.toString() == '_CheckboxRenderObjectWidget'; - })); + return tester.renderObject(find.byType(Checkbox)); } await tester.pumpWidget(buildFrame(false)); @@ -365,9 +356,7 @@ void main() { } RenderToggleable getCheckboxRenderer() { - return tester.renderObject(find.byWidgetPredicate((Widget widget) { - return widget.runtimeType.toString() == '_CheckboxRenderObjectWidget'; - })); + return tester.renderObject(find.byType(Checkbox)); } await tester.pumpWidget(buildFrame(checkColor: const Color(0xFFFFFFFF))); @@ -387,181 +376,4 @@ void main() { expect(getCheckboxRenderer(), paints..rrect(color: const Color(0xFF000000))); // paints's color is 0xFF000000 (params) }); - testWidgets('Checkbox is focusable and has correct focus color', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(debugLabel: 'Checkbox'); - tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; - bool value = true; - Widget buildApp({bool enabled = true}) { - return MaterialApp( - home: Material( - child: Center( - child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) { - return Checkbox( - value: value, - onChanged: enabled ? (bool newValue) { - setState(() { - value = newValue; - }); - } : null, - focusColor: Colors.orange[500], - autofocus: true, - focusNode: focusNode, - ); - }), - ), - ), - ); - } - await tester.pumpWidget(buildApp()); - - await tester.pumpAndSettle(); - expect(focusNode.hasPrimaryFocus, isTrue); - expect( - Material.of(tester.element(find.byType(Checkbox))), - paints - ..circle(color: Colors.orange[500]) - ..rrect( - color: const Color(0xff1e88e5), - rrect: RRect.fromLTRBR( - 391.0, 291.0, 409.0, 309.0, const Radius.circular(1.0))) - ..path(color: Colors.white), - ); - - // Check the false value. - value = false; - await tester.pumpWidget(buildApp()); - await tester.pumpAndSettle(); - expect(focusNode.hasPrimaryFocus, isTrue); - expect( - Material.of(tester.element(find.byType(Checkbox))), - paints - ..circle(color: Colors.orange[500]) - ..drrect( - color: const Color(0x8a000000), - outer: RRect.fromLTRBR( - 391.0, 291.0, 409.0, 309.0, const Radius.circular(1.0)), - inner: RRect.fromLTRBR(393.0, - 293.0, 407.0, 307.0, const Radius.circular(-1.0))), - ); - - // Check what happens when disabled. - value = false; - await tester.pumpWidget(buildApp(enabled: false)); - await tester.pumpAndSettle(); - expect(focusNode.hasPrimaryFocus, isFalse); - expect( - Material.of(tester.element(find.byType(Checkbox))), - paints - ..drrect( - color: const Color(0x61000000), - outer: RRect.fromLTRBR( - 391.0, 291.0, 409.0, 309.0, const Radius.circular(1.0)), - inner: RRect.fromLTRBR(393.0, - 293.0, 407.0, 307.0, const Radius.circular(-1.0))), - ); - }); - - testWidgets('Checkbox can be hovered and has correct hover color', (WidgetTester tester) async { - tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; - bool value = true; - Widget buildApp({bool enabled = true}) { - return MaterialApp( - home: Material( - child: Center( - child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) { - return Checkbox( - value: value, - onChanged: enabled ? (bool newValue) { - setState(() { - value = newValue; - }); - } : null, - hoverColor: Colors.orange[500], - ); - }), - ), - ), - ); - } - await tester.pumpWidget(buildApp()); - await tester.pumpAndSettle(); - expect( - Material.of(tester.element(find.byType(Checkbox))), - paints - ..rrect( - color: const Color(0xff1e88e5), - rrect: RRect.fromLTRBR( - 391.0, 291.0, 409.0, 309.0, const Radius.circular(1.0))) - ..path(color: const Color(0xffffffff), style: PaintingStyle.stroke, strokeWidth: 2.0), - ); - - // Start hovering - final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); - addTearDown(gesture.removePointer); - await gesture.moveTo(tester.getCenter(find.byType(Checkbox))); - - await tester.pumpWidget(buildApp()); - await tester.pumpAndSettle(); - expect( - Material.of(tester.element(find.byType(Checkbox))), - paints - ..rrect( - color: const Color(0xff1e88e5), - rrect: RRect.fromLTRBR( - 391.0, 291.0, 409.0, 309.0, const Radius.circular(1.0))) - ..path(color: const Color(0xffffffff), style: PaintingStyle.stroke, strokeWidth: 2.0), - ); - - // Check what happens when disabled. - await tester.pumpWidget(buildApp(enabled: false)); - await tester.pumpAndSettle(); - expect( - Material.of(tester.element(find.byType(Checkbox))), - paints - ..rrect( - color: const Color(0x61000000), - rrect: RRect.fromLTRBR( - 391.0, 291.0, 409.0, 309.0, const Radius.circular(1.0))) - ..path(color: const Color(0xffffffff), style: PaintingStyle.stroke, strokeWidth: 2.0), - ); - }); - - testWidgets('Checkbox can be toggled by keyboard shortcuts', (WidgetTester tester) async { - tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; - bool value = true; - Widget buildApp({bool enabled = true}) { - return MaterialApp( - home: Material( - child: Center( - child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) { - return Checkbox( - value: value, - onChanged: enabled ? (bool newValue) { - setState(() { - value = newValue; - }); - } : null, - focusColor: Colors.orange[500], - autofocus: true, - ); - }), - ), - ), - ); - } - await tester.pumpWidget(buildApp()); - await tester.pumpAndSettle(); - await tester.sendKeyEvent(LogicalKeyboardKey.enter); - await tester.pumpAndSettle(); - expect(value, isFalse); - await tester.sendKeyEvent(LogicalKeyboardKey.enter); - await tester.pumpAndSettle(); - expect(value, isTrue); - await tester.sendKeyEvent(LogicalKeyboardKey.space); - await tester.pumpAndSettle(); - expect(value, isFalse); - await tester.sendKeyEvent(LogicalKeyboardKey.space); - await tester.pumpAndSettle(); - expect(value, isTrue); - }); } diff --git a/packages/flutter/test/material/radio_test.dart b/packages/flutter/test/material/radio_test.dart index 65acf51dfc..bf817a5090 100644 --- a/packages/flutter/test/material/radio_test.dart +++ b/packages/flutter/test/material/radio_test.dart @@ -9,7 +9,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/material.dart'; -import '../rendering/mock_canvas.dart'; import '../widgets/semantics_tester.dart'; void main() { @@ -132,7 +131,6 @@ void main() { SemanticsFlag.hasCheckedState, SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled, - SemanticsFlag.isFocusable, ], actions: [ SemanticsAction.tap, @@ -159,7 +157,6 @@ void main() { SemanticsFlag.isChecked, SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled, - SemanticsFlag.isFocusable, ], actions: [ SemanticsAction.tap, @@ -184,7 +181,6 @@ void main() { SemanticsFlag.isInMutuallyExclusiveGroup, SemanticsFlag.hasCheckedState, SemanticsFlag.hasEnabledState, - SemanticsFlag.isFocusable, ], ), ], @@ -236,7 +232,7 @@ void main() { )); await tester.tap(find.byKey(key)); - final RenderObject object = tester.firstRenderObject(find.byType(Focus)); + final RenderObject object = tester.firstRenderObject(find.byKey(key)); expect(radioValue, 1); expect(semanticEvent, { @@ -286,227 +282,5 @@ void main() { ), ); }, skip: isBrowser); - - testWidgets('Radio is focusable and has correct focus color', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(debugLabel: 'Radio'); - tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; - int groupValue = 0; - const Key radioKey = Key('radio'); - Widget buildApp({bool enabled = true}) { - return MaterialApp( - home: Material( - child: Center( - child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) { - return Container( - width: 100, - height: 100, - color: Colors.white, - child: Radio( - key: radioKey, - value: 0, - onChanged: enabled ? (int newValue) { - setState(() { - groupValue = newValue; - }); - } : null, - focusColor: Colors.orange[500], - autofocus: true, - focusNode: focusNode, - groupValue: groupValue, - ), - ); - }), - ), - ), - ); - } - await tester.pumpWidget(buildApp()); - - await tester.pumpAndSettle(); - expect(focusNode.hasPrimaryFocus, isTrue); - expect( - Material.of(tester.element(find.byKey(radioKey))), - paints - ..rect( - color: const Color(0xffffffff), - rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0)) - ..circle(color: Colors.orange[500]) - ..circle(color: const Color(0xff1e88e5)) - ..circle(color: const Color(0xff1e88e5)), - ); - - // Check when the radio isn't selected. - groupValue = 1; - await tester.pumpWidget(buildApp()); - await tester.pumpAndSettle(); - expect(focusNode.hasPrimaryFocus, isTrue); - expect( - Material.of(tester.element(find.byKey(radioKey))), - paints - ..rect( - color: const Color(0xffffffff), - rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0)) - ..circle(color: Colors.orange[500]) - ..circle(color: const Color(0x8a000000), style: PaintingStyle.stroke, strokeWidth: 2.0) - ); - - // Check when the radio is selected, but disabled. - groupValue = 0; - await tester.pumpWidget(buildApp(enabled: false)); - await tester.pumpAndSettle(); - expect(focusNode.hasPrimaryFocus, isFalse); - expect( - Material.of(tester.element(find.byKey(radioKey))), - paints - ..rect( - color: const Color(0xffffffff), - rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0)) - ..circle(color: const Color(0x61000000)) - ..circle(color: const Color(0x61000000)), - ); - }); - - testWidgets('Radio can be hovered and has correct focus color', (WidgetTester tester) async { - tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; - int groupValue = 0; - const Key radioKey = Key('radio'); - Widget buildApp({bool enabled = true}) { - return MaterialApp( - home: Material( - child: Center( - child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) { - return Container( - width: 100, - height: 100, - color: Colors.white, - child: Radio( - key: radioKey, - value: 0, - onChanged: enabled ? (int newValue) { - setState(() { - groupValue = newValue; - }); - } : null, - hoverColor: Colors.orange[500], - groupValue: groupValue, - ), - ); - }), - ), - ), - ); - } - await tester.pumpWidget(buildApp()); - - await tester.pumpAndSettle(); - expect( - Material.of(tester.element(find.byKey(radioKey))), - paints - ..rect( - color: const Color(0xffffffff), - rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0)) - ..circle(color: const Color(0xff1e88e5)) - ..circle(color: const Color(0xff1e88e5)), - ); - - // Start hovering - final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); - addTearDown(gesture.removePointer); - await gesture.moveTo(tester.getCenter(find.byKey(radioKey))); - - // Check when the radio isn't selected. - groupValue = 1; - await tester.pumpWidget(buildApp()); - await tester.pumpAndSettle(); - expect( - Material.of(tester.element(find.byKey(radioKey))), - paints - ..rect( - color: const Color(0xffffffff), - rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0)) - ..circle(color: Colors.orange[500]) - ..circle(color: const Color(0x8a000000), style: PaintingStyle.stroke, strokeWidth: 2.0) - ); - - // Check when the radio is selected, but disabled. - groupValue = 0; - await tester.pumpWidget(buildApp(enabled: false)); - await tester.pumpAndSettle(); - expect( - Material.of(tester.element(find.byKey(radioKey))), - paints - ..rect( - color: const Color(0xffffffff), - rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0)) - ..circle(color: const Color(0x61000000)) - ..circle(color: const Color(0x61000000)), - ); - }); - - testWidgets('Radio can be toggled by keyboard shortcuts', (WidgetTester tester) async { - tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; - int groupValue = 1; - const Key radioKey0 = Key('radio0'); - const Key radioKey1 = Key('radio1'); - final FocusNode focusNode1 = FocusNode(debugLabel: 'radio1'); - Widget buildApp({bool enabled = true}) { - return MaterialApp( - home: Material( - child: Center( - child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) { - return Container( - width: 100, - height: 100, - color: Colors.white, - child: Row( - children: [ - Radio( - key: radioKey0, - value: 0, - onChanged: enabled ? (int newValue) { - setState(() { - groupValue = newValue; - }); - } : null, - hoverColor: Colors.orange[500], - groupValue: groupValue, - autofocus: true, - ), - Radio( - key: radioKey1, - value: 1, - onChanged: enabled ? (int newValue) { - setState(() { - groupValue = newValue; - }); - } : null, - hoverColor: Colors.orange[500], - groupValue: groupValue, - focusNode: focusNode1, - ), - ], - ), - ); - }), - ), - ), - ); - } - - await tester.pumpWidget(buildApp()); - await tester.pumpAndSettle(); - - await tester.sendKeyEvent(LogicalKeyboardKey.enter); - await tester.pumpAndSettle(); - expect(groupValue, equals(0)); - - focusNode1.requestFocus(); - await tester.pumpAndSettle(); - - await tester.sendKeyEvent(LogicalKeyboardKey.space); - await tester.pumpAndSettle(); - expect(groupValue, equals(1)); - }); - } diff --git a/packages/flutter/test/material/raw_material_button_test.dart b/packages/flutter/test/material/raw_material_button_test.dart index 6bb93d2898..a769865dfc 100644 --- a/packages/flutter/test/material/raw_material_button_test.dart +++ b/packages/flutter/test/material/raw_material_button_test.dart @@ -2,7 +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/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; @@ -48,7 +47,6 @@ void main() { Shortcuts( shortcuts: { LogicalKeySet(LogicalKeyboardKey.enter): const Intent(ActivateAction.key), - LogicalKeySet(LogicalKeyboardKey.space): const Intent(SelectAction.key), }, child: Directionality( textDirection: TextDirection.ltr, @@ -76,12 +74,6 @@ void main() { await tester.pumpAndSettle(); expect(pressed, isTrue); - - pressed = false; - await tester.sendKeyEvent(LogicalKeyboardKey.space); - await tester.pumpAndSettle(); - - expect(pressed, kIsWeb ? isFalse : isTrue); }); testWidgets('materialTapTargetSize.padded expands hit test area', (WidgetTester tester) async { diff --git a/packages/flutter/test/material/switch_test.dart b/packages/flutter/test/material/switch_test.dart index 05e00d37d2..7c0fc91451 100644 --- a/packages/flutter/test/material/switch_test.dart +++ b/packages/flutter/test/material/switch_test.dart @@ -518,7 +518,7 @@ void main() { ), ); await tester.tap(find.byType(Switch)); - final RenderObject object = tester.firstRenderObject(find.byType(Focus)); + final RenderObject object = tester.firstRenderObject(find.byType(Switch)); expect(value, true); expect(semanticEvent, { @@ -623,198 +623,4 @@ void main() { }); - testWidgets('Switch is focusable and has correct focus color', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(debugLabel: 'Switch'); - tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; - bool value = true; - Widget buildApp({bool enabled = true}) { - return MaterialApp( - home: Material( - child: Center( - child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) { - return Switch( - value: value, - onChanged: enabled ? (bool newValue) { - setState(() { - value = newValue; - }); - } : null, - focusColor: Colors.orange[500], - autofocus: true, - focusNode: focusNode, - ); - }), - ), - ), - ); - } - await tester.pumpWidget(buildApp()); - - await tester.pumpAndSettle(); - expect(focusNode.hasPrimaryFocus, isTrue); - expect( - Material.of(tester.element(find.byType(Switch))), - paints - ..rrect( - color: const Color(0x801e88e5), - rrect: RRect.fromLTRBR( - 383.5, 293.0, 416.5, 307.0, const Radius.circular(7.0))) - ..circle(color: Colors.orange[500]) - ..circle(color: const Color(0x33000000)) - ..circle(color: const Color(0x24000000)) - ..circle(color: const Color(0x1f000000)) - ..circle(color: const Color(0xff1e88e5)), - ); - - // Check the false value. - value = false; - await tester.pumpWidget(buildApp()); - await tester.pumpAndSettle(); - expect(focusNode.hasPrimaryFocus, isTrue); - expect( - Material.of(tester.element(find.byType(Switch))), - paints - ..rrect( - color: const Color(0x52000000), - rrect: RRect.fromLTRBR( - 383.5, 293.0, 416.5, 307.0, const Radius.circular(7.0))) - ..circle(color: Colors.orange[500]) - ..circle(color: const Color(0x33000000)) - ..circle(color: const Color(0x24000000)) - ..circle(color: const Color(0x1f000000)) - ..circle(color: const Color(0xfffafafa)), - ); - - // Check what happens when disabled. - value = false; - await tester.pumpWidget(buildApp(enabled: false)); - await tester.pumpAndSettle(); - expect(focusNode.hasPrimaryFocus, isFalse); - expect( - Material.of(tester.element(find.byType(Switch))), - paints - ..rrect( - color: const Color(0x1f000000), - rrect: RRect.fromLTRBR( - 383.5, 293.0, 416.5, 307.0, const Radius.circular(7.0))) - ..circle(color: const Color(0x33000000)) - ..circle(color: const Color(0x24000000)) - ..circle(color: const Color(0x1f000000)) - ..circle(color: const Color(0xffbdbdbd)), - ); - }); - - testWidgets('Switch can be hovered and has correct hover color', (WidgetTester tester) async { - tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; - bool value = true; - Widget buildApp({bool enabled = true}) { - return MaterialApp( - home: Material( - child: Center( - child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) { - return Switch( - value: value, - onChanged: enabled ? (bool newValue) { - setState(() { - value = newValue; - }); - } : null, - hoverColor: Colors.orange[500], - ); - }), - ), - ), - ); - } - await tester.pumpWidget(buildApp()); - await tester.pumpAndSettle(); - expect( - Material.of(tester.element(find.byType(Switch))), - paints - ..rrect( - color: const Color(0x801e88e5), - rrect: RRect.fromLTRBR( - 383.5, 293.0, 416.5, 307.0, const Radius.circular(7.0))) - ..circle(color: const Color(0x33000000)) - ..circle(color: const Color(0x24000000)) - ..circle(color: const Color(0x1f000000)) - ..circle(color: const Color(0xff1e88e5)), - ); - - // Start hovering - final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); - addTearDown(gesture.removePointer); - await gesture.moveTo(tester.getCenter(find.byType(Switch))); - - await tester.pumpWidget(buildApp()); - await tester.pumpAndSettle(); - expect( - Material.of(tester.element(find.byType(Switch))), - paints - ..rrect( - color: const Color(0x801e88e5), - rrect: RRect.fromLTRBR( - 383.5, 293.0, 416.5, 307.0, const Radius.circular(7.0))) - ..circle(color: Colors.orange[500]) - ..circle(color: const Color(0x33000000)) - ..circle(color: const Color(0x24000000)) - ..circle(color: const Color(0x1f000000)) - ..circle(color: const Color(0xff1e88e5)), - ); - - // Check what happens when disabled. - await tester.pumpWidget(buildApp(enabled: false)); - await tester.pumpAndSettle(); - expect( - Material.of(tester.element(find.byType(Switch))), - paints - ..rrect( - color: const Color(0x1f000000), - rrect: RRect.fromLTRBR( - 383.5, 293.0, 416.5, 307.0, const Radius.circular(7.0))) - ..circle(color: const Color(0x33000000)) - ..circle(color: const Color(0x24000000)) - ..circle(color: const Color(0x1f000000)) - ..circle(color: const Color(0xffbdbdbd)), - ); - }); - - testWidgets('Switch can be toggled by keyboard shortcuts', (WidgetTester tester) async { - tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; - bool value = true; - Widget buildApp({bool enabled = true}) { - return MaterialApp( - home: Material( - child: Center( - child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) { - return Switch( - value: value, - onChanged: enabled ? (bool newValue) { - setState(() { - value = newValue; - }); - } : null, - focusColor: Colors.orange[500], - autofocus: true, - ); - }), - ), - ), - ); - } - await tester.pumpWidget(buildApp()); - await tester.pumpAndSettle(); - await tester.sendKeyEvent(LogicalKeyboardKey.enter); - await tester.pumpAndSettle(); - expect(value, isFalse); - await tester.sendKeyEvent(LogicalKeyboardKey.enter); - await tester.pumpAndSettle(); - expect(value, isTrue); - await tester.sendKeyEvent(LogicalKeyboardKey.space); - await tester.pumpAndSettle(); - expect(value, isFalse); - await tester.sendKeyEvent(LogicalKeyboardKey.space); - await tester.pumpAndSettle(); - expect(value, isTrue); - }); } diff --git a/packages/flutter/test/widgets/focus_manager_test.dart b/packages/flutter/test/widgets/focus_manager_test.dart index 81f0382ce5..9c693f3a4b 100644 --- a/packages/flutter/test/widgets/focus_manager_test.dart +++ b/packages/flutter/test/widgets/focus_manager_test.dart @@ -66,12 +66,9 @@ void main() { FocusNode( debugLabel: 'Label', ).debugFillProperties(builder); - final List description = builder.properties.map((DiagnosticsNode n) => n.toString()).toList(); + final List description = builder.properties.where((DiagnosticsNode n) => !n.isFiltered(DiagnosticLevel.info)).map((DiagnosticsNode n) => n.toString()).toList(); expect(description, [ - 'context: null', - 'canRequestFocus: true', - 'hasFocus: false', - 'hasPrimaryFocus: false' + 'debugLabel: "Label"', ]); }); }); @@ -624,12 +621,9 @@ void main() { FocusScopeNode( debugLabel: 'Scope Label', ).debugFillProperties(builder); - final List description = builder.properties.map((DiagnosticsNode n) => n.toString()).toList(); + final List description = builder.properties.where((DiagnosticsNode n) => !n.isFiltered(DiagnosticLevel.info)).map((DiagnosticsNode n) => n.toString()).toList(); expect(description, [ - 'context: null', - 'canRequestFocus: true', - 'hasFocus: false', - 'hasPrimaryFocus: false' + 'debugLabel: "Scope Label"', ]); }); testWidgets('debugDescribeFocusTree produces correct output', (WidgetTester tester) async { @@ -669,36 +663,43 @@ void main() { ' │ primaryFocusCreator: Container-[GlobalKey#00000] ← [root]\n' ' │\n' ' └─rootScope: FocusScopeNode#00000(Root Focus Scope)\n' - ' │ IN FOCUS PATH\n' + ' │ FOCUSED\n' + ' │ debugLabel: "Root Focus Scope"\n' ' │ focusedChildren: FocusScopeNode#00000\n' ' │\n' ' ├─Child 1: FocusScopeNode#00000(Scope 1)\n' ' │ │ context: Container-[GlobalKey#00000]\n' + ' │ │ debugLabel: "Scope 1"\n' ' │ │\n' ' │ └─Child 1: FocusNode#00000(Parent 1)\n' ' │ │ context: Container-[GlobalKey#00000]\n' + ' │ │ debugLabel: "Parent 1"\n' ' │ │\n' ' │ ├─Child 1: FocusNode#00000(Child 1)\n' ' │ │ context: Container-[GlobalKey#00000]\n' + ' │ │ debugLabel: "Child 1"\n' ' │ │\n' ' │ └─Child 2: FocusNode#00000\n' ' │ context: Container-[GlobalKey#00000]\n' ' │\n' ' └─Child 2: FocusScopeNode#00000\n' ' │ context: Container-[GlobalKey#00000]\n' - ' │ IN FOCUS PATH\n' + ' │ FOCUSED\n' ' │ focusedChildren: FocusNode#00000(Child 4)\n' ' │\n' ' └─Child 1: FocusNode#00000(Parent 2)\n' ' │ context: Container-[GlobalKey#00000]\n' - ' │ IN FOCUS PATH\n' + ' │ FOCUSED\n' + ' │ debugLabel: "Parent 2"\n' ' │\n' ' ├─Child 1: FocusNode#00000(Child 3)\n' ' │ context: Container-[GlobalKey#00000]\n' + ' │ debugLabel: "Child 3"\n' ' │\n' ' └─Child 2: FocusNode#00000(Child 4)\n' ' context: Container-[GlobalKey#00000]\n' - ' PRIMARY FOCUS\n' + ' FOCUSED\n' + ' debugLabel: "Child 4"\n' )); }); }); diff --git a/packages/flutter/test/widgets/focus_scope_test.dart b/packages/flutter/test/widgets/focus_scope_test.dart index 84996f2f20..baefc7a4b6 100644 --- a/packages/flutter/test/widgets/focus_scope_test.dart +++ b/packages/flutter/test/widgets/focus_scope_test.dart @@ -229,29 +229,34 @@ void main() { parentFocusScope.toStringDeep(), equalsIgnoringHashCodes('FocusScopeNode#00000(Parent Scope Node)\n' ' │ context: FocusScope\n' - ' │ IN FOCUS PATH\n' + ' │ FOCUSED\n' + ' │ debugLabel: "Parent Scope Node"\n' ' │ focusedChildren: FocusNode#00000(Child)\n' ' │\n' ' └─Child 1: FocusNode#00000(Child)\n' ' context: Focus\n' - ' PRIMARY FOCUS\n'), + ' FOCUSED\n' + ' debugLabel: "Child"\n'), ); expect(WidgetsBinding.instance.focusManager.rootScope, hasAGoodToStringDeep); expect( WidgetsBinding.instance.focusManager.rootScope.toStringDeep(minLevel: DiagnosticLevel.info), equalsIgnoringHashCodes('FocusScopeNode#00000(Root Focus Scope)\n' - ' │ IN FOCUS PATH\n' + ' │ FOCUSED\n' + ' │ debugLabel: "Root Focus Scope"\n' ' │ focusedChildren: FocusScopeNode#00000(Parent Scope Node)\n' ' │\n' ' └─Child 1: FocusScopeNode#00000(Parent Scope Node)\n' ' │ context: FocusScope\n' - ' │ IN FOCUS PATH\n' + ' │ FOCUSED\n' + ' │ debugLabel: "Parent Scope Node"\n' ' │ focusedChildren: FocusNode#00000(Child)\n' ' │\n' ' └─Child 1: FocusNode#00000(Child)\n' ' context: Focus\n' - ' PRIMARY FOCUS\n'), + ' FOCUSED\n' + ' debugLabel: "Child"\n'), ); // Add the child focus scope to the focus tree.