Unverified Commit 263de65f authored by Ayush Bherwani's avatar Ayush Bherwani Committed by GitHub

[ExpansionPanelList] adds elevation property (#62840)

parent 71aa7395
......@@ -12,6 +12,7 @@ import 'expand_icon.dart';
import 'ink_well.dart';
import 'material_localizations.dart';
import 'mergeable_material.dart';
import 'shadows.dart';
import 'theme.dart';
const double _kPanelHeaderCollapsedHeight = kMinInteractiveDimension;
......@@ -231,6 +232,7 @@ class ExpansionPanelList extends StatefulWidget {
this.animationDuration = kThemeAnimationDuration,
this.expandedHeaderPadding = _kPanelHeaderExpandedDefaultPadding,
this.dividerColor,
this.elevation = 2,
}) : assert(children != null),
assert(animationDuration != null),
_allowOnlyOnePanelOpen = false,
......@@ -321,6 +323,7 @@ class ExpansionPanelList extends StatefulWidget {
this.initialOpenPanelValue,
this.expandedHeaderPadding = _kPanelHeaderExpandedDefaultPadding,
this.dividerColor,
this.elevation = 2,
}) : assert(children != null),
assert(animationDuration != null),
_allowOnlyOnePanelOpen = true,
......@@ -371,6 +374,17 @@ class ExpansionPanelList extends StatefulWidget {
/// is null, then [ThemeData.dividerColor] is used.
final Color dividerColor;
/// Defines elevation for the [ExpansionPanel] while it's expanded.
///
/// This uses [kElevationToShadow] to simulate shadows, it does not use
/// [Material]'s arbitrary elevation feature.
///
/// The following values can be used to define the elevation: 0, 1, 2, 3, 4, 6,
/// 8, 9, 12, 16, 24.
///
/// By default, the value of elevation is 2.
final int elevation;
@override
State<StatefulWidget> createState() => _ExpansionPanelListState();
}
......@@ -456,6 +470,11 @@ class _ExpansionPanelListState extends State<ExpansionPanelList> {
@override
Widget build(BuildContext context) {
assert(kElevationToShadow.containsKey(widget.elevation),
'Invalid value for elevation. See the kElevationToShadow constant for'
' possible elevation values.'
);
final List<MergeableMaterialItem> items = <MergeableMaterialItem>[];
for (int index = 0; index < widget.children.length; index += 1) {
......@@ -537,6 +556,7 @@ class _ExpansionPanelListState extends State<ExpansionPanelList> {
return MergeableMaterial(
hasDividers: true,
dividerColor: widget.dividerColor,
elevation: widget.elevation,
children: items,
);
}
......
......@@ -33,6 +33,9 @@ const Color _kKeyUmbraOpacity = Color(0x33000000); // alpha = 0.2
const Color _kKeyPenumbraOpacity = Color(0x24000000); // alpha = 0.14
const Color _kAmbientShadowOpacity = Color(0x1F000000); // alpha = 0.12
const Map<int, List<BoxShadow>> _elevationToShadow = <int, List<BoxShadow>>{
// The empty list depicts no elevation.
0: <BoxShadow>[],
1: <BoxShadow>[
BoxShadow(offset: Offset(0.0, 2.0), blurRadius: 1.0, spreadRadius: -1.0, color: _kKeyUmbraOpacity),
BoxShadow(offset: Offset(0.0, 1.0), blurRadius: 1.0, spreadRadius: 0.0, color: _kKeyPenumbraOpacity),
......
......@@ -15,12 +15,14 @@ class SimpleExpansionPanelListTestWidget extends StatefulWidget {
this.canTapOnHeader = false,
this.expandedHeaderPadding,
this.dividerColor,
this.elevation = 2,
}) : super(key: key);
final Key firstPanelKey;
final Key secondPanelKey;
final bool canTapOnHeader;
final Color dividerColor;
final int elevation;
/// If null, the default [ExpansionPanelList]'s expanded header padding value is applied via [defaultExpandedHeaderPadding]
final EdgeInsets expandedHeaderPadding;
......@@ -48,6 +50,7 @@ class _SimpleExpansionPanelListTestWidgetState extends State<SimpleExpansionPane
});
},
dividerColor: widget.dividerColor,
elevation: widget.elevation,
children: <ExpansionPanel>[
ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
......@@ -1396,4 +1399,65 @@ void main() {
// For the last DecoratedBox, we will have a Border.top with the provided dividerColor.
expect(boxDecoration.border.top.color, dividerColor);
});
testWidgets('elevation is propagated properly to MergeableMaterial', (WidgetTester tester) async {
const int _elevation = 8;
// Test for ExpansionPanelList.
await tester.pumpWidget(const MaterialApp(
home: SingleChildScrollView(
child: SimpleExpansionPanelListTestWidget(
elevation: _elevation,
),
),
));
expect(tester.widget<MergeableMaterial>(find.byType(MergeableMaterial)).elevation, _elevation);
// Test for ExpansionPanelList.radio.
await tester.pumpWidget(MaterialApp(
home: SingleChildScrollView(
child: ExpansionPanelList.radio(
elevation: _elevation,
children: <ExpansionPanelRadio>[
ExpansionPanelRadio(
headerBuilder: (BuildContext context, bool isExpanded) {
return Text(isExpanded ? 'B' : 'A', key: const Key('firstKey'));
},
body: const SizedBox(height: 100.0),
value: 0,
),
ExpansionPanelRadio(
headerBuilder: (BuildContext context, bool isExpanded) {
return Text(isExpanded ? 'D' : 'C', key: const Key('secondKey'));
},
body: const SizedBox(height: 100.0),
value: 1,
),
],
),
),
));
expect(tester.widget<MergeableMaterial>(find.byType(MergeableMaterial)).elevation, _elevation);
});
testWidgets('Using a value non defined value throws assertion error', (WidgetTester tester) async {
// It should throw an AssertionError since, 19 is not defined in kElevationToShadow.
await tester.pumpWidget(const MaterialApp(
home: SingleChildScrollView(
child: SimpleExpansionPanelListTestWidget(
elevation: 19,
),
),
));
final dynamic exception = tester.takeException();
expect(exception, isAssertionError);
expect((exception as AssertionError).toString(), contains(
'Invalid value for elevation. See the kElevationToShadow constant for'
' possible elevation values.'
));
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment