I am running into a problem where I am wanting to remove the space between expansion panels of an expansion panel list when the panels are expanded.
Images of unwanted behavior, these images are taken from flutter documentation:
List when not expanded, which is fine:
List when expanded: - You can see the gap between the sections. This is what I do not want for my app.
Any tips are appreciated.
If you have some prior programming experience, this should make sense...
Find these files:
These files should be located in the External Library > Dart Packages > Flutter > src > material. Note that the material folder may be expressed as "src.material".
Android Studio should allow you to hover over the ExpansionPanel Widget and right-click it. After right-clicking it you should see a list of shortcuts, one being "Go to". Click on the "Go to" option which should bring up more options, one of which being "Implementation(s)". This should bring you to where you need. According to my version, double clicking the widget name so the entire widget's name is highlighted should allow for a keyboard shortcut Ctrl+Alt+B.
The key file you must go into is "mergeable_material.dart". In "mergeable.dart", go to the MaterialGap constructor:
const MaterialGap({
@required LocalKey key,
this.size = 16.0,
//This is the size of the gap between ExpansionTiles WHEN EXPANDED
}) : assert(key != null),
super(key);
This size variable controls the gap between the Expansion Tiles ONLY when EXPANDED. Setting "this.size = 0.0" should remove the gap.
If you want to know why this is, long story short, when the ExpansionPanels list of widgets property is being defined (which is in expansion_panel.dart) a "MaterialGap" is being added between the children. We modified the material gap to be 0.0 thus effectively removing the gap.
Change the Trailing Icon in the Header.
Go to expand.dart file. Go to the bottom of the file where the build method should be. In the return statement, replace the icon button with 'whatever' you want (within reason).
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
assert(debugCheckHasMaterialLocalizations(context));
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
final String onTapHint = widget.isExpanded ? localizations.expandedIconTapHint :
localizations.collapsedIconTapHint;
return Semantics(
onTapHint: widget.onPressed == null ? null : onTapHint,
child: Container() //Replaced the Icon Button here to remove it
);
}
Keep in mind that without a button, you must make canTapOnHeader: true, or you won't be able to expand the panel. You can make sure you don't forget to do this by going to the ExpansionPanel constructor and changing "this.canTapOnHeader = false;" to "this.canTapOnHeader = true;
ExpansionPanel({
@required this.headerBuilder,
@required this.body,
this.isExpanded = false,
this.canTapOnHeader = true, //Changed this to true from false
}) : assert(headerBuilder != null),
assert(body != null),
assert(isExpanded != null),
assert(canTapOnHeader != null);
Remove the shadow and/or dividers
Go to the bottom of the expansion_panel.dart file. This is where the build method should be. At the bottom of the build method should be the return statement, which returns MergeableMaterial. You can do the following:
return MergeableMaterial(
hasDividers: false, //Change the boolean value of this
children: items,
elevation: 0,
//Add this line to remove shadow from elevation, REQUIRES ANOTHER STEP
);
If add the "elevation: 0" property you MUST go back to mergeable_material.dart and add the following line of code:
void _paintShadows(Canvas canvas, Rect rect) {
if (boxShadows == null) return; //ADD THIS LINE OF CODE
for (BoxShadow boxShadow in boxShadows) {
final Paint paint = boxShadow.toPaint();
// TODO(dragostis): Right now, we are only interpolating the border radii
// of the visible Material slices, not the shadows; they are not getting
// interpolated and always have the same rounded radii. Once shadow
// performance is better, shadows should be redrawn every single time the
// slices' radii get interpolated and use those radii not the defaults.
canvas.drawRRect(kMaterialEdges[MaterialType.card].toRRect(rect), paint);
}
}
I think you get the idea, You can do more to remove padding and whatnot by messing around with constants.
Note: I'm relatively new to flutter, (2 months last summer and 2 months now- I'm a college student) in fact. I don't know how modifying these files may impact other widgets but I haven't noticed any issues by doing this. This is also my first post so take it with a grain of salt. Modifying Material implementations may break some rules or conventions that I would refer to the documentation for.
Instead of changing the source code, you're better off making a copy of the expansion_panel.dart
and using this. For the space between the items to disappear, you must comment out on lines 486 and 487.
if (_isChildExpanded(index) && index != 0 && !_isChildExpanded(index - 1))
items.add(MaterialGap(key: _SaltedKey<BuildContext, int>(context, index * 2 - 1)));
And on lines 558 and 559.
if (_isChildExpanded(index) && index != widget.children.length - 1)
items.add(MaterialGap(key: _SaltedKey<BuildContext, int>(context, index * 2 + 1)));
Another issue with this component which you might want to fix, is with the canTapOnHeader
property. Setting it to true allows you to tap the card and expand, but you're stuck with a bunch of dead space on the right side of your card. To fix this, add a check to only show expandIconContainer
(line 526) as follows:
if (!child.canTapOnHeader) expandIconContainer,
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With