Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Remove attach target on deactivation of widget from overlay portal controller #164439

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

rkishan516
Copy link
Contributor

@rkishan516 rkishan516 commented Mar 2, 2025

Fix: Remove attach target on deactivation of widget from overlay portal controller
fixes: #164376

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • I followed the [breaking change policy] and added [Data Driven Fixes] where supported.
  • All existing and new tests are passing.

@flutter-dashboard
Copy link

It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption, contact "@test-exemption-reviewer" in the #hackers channel in Discord (don't just cc them here, they won't see it!).

If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix?

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. The test exemption team is a small volunteer group, so all reviewers should feel empowered to ask for tests, without delegating that responsibility entirely to the test exemption group.

@github-actions github-actions bot added the framework flutter/packages/flutter repository. See also f: labels. label Mar 2, 2025
Copy link
Contributor

@Piinks Piinks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @rkishan516 thank you for sending a PR!
It looks like this is failing many tests, and lacks tests itself. Can you take a look?
Thanks!

@rkishan516 rkishan516 force-pushed the portal-controller-deactivate branch from ccc208a to a1b0390 Compare March 12, 2025 03:32
@rkishan516 rkishan516 requested a review from Piinks March 12, 2025 03:37
@rkishan516
Copy link
Contributor Author

@Piinks I have made required changes.

@Piinks Piinks requested a review from LongCatIsLooong March 17, 2025 14:56
}
}

@override
void dispose() {
assert(widget.controller._attachTarget == this);
void deactivate() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the OverlayPortal is reparented, the _attachTarget has to be restored right? Also could you add a test for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have pushed the required change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should probably happen in activate instead of didUpdateWidget.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yaa, that will be correct. Let me do the change.

@rkishan516 rkishan516 force-pushed the portal-controller-deactivate branch 2 times, most recently from 4c7cdd2 to 1781190 Compare March 18, 2025 01:23
Copy link
Contributor

@LongCatIsLooong LongCatIsLooong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Detaching the controller from the state when it's deactivated makes sense to me.

However this would create a new corner case: when the state is deactivated, we don't want the developer to call show or hide on the associated controller because the state the controller attaches to may not rebuild. I think that can happen if you cache the OverlayPortal and gives it a global key.

assert(widget.controller._attachTarget == this);
void deactivate() {
super.deactivate();
assert(widget.controller._attachTarget == null || widget.controller._attachTarget == this);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the assert here should be assert(widget.controller._attachTarget == this)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that should be the case but when reattaching with global key, it was causing failures.
But i think since we are moving reattach in activate method, this can be removed.

@rkishan516
Copy link
Contributor Author

Detaching the controller from the state when it's deactivated makes sense to me.

However this would create a new corner case: when the state is deactivated, we don't want the developer to call show or hide on the associated controller because the state the controller attaches to may not rebuild. I think that can happen if you cache the OverlayPortal and gives it a global key.

I will write a test for this and check.

@rkishan516 rkishan516 force-pushed the portal-controller-deactivate branch 4 times, most recently from db30016 to 58584ed Compare March 20, 2025 02:21
@rkishan516
Copy link
Contributor Author

Detaching the controller from the state when it's deactivated makes sense to me.
However this would create a new corner case: when the state is deactivated, we don't want the developer to call show or hide on the associated controller because the state the controller attaches to may not rebuild. I think that can happen if you cache the OverlayPortal and gives it a global key.

I will write a test for this and check.

I have added the test, we don't need to call show method again after reactivation.

@LongCatIsLooong
Copy link
Contributor

It seems there's a leak in one of the new tests. Maybe one of the OverlayEntries were not disposed at the end of the test?

@LongCatIsLooong
Copy link
Contributor

I think setting _attatchTarget to null when the Element is deactivated could change the behavior in some corner cases, and what we want to fix is just the assert in _setupController. Can fix the asserts by:

  • In the _setupController assert, if the attached state is not Element.debugIsActive, then we could allow the re-attach,
  • assert in State.activate that _attachTarget == this

@rkishan516
Copy link
Contributor Author

I think setting _attatchTarget to null when the Element is deactivated could change the behavior in some corner cases, and what we want to fix is just the assert in _setupController. Can fix the asserts by:

  • In the _setupController assert, if the attached state is not Element.debugIsActive, then we could allow the re-attach,
  • assert in State.activate that _attachTarget == this

Element.debugIsActive
Returns true if the Element is active.
This getter always returns false in profile and release builds.

I think this will not solve the issue because in release it will always reattach to newer element.

@rkishan516 rkishan516 force-pushed the portal-controller-deactivate branch 2 times, most recently from 23c1126 to df3f365 Compare March 21, 2025 01:58
@rkishan516
Copy link
Contributor Author

It seems there's a leak in one of the new tests. Maybe one of the OverlayEntries were not disposed at the end of the test?

Even after disposing overlay entries, its showing leak.

@rkishan516 rkishan516 force-pushed the portal-controller-deactivate branch from df3f365 to e3304ec Compare March 21, 2025 02:38
@LongCatIsLooong
Copy link
Contributor

in release

The issue won't happen in release builds. The crash is caused by an assert.

@LongCatIsLooong
Copy link
Contributor

It seems there's a leak in one of the new tests. Maybe one of the OverlayEntries were not disposed at the end of the test?

Even after disposing overlay entries, its showing leak.

The relevant error messages were:

     Actual: <Instance of 'Leaks'>
     Which: contains leaks:
            # The text is generated by leak_tracker.
            # For leak troubleshooting tips open:
            # https://github.com/flutter/flutter/blob/main/docs/contributing/testing/Leak-tracking.md
            notDisposed:
              total: 4
              objects:
                ValueNotifier<_OverlayEntryWidgetState?>:
                  test: attachTarget is restored after reparenting
                  identityHashCode: 933371839
                OverlayEntry:
                  test: attachTarget is restored after reparenting
                  identityHashCode: 947575411
                ValueNotifier<_OverlayEntryWidgetState?>:
                  test: attachTarget is restored after reparenting
                  identityHashCode: 51179505
                OverlayEntry:
                  test: attachTarget is restored after reparenting
                  identityHashCode: 662836894

@rkishan516
Copy link
Contributor Author

in release

The issue won't happen in release builds. The crash is caused by an assert.

Okay, then I will do the change in _setupController.

late OverlayEntry overlayEntry1, overlayEntry2;
addTearDown(() {
overlayEntry1.dispose();
return overlayEntry2.dispose();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: remove the return

);

// Move to second overlay
setState(() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect when the StatefulBuilder rebuilds, the builder callback will be called a second time, creating new OverlayEntries so in the test 4 OverlayEntries were created but only 2 were disposed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yaa, I need to give GlobalKey to Overlay widgets such that they also maintain themself until test is completed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even after adding global key, tests are failing

@rkishan516 rkishan516 force-pushed the portal-controller-deactivate branch from e3304ec to d6c5f3d Compare March 21, 2025 04:08
@rkishan516 rkishan516 force-pushed the portal-controller-deactivate branch from d6c5f3d to cef835f Compare March 21, 2025 04:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
framework flutter/packages/flutter repository. See also f: labels.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

OverlayPortal: Failed to attach OverlayPortalController to _OverlayPortalState on page resize
3 participants