[ExpansionPanelList] adds elevation property (#62840)
This commit is contained in:
parent
71aa7395b9
commit
263de65f48
@ -12,6 +12,7 @@ import 'expand_icon.dart';
|
|||||||
import 'ink_well.dart';
|
import 'ink_well.dart';
|
||||||
import 'material_localizations.dart';
|
import 'material_localizations.dart';
|
||||||
import 'mergeable_material.dart';
|
import 'mergeable_material.dart';
|
||||||
|
import 'shadows.dart';
|
||||||
import 'theme.dart';
|
import 'theme.dart';
|
||||||
|
|
||||||
const double _kPanelHeaderCollapsedHeight = kMinInteractiveDimension;
|
const double _kPanelHeaderCollapsedHeight = kMinInteractiveDimension;
|
||||||
@ -231,6 +232,7 @@ class ExpansionPanelList extends StatefulWidget {
|
|||||||
this.animationDuration = kThemeAnimationDuration,
|
this.animationDuration = kThemeAnimationDuration,
|
||||||
this.expandedHeaderPadding = _kPanelHeaderExpandedDefaultPadding,
|
this.expandedHeaderPadding = _kPanelHeaderExpandedDefaultPadding,
|
||||||
this.dividerColor,
|
this.dividerColor,
|
||||||
|
this.elevation = 2,
|
||||||
}) : assert(children != null),
|
}) : assert(children != null),
|
||||||
assert(animationDuration != null),
|
assert(animationDuration != null),
|
||||||
_allowOnlyOnePanelOpen = false,
|
_allowOnlyOnePanelOpen = false,
|
||||||
@ -321,6 +323,7 @@ class ExpansionPanelList extends StatefulWidget {
|
|||||||
this.initialOpenPanelValue,
|
this.initialOpenPanelValue,
|
||||||
this.expandedHeaderPadding = _kPanelHeaderExpandedDefaultPadding,
|
this.expandedHeaderPadding = _kPanelHeaderExpandedDefaultPadding,
|
||||||
this.dividerColor,
|
this.dividerColor,
|
||||||
|
this.elevation = 2,
|
||||||
}) : assert(children != null),
|
}) : assert(children != null),
|
||||||
assert(animationDuration != null),
|
assert(animationDuration != null),
|
||||||
_allowOnlyOnePanelOpen = true,
|
_allowOnlyOnePanelOpen = true,
|
||||||
@ -371,6 +374,17 @@ class ExpansionPanelList extends StatefulWidget {
|
|||||||
/// is null, then [ThemeData.dividerColor] is used.
|
/// is null, then [ThemeData.dividerColor] is used.
|
||||||
final Color dividerColor;
|
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
|
@override
|
||||||
State<StatefulWidget> createState() => _ExpansionPanelListState();
|
State<StatefulWidget> createState() => _ExpansionPanelListState();
|
||||||
}
|
}
|
||||||
@ -456,6 +470,11 @@ class _ExpansionPanelListState extends State<ExpansionPanelList> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
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>[];
|
final List<MergeableMaterialItem> items = <MergeableMaterialItem>[];
|
||||||
|
|
||||||
for (int index = 0; index < widget.children.length; index += 1) {
|
for (int index = 0; index < widget.children.length; index += 1) {
|
||||||
@ -537,6 +556,7 @@ class _ExpansionPanelListState extends State<ExpansionPanelList> {
|
|||||||
return MergeableMaterial(
|
return MergeableMaterial(
|
||||||
hasDividers: true,
|
hasDividers: true,
|
||||||
dividerColor: widget.dividerColor,
|
dividerColor: widget.dividerColor,
|
||||||
|
elevation: widget.elevation,
|
||||||
children: items,
|
children: items,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,9 @@ const Color _kKeyUmbraOpacity = Color(0x33000000); // alpha = 0.2
|
|||||||
const Color _kKeyPenumbraOpacity = Color(0x24000000); // alpha = 0.14
|
const Color _kKeyPenumbraOpacity = Color(0x24000000); // alpha = 0.14
|
||||||
const Color _kAmbientShadowOpacity = Color(0x1F000000); // alpha = 0.12
|
const Color _kAmbientShadowOpacity = Color(0x1F000000); // alpha = 0.12
|
||||||
const Map<int, List<BoxShadow>> _elevationToShadow = <int, List<BoxShadow>>{
|
const Map<int, List<BoxShadow>> _elevationToShadow = <int, List<BoxShadow>>{
|
||||||
|
// The empty list depicts no elevation.
|
||||||
|
0: <BoxShadow>[],
|
||||||
|
|
||||||
1: <BoxShadow>[
|
1: <BoxShadow>[
|
||||||
BoxShadow(offset: Offset(0.0, 2.0), blurRadius: 1.0, spreadRadius: -1.0, color: _kKeyUmbraOpacity),
|
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),
|
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.canTapOnHeader = false,
|
||||||
this.expandedHeaderPadding,
|
this.expandedHeaderPadding,
|
||||||
this.dividerColor,
|
this.dividerColor,
|
||||||
|
this.elevation = 2,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final Key firstPanelKey;
|
final Key firstPanelKey;
|
||||||
final Key secondPanelKey;
|
final Key secondPanelKey;
|
||||||
final bool canTapOnHeader;
|
final bool canTapOnHeader;
|
||||||
final Color dividerColor;
|
final Color dividerColor;
|
||||||
|
final int elevation;
|
||||||
|
|
||||||
/// If null, the default [ExpansionPanelList]'s expanded header padding value is applied via [defaultExpandedHeaderPadding]
|
/// If null, the default [ExpansionPanelList]'s expanded header padding value is applied via [defaultExpandedHeaderPadding]
|
||||||
final EdgeInsets expandedHeaderPadding;
|
final EdgeInsets expandedHeaderPadding;
|
||||||
@ -48,6 +50,7 @@ class _SimpleExpansionPanelListTestWidgetState extends State<SimpleExpansionPane
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
dividerColor: widget.dividerColor,
|
dividerColor: widget.dividerColor,
|
||||||
|
elevation: widget.elevation,
|
||||||
children: <ExpansionPanel>[
|
children: <ExpansionPanel>[
|
||||||
ExpansionPanel(
|
ExpansionPanel(
|
||||||
headerBuilder: (BuildContext context, bool isExpanded) {
|
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.
|
// For the last DecoratedBox, we will have a Border.top with the provided dividerColor.
|
||||||
expect(boxDecoration.border.top.color, 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.'
|
||||||
|
));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user