mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Prevent material switch from recreating its render object when it becomes disabled (#61398)
This commit is contained in:
parent
70bd819c52
commit
579cab8a86
@ -1092,25 +1092,30 @@ class _FocusableActionDetectorState extends State<FocusableActionDetector> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget child = MouseRegion(
|
||||
onEnter: _handleMouseEnter,
|
||||
onExit: _handleMouseExit,
|
||||
cursor: widget.mouseCursor,
|
||||
child: Focus(
|
||||
focusNode: widget.focusNode,
|
||||
autofocus: widget.autofocus,
|
||||
canRequestFocus: _canRequestFocus,
|
||||
onFocusChange: _handleFocusChange,
|
||||
child: widget.child,
|
||||
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,
|
||||
onExit: _handleMouseExit,
|
||||
cursor: widget.mouseCursor,
|
||||
child: Focus(
|
||||
focusNode: widget.focusNode,
|
||||
autofocus: widget.autofocus,
|
||||
canRequestFocus: _canRequestFocus,
|
||||
onFocusChange: _handleFocusChange,
|
||||
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() {
|
||||
),
|
||||
));
|
||||
|
||||
expect(tester.getSemantics(find.byType(Focus)), matchesSemantics(
|
||||
expect(tester.getSemantics(find.byType(Checkbox)), matchesSemantics(
|
||||
hasCheckedState: true,
|
||||
hasEnabledState: true,
|
||||
isEnabled: true,
|
||||
@ -85,7 +85,7 @@ void main() {
|
||||
),
|
||||
));
|
||||
|
||||
expect(tester.getSemantics(find.byType(Focus)), matchesSemantics(
|
||||
expect(tester.getSemantics(find.byType(Checkbox)), matchesSemantics(
|
||||
hasCheckedState: true,
|
||||
hasEnabledState: true,
|
||||
isChecked: true,
|
||||
@ -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(
|
||||
hasCheckedState: true,
|
||||
hasEnabledState: true,
|
||||
|
||||
@ -237,7 +237,24 @@ void main() {
|
||||
expect(semantics, hasSemantics(TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
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>[
|
||||
SemanticsFlag.hasCheckedState,
|
||||
SemanticsFlag.hasEnabledState,
|
||||
@ -258,7 +275,7 @@ void main() {
|
||||
expect(semantics, hasSemantics(TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
TestSemantics.rootChild(
|
||||
id: 2,
|
||||
id: 1,
|
||||
flags: <SemanticsFlag>[
|
||||
SemanticsFlag.hasCheckedState,
|
||||
SemanticsFlag.isChecked,
|
||||
|
||||
@ -1402,7 +1402,11 @@ void main() {
|
||||
children: <TestSemantics>[
|
||||
TestSemantics(
|
||||
id: 4,
|
||||
flags: <SemanticsFlag>[SemanticsFlag.hasEnabledState],
|
||||
flags: <SemanticsFlag>[
|
||||
SemanticsFlag.hasEnabledState,
|
||||
// isFocusable is delayed by 1 frame.
|
||||
SemanticsFlag.isFocusable,
|
||||
],
|
||||
value: '50%',
|
||||
increasedValue: '55%',
|
||||
decreasedValue: '45%',
|
||||
@ -1420,6 +1424,47 @@ void main() {
|
||||
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();
|
||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux, TargetPlatform.windows }));
|
||||
|
||||
|
||||
@ -1015,4 +1015,51 @@ void main() {
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user