-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Added single open panel functionality #19624
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
Conversation
| /// The duration of the expansion animation. | ||
| final Duration animationDuration; | ||
|
|
||
| //Whether multiple panels can be open simultaneously |
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.
Nit: space between // and text
| if (!widget._allowMultiplePanelsOpen) { | ||
| for (int i = 0; i < widget.children.length; i += 1) { | ||
| assert(widget.children[i] is ExpansionPanelRadio, | ||
| 'All children of ExpansionPanel.radio need to be of type ExpansionPanelRadio'); |
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.
indent with 2 spaces
| } | ||
|
|
||
| assert(_debugCheckExpandLimit(), | ||
| 'This expansion panel widget initialized with more than one panel open'); |
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.
Same here
| @override | ||
| void initState() { | ||
| super.initState(); | ||
| if (!widget._allowMultiplePanelsOpen) { |
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.
Since you are always using the negation of this field, consider changing it to _preventMultiplePanelsOpen or something similar
| } | ||
| _openPanels[pressedChild.value] = !isExpanded; | ||
| } | ||
| setState((){}); |
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.
You only want to setState if you aren't allowing multiple panels
|
|
||
| void _handlePressed(bool isExpanded, int index) { | ||
| if (widget.expansionCallback != null) | ||
| widget.expansionCallback(index, isExpanded); |
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.
Does this callback get invoked twice in radio mode?
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.
You mean by the rest of the code below? I don't think so, only possibly one other panel if it was open before.
| context, | ||
| children[index].isExpanded, | ||
| widget._allowMultiplePanelsOpen ? widget.children[index].isExpanded : | ||
| _openPanels[_widgetChild.value], |
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.
Indent with 2 lines
|
|
||
| testWidgets('Single Panel Open Test', (WidgetTester tester) async { | ||
|
|
||
| List<ExpansionPanel> _demoItems; |
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.
Initialize variables where they are declared
| }) : assert(headerBuilder != null), | ||
| assert(body != null), | ||
| assert(isExpanded != null); | ||
| assert(body != null), |
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.
Nit: keep asserts lined up
| bool panelExpanded = false; | ||
| for (int i = 0; i < widget.children.length; i += 1) { | ||
| assert(widget.children[i] is ExpansionPanelRadio, | ||
| 'All children of ExpansionPanel.radio need to be of type ExpansionPanelRadio'); |
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.
Indent by 2 spaces
| final int childKey = child.value; | ||
| final bool panelWillBeExpanded = child.initializesExpanded || (childKey == _currentOpenPanel?.value); | ||
| assert(!panelExpanded && panelWillBeExpanded || panelExpanded && !panelWillBeExpanded, | ||
| 'Trying to initialize radio panel list with more than two open panels!'); |
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.
Indent by 2 spaces
| void initState() { | ||
| super.initState(); | ||
| if (widget._allowOnlyOnePanelOpen) { | ||
| for (int i = 0; i < widget.children.length; i += 1) { |
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.
Since you don't need the index i for anything, use for (ExpansionPanelRadio child in widget.children), here and elsewhere. I don't think you need to assert in initState unless you are dealing with the radio -> non radio or reverse
| @override | ||
| void didUpdateWidget(ExpansionPanelList oldWidget) { | ||
| super.didUpdateWidget(oldWidget); | ||
|
|
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.
What happens if I change from radio to non-radio or likewise?
| expect(tester.getRect(find.byType(AnimatedSize).at(2)), new Rect.fromLTWH(0.0, 56.0 + 1.0 + 56.0 + 16.0 + 16.0 + 48.0 + 16.0, 800.0, 100.0)); | ||
| }); | ||
|
|
||
| testWidgets('Single Panel Open Test', (WidgetTester tester) async { |
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.
Make sure you test adding/removing panels in radio mode (especially the open panel), and switch from radio to non radio
| }) : assert(headerBuilder != null), | ||
| assert(body != null), | ||
| assert(isExpanded != null); | ||
| }) : assert(headerBuilder != null), |
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.
Remove the extra spaces, here and below in the initializer list.
|
|
||
| /// An expansion panel that allows for radio-like functionality. | ||
| /// | ||
| /// This type of panel will automatically handle closing and opening itself, |
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.
Try and keep these doc comments to 80 columns
| /// An expansion panel that allows for radio-like functionality. | ||
| /// | ||
| /// This type of panel will automatically handle closing and opening itself, | ||
| /// set the [initializesExpanded] value to true on a panel if you want it to begin |
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.
I'm not sure about placing initializesExpanded on the RadioPanel. Try adding another named argument to the ExpansionPanelList.radio constructor which takes a panel or value of a panel to open "only the first time it is created". Then don't both updating in the future, it doesn't make sense to try and keep all of the initializesExpanded and state changed by user interaction in sync.
| @required this.value, | ||
| @required ExpansionPanelHeaderBuilder headerBuilder, | ||
| @required Widget body, | ||
| }) : assert(initializesExpanded != null), |
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.
Same as above, 1 space here.
| /// Whether this panel initializes expanded or not. | ||
| final bool initializesExpanded; | ||
|
|
||
| /// Identifier that corresponds to this specific object. |
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.
"The value that uniquely identifies a radio panel"
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.
Also this should be Object like I said, otherwise I can't use a String key
| _currentOpenPanel = child; | ||
| } | ||
| } | ||
| else if(oldWidget._allowOnlyOnePanelOpen) { |
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.
Move this to the previous line so that it directly follows the previous closing }.
| } | ||
| else if(oldWidget._allowOnlyOnePanelOpen) { | ||
| _currentOpenPanel = null; | ||
| setState((){}); |
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.
You don't/shouldn't setState in didUpdateWidget, because your state is already dirty. Might have been a miscommunication - this is only required in _handlePressed
| if (_isChildExpanded(index) && index != 0 && !_isChildExpanded(index - 1)) | ||
| items.add(new MaterialGap(key: new _SaltedKey<BuildContext, int>(context, index * 2 - 1))); | ||
|
|
||
| final ExpansionPanelRadio _widgetChild = widget._allowOnlyOnePanelOpen ? widget.children[index] : null; |
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.
Don't make locals private, rename to "radioPanel"
| child: widget.children[index].headerBuilder( | ||
| context, | ||
| children[index].isExpanded, | ||
| widget._allowOnlyOnePanelOpen ? _widgetChild.value == _currentOpenPanel?.value: |
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.
use the _isChildExpanded callback
| /// [ExpansionPanelRadio]. | ||
| const ExpansionPanelList.radio({ | ||
| Key key, | ||
| this.children = const <ExpansionPanelRadio>[], |
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.
Something I missed earlier - this isn't a type annotation for children, it is just the type for the default value. You need to use an explicitly typed param with an initializer.
({
List<ExpansionPanelRadio> children = ..,
}) : children = children,
| /// A unique identifier [value] must be assigned to each panel. | ||
| class ExpansionPanelRadio extends ExpansionPanel { | ||
|
|
||
| /// A subclass of expansion panel that allows for radio functionality. |
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.
Just say "An expansion panel" like you had above. Don't put the "subclass of" part in because if that changes, it's easy for the docs to get missed and be out of date.
|
|
||
| /// A subclass of expansion panel that allows for radio functionality. | ||
| /// | ||
| /// A unique identifier [value] must be passed into the constructor. The |
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.
Take out the word "identifier": it reads better as "A unique [value]".
Also, explain why it must be unique: say something like "The [value] is used to indicate which panel is currently selected."
| }) : assert(value != null), | ||
| super(body: body, headerBuilder: headerBuilder); | ||
|
|
||
| /// The value that uniquely identifies a radio panel. |
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.
Add why it exists: "so that the currently selected radio panel may be identified."
| final bool _allowOnlyOnePanelOpen; | ||
|
|
||
| /// The value of the panel that initially begins open. (This value is | ||
| /// only used when initializing with the radio constructor.) |
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.
radio -> [ExpansionPanelList.radio]
| child: new ConstrainedBox( | ||
| constraints: const BoxConstraints(minHeight: _kPanelHeaderCollapsedHeight), | ||
| child: children[index].headerBuilder( | ||
| child: widget.children[index].headerBuilder( |
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.
Since it's used more than once, you can add:
final ExpansionPanelRadio child = widget.children[index];above, and use child.headerBuilder( here, and below.
| /// This widget allows for at most one panel in the list to be open. | ||
| /// The expansion panel callback is triggered when an expansion panel | ||
| /// expand/collapse button is pushed. The [children] and [animationDuration] | ||
| /// arguments must not be null. The [children] objects also must of type |
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.
"objects also must of type" --> "objects must be instances of"

fixes #19240