-
Notifications
You must be signed in to change notification settings - Fork 29.8k
Description
RenderSemanticsGestureHandler contains multiple setters in the following style:
set onTap(GestureTapCallback value) {
if (_onTap == value)
return;
_onTap = value;
// Do work because value as changed.
}In real words: if the setter is set to the same value it already has no extra work should happen. However, in the precise environment the setters are called this check doesn't work as expected.
The setters are called from _GestureSemantics and the callbacks are set to a member function of that class. While RenderSemanticsGestureHandler is a long-lived object, _GestureSemantics is a short-lived widget. In fact, in every frame, a new _GestureSemantics is build and the callbacks of RenderSemanticsGestureHandler are updated to the new _GestureSemantics's member functions. The member function from the old _GestureSemantics object and the one from the new _GestureSemantics object are not considered equal by Dart, the _onTap == value check in the setter returns false and we do extra work in the setter for nothing as the actual onTap handler might not have changed.
Failing test case to illustrate the problem:
testWidgets('cache unchanged callbacks', (WidgetTester tester) async {
final GestureTapCallback inputCallback = () {};
await tester.pumpWidget(
new Center(
child: new GestureDetector(
onTap: inputCallback,
child: new Container(),
)
)
);
final RenderSemanticsGestureHandler renderObj1 = tester.renderObject(find.byType(GestureDetector));
final GestureTapCallback actualCallback1 = renderObj1.onTap;
await tester.pumpWidget(
new Center(
child: new GestureDetector(
onTap: inputCallback,
child: new Container(),
)
)
);
final RenderSemanticsGestureHandler renderObj2 = tester.renderObject(find.byType(GestureDetector));
final GestureTapCallback actualCallback2 = renderObj2.onTap;
expect(renderObj1, same(renderObj2));
expect(actualCallback1, same(actualCallback2)); // This call fails.
});