[ExpansionPanelList] adds elevation property (#62840)

This commit is contained in:
Ayush Bherwani 2020-08-05 21:56:04 +05:30 committed by GitHub
parent 71aa7395b9
commit 263de65f48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 0 deletions

View File

@ -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,
);
}

View File

@ -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),

View File

@ -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.'
));
});
}