Unverified Commit 579cab8a authored by LongCatIsLooong's avatar LongCatIsLooong Committed by GitHub

Prevent material switch from recreating its render object when it becomes disabled (#61398)

parent 70bd819c
...@@ -1092,7 +1092,17 @@ class _FocusableActionDetectorState extends State<FocusableActionDetector> { ...@@ -1092,7 +1092,17 @@ class _FocusableActionDetectorState extends State<FocusableActionDetector> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget child = MouseRegion( final Map<Type, Action<Intent>> actions = widget.enabled && widget.actions != null
? widget.actions
: const <Type, Action<Intent>>{};
final Map<LogicalKeySet, Intent> shortcuts = widget.enabled && widget.shortcuts != null
? widget.shortcuts
: const <LogicalKeySet, Intent>{};
return Actions(actions: actions,
child: Shortcuts(
shortcuts: shortcuts,
child: MouseRegion(
onEnter: _handleMouseEnter, onEnter: _handleMouseEnter,
onExit: _handleMouseExit, onExit: _handleMouseExit,
cursor: widget.mouseCursor, cursor: widget.mouseCursor,
...@@ -1103,14 +1113,9 @@ class _FocusableActionDetectorState extends State<FocusableActionDetector> { ...@@ -1103,14 +1113,9 @@ class _FocusableActionDetectorState extends State<FocusableActionDetector> {
onFocusChange: _handleFocusChange, onFocusChange: _handleFocusChange,
child: widget.child, child: widget.child,
), ),
),
),
); );
if (widget.enabled && widget.actions != null && widget.actions.isNotEmpty) {
child = Actions(actions: widget.actions, child: child);
}
if (widget.enabled && widget.shortcuts != null && widget.shortcuts.isNotEmpty) {
child = Shortcuts(shortcuts: widget.shortcuts, child: child);
}
return child;
} }
} }
......
...@@ -70,7 +70,7 @@ void main() { ...@@ -70,7 +70,7 @@ void main() {
), ),
)); ));
expect(tester.getSemantics(find.byType(Focus)), matchesSemantics( expect(tester.getSemantics(find.byType(Checkbox)), matchesSemantics(
hasCheckedState: true, hasCheckedState: true,
hasEnabledState: true, hasEnabledState: true,
isEnabled: true, isEnabled: true,
...@@ -85,7 +85,7 @@ void main() { ...@@ -85,7 +85,7 @@ void main() {
), ),
)); ));
expect(tester.getSemantics(find.byType(Focus)), matchesSemantics( expect(tester.getSemantics(find.byType(Checkbox)), matchesSemantics(
hasCheckedState: true, hasCheckedState: true,
hasEnabledState: true, hasEnabledState: true,
isChecked: true, isChecked: true,
...@@ -101,6 +101,15 @@ void main() { ...@@ -101,6 +101,15 @@ void main() {
), ),
)); ));
expect(tester.getSemantics(find.byWidgetPredicate((Widget widget) => widget.runtimeType.toString() == '_CheckboxRenderObjectWidget')), matchesSemantics(
hasCheckedState: true,
hasEnabledState: true,
// isFocusable is delayed by 1 frame.
isFocusable: true,
));
await tester.pump();
// isFocusable should be false now after the 1 frame delay.
expect(tester.getSemantics(find.byWidgetPredicate((Widget widget) => widget.runtimeType.toString() == '_CheckboxRenderObjectWidget')), matchesSemantics( expect(tester.getSemantics(find.byWidgetPredicate((Widget widget) => widget.runtimeType.toString() == '_CheckboxRenderObjectWidget')), matchesSemantics(
hasCheckedState: true, hasCheckedState: true,
hasEnabledState: true, hasEnabledState: true,
......
...@@ -237,7 +237,24 @@ void main() { ...@@ -237,7 +237,24 @@ void main() {
expect(semantics, hasSemantics(TestSemantics.root( expect(semantics, hasSemantics(TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
TestSemantics.rootChild( TestSemantics.rootChild(
id: 2, id: 1,
flags: <SemanticsFlag>[
SemanticsFlag.hasCheckedState,
SemanticsFlag.hasEnabledState,
SemanticsFlag.isInMutuallyExclusiveGroup,
SemanticsFlag.isFocusable, // This flag is delayed by 1 frame.
],
),
],
), ignoreRect: true, ignoreTransform: true));
await tester.pump();
// Now the isFocusable should be gone.
expect(semantics, hasSemantics(TestSemantics.root(
children: <TestSemantics>[
TestSemantics.rootChild(
id: 1,
flags: <SemanticsFlag>[ flags: <SemanticsFlag>[
SemanticsFlag.hasCheckedState, SemanticsFlag.hasCheckedState,
SemanticsFlag.hasEnabledState, SemanticsFlag.hasEnabledState,
...@@ -258,7 +275,7 @@ void main() { ...@@ -258,7 +275,7 @@ void main() {
expect(semantics, hasSemantics(TestSemantics.root( expect(semantics, hasSemantics(TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
TestSemantics.rootChild( TestSemantics.rootChild(
id: 2, id: 1,
flags: <SemanticsFlag>[ flags: <SemanticsFlag>[
SemanticsFlag.hasCheckedState, SemanticsFlag.hasCheckedState,
SemanticsFlag.isChecked, SemanticsFlag.isChecked,
......
...@@ -1402,7 +1402,11 @@ void main() { ...@@ -1402,7 +1402,11 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
TestSemantics( TestSemantics(
id: 4, id: 4,
flags: <SemanticsFlag>[SemanticsFlag.hasEnabledState], flags: <SemanticsFlag>[
SemanticsFlag.hasEnabledState,
// isFocusable is delayed by 1 frame.
SemanticsFlag.isFocusable,
],
value: '50%', value: '50%',
increasedValue: '55%', increasedValue: '55%',
decreasedValue: '45%', decreasedValue: '45%',
...@@ -1420,6 +1424,47 @@ void main() { ...@@ -1420,6 +1424,47 @@ void main() {
ignoreTransform: true, ignoreTransform: true,
), ),
); );
await tester.pump();
expect(
semantics,
hasSemantics(
TestSemantics.root(
children: <TestSemantics>[
TestSemantics(
id: 1,
textDirection: TextDirection.ltr,
children: <TestSemantics>[
TestSemantics(
id: 2,
children: <TestSemantics>[
TestSemantics(
id: 3,
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
children: <TestSemantics>[
TestSemantics(
id: 4,
flags: <SemanticsFlag>[
SemanticsFlag.hasEnabledState,
],
value: '50%',
increasedValue: '55%',
decreasedValue: '45%',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
),
],
),
ignoreRect: true,
ignoreTransform: true,
),
);
semantics.dispose(); semantics.dispose();
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux, TargetPlatform.windows })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux, TargetPlatform.windows }));
......
...@@ -1015,4 +1015,51 @@ void main() { ...@@ -1015,4 +1015,51 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
}); });
testWidgets('Material switch should not recreate its render object when disabled', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/61247.
bool value = true;
bool enabled = true;
StateSetter stateSetter;
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
stateSetter = setState;
return Material(
child: Center(
child: Switch(
value: value,
onChanged: !enabled ? null : (bool newValue) {
setState(() {
value = newValue;
});
},
),
),
);
},
),
),
);
final RenderToggleable oldSwitchRenderObject = tester
.renderObject(find.byWidgetPredicate((Widget widget) => widget is LeafRenderObjectWidget));
stateSetter(() { value = false; });
await tester.pump();
// Disable the switch when the implicit animation begins.
stateSetter(() { enabled = false; });
await tester.pump();
final RenderToggleable updatedSwitchRenderObject = tester
.renderObject(find.byWidgetPredicate((Widget widget) => widget is LeafRenderObjectWidget));
expect(updatedSwitchRenderObject.isInteractive, false);
expect(updatedSwitchRenderObject, oldSwitchRenderObject);
expect(updatedSwitchRenderObject.position.isCompleted, false);
expect(updatedSwitchRenderObject.position.isDismissed, false);
});
} }
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