-
Notifications
You must be signed in to change notification settings - Fork 29.3k
Fix ExpansionPanelList Duplicate Global Keys Exception #31228
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 ExpansionPanelList Duplicate Global Keys Exception #31228
Conversation
Updated documentation with ExpansionPanelList.radio implementation. Also reverted the call order for the radio implementation to call the tapped panel first, and then the closed panel. Also reverted the closed panel call to false instead of true.
Tests were also added to verify |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good; just had one question about semantics.
if (widget._allowOnlyOnePanelOpen) { | ||
final ExpansionPanelRadio pressedChild = widget.children[index]; | ||
|
||
// Determine if an ExpansionPanelRadio was opened prior, invoking |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggested rewording: If another ExpansionPanelRadio was already open, apply its expansionCallback (if any) to false, because it's closing.
However, it's possible that all of the expansion panels were open because previously this was an ordinary expansion panel list?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand the question correctly, I think you're asking about how this logic works with the case where ExpansionPanelList --> press --> ExpansionPanelList.radio
I believe _handlePressed
only opens/closes the radio panel in question, and additionally closing a previous panel if it was an ExpansionPanelList.radio
. Lets say tapping a panel of ExpansionPanelList with all panels open sets state that updates the widget to behave like ExpansionPanelList.radio:
- First,
_handlePressed
will be called and trigger the callback once. - Then,
didUpdateWidget
would update the widget and set the open radio panel, if any
Thus, expansionCallback
will not be called for any of the prior open ExpansionPanelList
panels. Should this be a case we should consider?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense; in this (corner) case there's no need to call the expansionCallback on the formerly open panels, since they were part of an ordinary ExpansionPanelList and its callback wouldn't be called in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Description
The current
ExpansionPanelList
implementation does not work correctly when wrapped withLayoutBuilder
and possiblyStreamBuilder
becausesetState
is being called when panel is pressed, triggering a rebuild of theExpansionPanelList
whileLayoutBuilder
is also trying to rebuild, causing duplicate global keys to be generated and throwing an exception. This seems to have been introduced when trying to manage expanded states forExpansionPanelList.radio
here.This PR does the following:
setState
to only be invoked when guarded bywidget._allowMultiplePanelsOpen
, fixing the case forExpansionPanelList
._currentOpenPanel
toinitialOpenPanelValue
indidUpdateWidget
, since this should only occur oninitState
and not every time the widget is updated. This fixes the problem forExpansionPanelList.radio
.However, there are some design choices I want to consider before moving forward:
ExpansionPanelList.radio
, in this implementation, will callexpansionCallback
twice in the event that one panel is open prior to opening a new one. This is fine, but some documentation would have to be added to explain this behavior if moving forward with this implementation.UPDATE: Documentation and tests were added to verify that
expansionCallback
may be invoked twice in the event that a previously opened panel has to be closed.didUpdateWidget
"guards" for transitions betweenExpansionPanelList
toExpansionPanelList.radio
and vice versa, and while I can't imagine that users will do this much, it's still a possibility and want to ensure that this logic is valid.UPDATE: Added some logic to ensure that
didUpdateWidget
initialized the open panel correctly if changed from an ExpansionPanelList to ExpansionPanelList.radio.I've also considered refactoringExpansionPanelList.radio
into a widget of its own that has its ownExpansionPanelList
widget, since it feels like a lot of the logic diverges into two paths in the current implementation, but that would be a breaking change.Related Issues
Fixes #13780
Tests
expansionCallback
's behavior forExpansionPanelList.radio
.Checklist
Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]
). This will ensure a smooth and quick review process.///
).flutter analyze --flutter-repo
) does not report any problems on my PR.Breaking Change
Does your PR require Flutter developers to manually update their apps to accommodate your change?