Skip to content

Caching callbacks in RenderSemanticsGestureHandler doesn't work as expected #11552

Closed

Description

@goderbauer

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.
  });

Activity

  1. goderbauer commented on Aug 8, 2017

    @goderbauer
    MemberAuthor

    Actually, there are additional checks in the setter to ensure that we don't do anything crazy expansive in these cases. With that, the issue doesn't seem to be a (performance) concern. It was just slightly confusing when debugging this. I am going to close this for now.

  2. github-actions commented on Sep 4, 2021

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  3. locked as resolved and limited conversation to collaborators on Sep 4, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions