Unverified Commit a9c2f8b9 authored by Mohammed  CHAHBOUN's avatar Mohammed CHAHBOUN Committed by GitHub

Add onFocusChange property for ListTile widget (#111498)

parent 50f101ac
...@@ -176,6 +176,7 @@ class CheckboxListTile extends StatelessWidget { ...@@ -176,6 +176,7 @@ class CheckboxListTile extends StatelessWidget {
this.side, this.side,
this.visualDensity, this.visualDensity,
this.focusNode, this.focusNode,
this.onFocusChange,
this.enableFeedback, this.enableFeedback,
}) : assert(tristate != null), }) : assert(tristate != null),
assert(tristate || value != null), assert(tristate || value != null),
...@@ -320,6 +321,9 @@ class CheckboxListTile extends StatelessWidget { ...@@ -320,6 +321,9 @@ class CheckboxListTile extends StatelessWidget {
/// {@macro flutter.widgets.Focus.focusNode} /// {@macro flutter.widgets.Focus.focusNode}
final FocusNode? focusNode; final FocusNode? focusNode;
/// {@macro flutter.material.inkwell.onFocusChange}
final ValueChanged<bool>? onFocusChange;
/// {@macro flutter.material.ListTile.enableFeedback} /// {@macro flutter.material.ListTile.enableFeedback}
/// ///
/// See also: /// See also:
...@@ -401,6 +405,7 @@ class CheckboxListTile extends StatelessWidget { ...@@ -401,6 +405,7 @@ class CheckboxListTile extends StatelessWidget {
tileColor: tileColor, tileColor: tileColor,
visualDensity: visualDensity, visualDensity: visualDensity,
focusNode: focusNode, focusNode: focusNode,
onFocusChange: onFocusChange,
enableFeedback: enableFeedback, enableFeedback: enableFeedback,
), ),
); );
......
...@@ -559,10 +559,12 @@ class InkResponse extends StatelessWidget { ...@@ -559,10 +559,12 @@ class InkResponse extends StatelessWidget {
/// duplication of information. /// duplication of information.
final bool excludeFromSemantics; final bool excludeFromSemantics;
/// {@template flutter.material.inkwell.onFocusChange}
/// Handler called when the focus changes. /// 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 widget's node gains focus, and false if it loses
/// focus. /// focus.
/// {@endtemplate}
final ValueChanged<bool>? onFocusChange; final ValueChanged<bool>? onFocusChange;
/// {@macro flutter.widgets.Focus.autofocus} /// {@macro flutter.widgets.Focus.autofocus}
......
...@@ -282,6 +282,7 @@ class ListTile extends StatelessWidget { ...@@ -282,6 +282,7 @@ class ListTile extends StatelessWidget {
this.enabled = true, this.enabled = true,
this.onTap, this.onTap,
this.onLongPress, this.onLongPress,
this.onFocusChange,
this.mouseCursor, this.mouseCursor,
this.selected = false, this.selected = false,
this.focusColor, this.focusColor,
...@@ -456,6 +457,9 @@ class ListTile extends StatelessWidget { ...@@ -456,6 +457,9 @@ class ListTile extends StatelessWidget {
/// Inoperative if [enabled] is false. /// Inoperative if [enabled] is false.
final GestureLongPressCallback? onLongPress; final GestureLongPressCallback? onLongPress;
/// {@macro flutter.material.inkwell.onFocusChange}
final ValueChanged<bool>? onFocusChange;
/// {@template flutter.material.ListTile.mouseCursor} /// {@template flutter.material.ListTile.mouseCursor}
/// The cursor for a mouse pointer when it enters or is hovering over the /// The cursor for a mouse pointer when it enters or is hovering over the
/// widget. /// widget.
...@@ -738,6 +742,7 @@ class ListTile extends StatelessWidget { ...@@ -738,6 +742,7 @@ class ListTile extends StatelessWidget {
customBorder: shape ?? tileTheme.shape, customBorder: shape ?? tileTheme.shape,
onTap: enabled ? onTap : null, onTap: enabled ? onTap : null,
onLongPress: enabled ? onLongPress : null, onLongPress: enabled ? onLongPress : null,
onFocusChange: onFocusChange,
mouseCursor: effectiveMouseCursor, mouseCursor: effectiveMouseCursor,
canRequestFocus: enabled, canRequestFocus: enabled,
focusNode: focusNode, focusNode: focusNode,
......
...@@ -171,6 +171,7 @@ class RadioListTile<T> extends StatelessWidget { ...@@ -171,6 +171,7 @@ class RadioListTile<T> extends StatelessWidget {
this.selectedTileColor, this.selectedTileColor,
this.visualDensity, this.visualDensity,
this.focusNode, this.focusNode,
this.onFocusChange,
this.enableFeedback, this.enableFeedback,
}) : assert(toggleable != null), }) : assert(toggleable != null),
assert(isThreeLine != null), assert(isThreeLine != null),
...@@ -320,6 +321,9 @@ class RadioListTile<T> extends StatelessWidget { ...@@ -320,6 +321,9 @@ class RadioListTile<T> extends StatelessWidget {
/// {@macro flutter.widgets.Focus.focusNode} /// {@macro flutter.widgets.Focus.focusNode}
final FocusNode? focusNode; final FocusNode? focusNode;
/// {@macro flutter.material.inkwell.onFocusChange}
final ValueChanged<bool>? onFocusChange;
/// {@macro flutter.material.ListTile.enableFeedback} /// {@macro flutter.material.ListTile.enableFeedback}
/// ///
/// See also: /// See also:
...@@ -385,6 +389,7 @@ class RadioListTile<T> extends StatelessWidget { ...@@ -385,6 +389,7 @@ class RadioListTile<T> extends StatelessWidget {
contentPadding: contentPadding, contentPadding: contentPadding,
visualDensity: visualDensity, visualDensity: visualDensity,
focusNode: focusNode, focusNode: focusNode,
onFocusChange: onFocusChange,
enableFeedback: enableFeedback, enableFeedback: enableFeedback,
), ),
); );
......
...@@ -114,6 +114,7 @@ class Switch extends StatelessWidget { ...@@ -114,6 +114,7 @@ class Switch extends StatelessWidget {
this.overlayColor, this.overlayColor,
this.splashRadius, this.splashRadius,
this.focusNode, this.focusNode,
this.onFocusChange,
this.autofocus = false, this.autofocus = false,
}) : _switchType = _SwitchType.material, }) : _switchType = _SwitchType.material,
assert(dragStartBehavior != null), assert(dragStartBehavior != null),
...@@ -158,6 +159,7 @@ class Switch extends StatelessWidget { ...@@ -158,6 +159,7 @@ class Switch extends StatelessWidget {
this.overlayColor, this.overlayColor,
this.splashRadius, this.splashRadius,
this.focusNode, this.focusNode,
this.onFocusChange,
this.autofocus = false, this.autofocus = false,
}) : assert(autofocus != null), }) : assert(autofocus != null),
assert(activeThumbImage != null || onActiveThumbImageError == null), assert(activeThumbImage != null || onActiveThumbImageError == null),
...@@ -455,6 +457,9 @@ class Switch extends StatelessWidget { ...@@ -455,6 +457,9 @@ class Switch extends StatelessWidget {
/// {@macro flutter.widgets.Focus.focusNode} /// {@macro flutter.widgets.Focus.focusNode}
final FocusNode? focusNode; final FocusNode? focusNode;
/// {@macro flutter.material.inkwell.onFocusChange}
final ValueChanged<bool>? onFocusChange;
/// {@macro flutter.widgets.Focus.autofocus} /// {@macro flutter.widgets.Focus.autofocus}
final bool autofocus; final bool autofocus;
...@@ -478,6 +483,7 @@ class Switch extends StatelessWidget { ...@@ -478,6 +483,7 @@ class Switch extends StatelessWidget {
final Size size = _getSwitchSize(context); final Size size = _getSwitchSize(context);
return Focus( return Focus(
focusNode: focusNode, focusNode: focusNode,
onFocusChange: onFocusChange,
autofocus: autofocus, autofocus: autofocus,
child: Container( child: Container(
width: size.width, // Same size as the Material switch. width: size.width, // Same size as the Material switch.
...@@ -518,6 +524,7 @@ class Switch extends StatelessWidget { ...@@ -518,6 +524,7 @@ class Switch extends StatelessWidget {
overlayColor: overlayColor, overlayColor: overlayColor,
splashRadius: splashRadius, splashRadius: splashRadius,
focusNode: focusNode, focusNode: focusNode,
onFocusChange: onFocusChange,
autofocus: autofocus, autofocus: autofocus,
); );
} }
...@@ -577,6 +584,7 @@ class _MaterialSwitch extends StatefulWidget { ...@@ -577,6 +584,7 @@ class _MaterialSwitch extends StatefulWidget {
this.overlayColor, this.overlayColor,
this.splashRadius, this.splashRadius,
this.focusNode, this.focusNode,
this.onFocusChange,
this.autofocus = false, this.autofocus = false,
}) : assert(dragStartBehavior != null), }) : assert(dragStartBehavior != null),
assert(activeThumbImage != null || onActiveThumbImageError == null), assert(activeThumbImage != null || onActiveThumbImageError == null),
...@@ -603,6 +611,7 @@ class _MaterialSwitch extends StatefulWidget { ...@@ -603,6 +611,7 @@ class _MaterialSwitch extends StatefulWidget {
final MaterialStateProperty<Color?>? overlayColor; final MaterialStateProperty<Color?>? overlayColor;
final double? splashRadius; final double? splashRadius;
final FocusNode? focusNode; final FocusNode? focusNode;
final Function(bool)? onFocusChange;
final bool autofocus; final bool autofocus;
final Size size; final Size size;
...@@ -822,6 +831,7 @@ class _MaterialSwitchState extends State<_MaterialSwitch> with TickerProviderSta ...@@ -822,6 +831,7 @@ class _MaterialSwitchState extends State<_MaterialSwitch> with TickerProviderSta
child: buildToggleable( child: buildToggleable(
mouseCursor: effectiveMouseCursor, mouseCursor: effectiveMouseCursor,
focusNode: widget.focusNode, focusNode: widget.focusNode,
onFocusChange: widget.onFocusChange,
autofocus: widget.autofocus, autofocus: widget.autofocus,
size: widget.size, size: widget.size,
painter: _painter painter: _painter
......
...@@ -176,6 +176,7 @@ class SwitchListTile extends StatelessWidget { ...@@ -176,6 +176,7 @@ class SwitchListTile extends StatelessWidget {
this.selectedTileColor, this.selectedTileColor,
this.visualDensity, this.visualDensity,
this.focusNode, this.focusNode,
this.onFocusChange,
this.enableFeedback, this.enableFeedback,
this.hoverColor, this.hoverColor,
}) : _switchListTileType = _SwitchListTileType.material, }) : _switchListTileType = _SwitchListTileType.material,
...@@ -221,6 +222,7 @@ class SwitchListTile extends StatelessWidget { ...@@ -221,6 +222,7 @@ class SwitchListTile extends StatelessWidget {
this.selectedTileColor, this.selectedTileColor,
this.visualDensity, this.visualDensity,
this.focusNode, this.focusNode,
this.onFocusChange,
this.enableFeedback, this.enableFeedback,
this.hoverColor, this.hoverColor,
}) : _switchListTileType = _SwitchListTileType.adaptive, }) : _switchListTileType = _SwitchListTileType.adaptive,
...@@ -368,6 +370,9 @@ class SwitchListTile extends StatelessWidget { ...@@ -368,6 +370,9 @@ class SwitchListTile extends StatelessWidget {
/// {@macro flutter.widgets.Focus.focusNode} /// {@macro flutter.widgets.Focus.focusNode}
final FocusNode? focusNode; final FocusNode? focusNode;
/// {@macro flutter.material.inkwell.onFocusChange}
final ValueChanged<bool>? onFocusChange;
/// {@macro flutter.material.ListTile.enableFeedback} /// {@macro flutter.material.ListTile.enableFeedback}
/// ///
/// See also: /// See also:
...@@ -394,6 +399,7 @@ class SwitchListTile extends StatelessWidget { ...@@ -394,6 +399,7 @@ class SwitchListTile extends StatelessWidget {
inactiveTrackColor: inactiveTrackColor, inactiveTrackColor: inactiveTrackColor,
inactiveThumbColor: inactiveThumbColor, inactiveThumbColor: inactiveThumbColor,
autofocus: autofocus, autofocus: autofocus,
onFocusChange: onFocusChange,
); );
break; break;
...@@ -409,6 +415,7 @@ class SwitchListTile extends StatelessWidget { ...@@ -409,6 +415,7 @@ class SwitchListTile extends StatelessWidget {
inactiveTrackColor: inactiveTrackColor, inactiveTrackColor: inactiveTrackColor,
inactiveThumbColor: inactiveThumbColor, inactiveThumbColor: inactiveThumbColor,
autofocus: autofocus, autofocus: autofocus,
onFocusChange: onFocusChange,
); );
} }
...@@ -452,6 +459,7 @@ class SwitchListTile extends StatelessWidget { ...@@ -452,6 +459,7 @@ class SwitchListTile extends StatelessWidget {
tileColor: tileColor, tileColor: tileColor,
visualDensity: visualDensity, visualDensity: visualDensity,
focusNode: focusNode, focusNode: focusNode,
onFocusChange: onFocusChange,
enableFeedback: enableFeedback, enableFeedback: enableFeedback,
hoverColor: hoverColor, hoverColor: hoverColor,
), ),
......
...@@ -305,6 +305,7 @@ mixin ToggleableStateMixin<S extends StatefulWidget> on TickerProviderStateMixin ...@@ -305,6 +305,7 @@ mixin ToggleableStateMixin<S extends StatefulWidget> on TickerProviderStateMixin
/// build method - potentially after wrapping it in other widgets. /// build method - potentially after wrapping it in other widgets.
Widget buildToggleable({ Widget buildToggleable({
FocusNode? focusNode, FocusNode? focusNode,
Function(bool)? onFocusChange,
bool autofocus = false, bool autofocus = false,
required MaterialStateProperty<MouseCursor> mouseCursor, required MaterialStateProperty<MouseCursor> mouseCursor,
required Size size, required Size size,
...@@ -314,6 +315,7 @@ mixin ToggleableStateMixin<S extends StatefulWidget> on TickerProviderStateMixin ...@@ -314,6 +315,7 @@ mixin ToggleableStateMixin<S extends StatefulWidget> on TickerProviderStateMixin
actions: _actionMap, actions: _actionMap,
focusNode: focusNode, focusNode: focusNode,
autofocus: autofocus, autofocus: autofocus,
onFocusChange: onFocusChange,
enabled: isInteractive, enabled: isInteractive,
onShowFocusHighlight: _handleFocusHighlightChanged, onShowFocusHighlight: _handleFocusHighlightChanged,
onShowHoverHighlight: _handleHoverChanged, onShowHoverHighlight: _handleHoverChanged,
......
...@@ -431,6 +431,35 @@ void main() { ...@@ -431,6 +431,35 @@ void main() {
expect(tileNode.hasPrimaryFocus, isTrue); expect(tileNode.hasPrimaryFocus, isTrue);
}); });
testWidgets('CheckboxListTile onFocusChange callback', (WidgetTester tester) async {
final FocusNode node = FocusNode(debugLabel: 'CheckboxListTile onFocusChange');
bool gotFocus = false;
await tester.pumpWidget(
MaterialApp(
home: Material(
child: CheckboxListTile(
value: true,
focusNode: node,
onFocusChange: (bool focused) {
gotFocus = focused;
},
onChanged: (bool? value) {},
),
),
),
);
node.requestFocus();
await tester.pump();
expect(gotFocus, isTrue);
expect(node.hasFocus, isTrue);
node.unfocus();
await tester.pump();
expect(gotFocus, isFalse);
expect(node.hasFocus, isFalse);
});
testWidgets('CheckboxListTile can be disabled', (WidgetTester tester) async { testWidgets('CheckboxListTile can be disabled', (WidgetTester tester) async {
bool? value = false; bool? value = false;
bool enabled = true; bool enabled = true;
......
...@@ -1476,6 +1476,34 @@ void main() { ...@@ -1476,6 +1476,34 @@ void main() {
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic); expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic);
}); });
testWidgets('ListTile onFocusChange callback', (WidgetTester tester) async {
final FocusNode node = FocusNode(debugLabel: 'ListTile Focus');
bool gotFocus = false;
await tester.pumpWidget(
MaterialApp(
home: Material(
child: ListTile(
focusNode: node,
onFocusChange: (bool focused) {
gotFocus = focused;
},
onTap: () {},
),
),
),
);
node.requestFocus();
await tester.pump();
expect(gotFocus, isTrue);
expect(node.hasFocus, isTrue);
node.unfocus();
await tester.pump();
expect(gotFocus, isFalse);
expect(node.hasFocus, isFalse);
});
testWidgets('ListTile respects tileColor & selectedTileColor', (WidgetTester tester) async { testWidgets('ListTile respects tileColor & selectedTileColor', (WidgetTester tester) async {
bool isSelected = false; bool isSelected = false;
final Color tileColor = Colors.green.shade500; final Color tileColor = Colors.green.shade500;
......
...@@ -793,6 +793,36 @@ void main() { ...@@ -793,6 +793,36 @@ void main() {
expect(tileNode.hasPrimaryFocus, isTrue); expect(tileNode.hasPrimaryFocus, isTrue);
}); });
testWidgets('RadioListTile onFocusChange callback', (WidgetTester tester) async {
final FocusNode node = FocusNode(debugLabel: 'RadioListTile onFocusChange');
bool gotFocus = false;
await tester.pumpWidget(
MaterialApp(
home: Material(
child: RadioListTile<bool>(
value: true,
focusNode: node,
onFocusChange: (bool focused) {
gotFocus = focused;
},
onChanged: (bool? value) {},
groupValue: true,
),
),
),
);
node.requestFocus();
await tester.pump();
expect(gotFocus, isTrue);
expect(node.hasFocus, isTrue);
node.unfocus();
await tester.pump();
expect(gotFocus, isFalse);
expect(node.hasFocus, isFalse);
});
group('feedback', () { group('feedback', () {
late FeedbackTester feedback; late FeedbackTester feedback;
......
...@@ -480,6 +480,64 @@ void main() { ...@@ -480,6 +480,64 @@ void main() {
expect(tileNode.hasPrimaryFocus, isTrue); expect(tileNode.hasPrimaryFocus, isTrue);
}); });
testWidgets('SwitchListTile onFocusChange callback', (WidgetTester tester) async {
final FocusNode node = FocusNode(debugLabel: 'SwitchListTile onFocusChange');
bool gotFocus = false;
await tester.pumpWidget(
MaterialApp(
home: Material(
child: SwitchListTile(
value: true,
focusNode: node,
onFocusChange: (bool focused) {
gotFocus = focused;
},
onChanged: (bool value) {},
),
),
),
);
node.requestFocus();
await tester.pump();
expect(gotFocus, isTrue);
expect(node.hasFocus, isTrue);
node.unfocus();
await tester.pump();
expect(gotFocus, isFalse);
expect(node.hasFocus, isFalse);
});
testWidgets('SwitchListTile.adaptive onFocusChange Callback', (WidgetTester tester) async {
final FocusNode node = FocusNode(debugLabel: 'SwitchListTile.adaptive onFocusChange');
bool gotFocus = false;
await tester.pumpWidget(
MaterialApp(
home: Material(
child: SwitchListTile.adaptive(
value: true,
focusNode: node,
onFocusChange: (bool focused) {
gotFocus = focused;
},
onChanged: (bool value) {},
),
),
),
);
node.requestFocus();
await tester.pump();
expect(gotFocus, isTrue);
expect(node.hasFocus, isTrue);
node.unfocus();
await tester.pump();
expect(gotFocus, isFalse);
expect(node.hasFocus, isFalse);
});
group('feedback', () { group('feedback', () {
late FeedbackTester feedback; late FeedbackTester feedback;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment