From e446974f78ce93670020e52719b5676e8236f4ee Mon Sep 17 00:00:00 2001 From: Morris Kurz Date: Mon, 30 May 2022 23:26:09 +0200 Subject: [PATCH 1/8] Increase minimum height of headerWidget in ExpansionPanel to smooth the animation. Signed-off-by: Morris Kurz --- packages/flutter/lib/src/material/expansion_panel.dart | 6 +++--- packages/flutter/test/material/expansion_panel_test.dart | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/flutter/lib/src/material/expansion_panel.dart b/packages/flutter/lib/src/material/expansion_panel.dart index d2925007f9147..cfe416745b502 100644 --- a/packages/flutter/lib/src/material/expansion_panel.dart +++ b/packages/flutter/lib/src/material/expansion_panel.dart @@ -4,7 +4,6 @@ import 'package:flutter/widgets.dart'; -import 'constants.dart'; import 'expand_icon.dart'; import 'ink_well.dart'; import 'material_localizations.dart'; @@ -12,9 +11,10 @@ import 'mergeable_material.dart'; import 'shadows.dart'; import 'theme.dart'; -const double _kPanelHeaderCollapsedHeight = kMinInteractiveDimension; +// Fix for #5848. The size is the same as the Icon Button used in the header. +const double _kPanelHeaderCollapsedHeight = 24 + 16 * 2; const EdgeInsets _kPanelHeaderExpandedDefaultPadding = EdgeInsets.symmetric( - vertical: 64.0 - _kPanelHeaderCollapsedHeight, + vertical: 12, ); class _SaltedKey extends LocalKey { diff --git a/packages/flutter/test/material/expansion_panel_test.dart b/packages/flutter/test/material/expansion_panel_test.dart index cea8f6a88c5ae..7f5e9b2add46c 100644 --- a/packages/flutter/test/material/expansion_panel_test.dart +++ b/packages/flutter/test/material/expansion_panel_test.dart @@ -1285,7 +1285,7 @@ void main() { // No padding applied to closed header RenderBox box = tester.renderObject(find.ancestor(of: find.byKey(firstPanelKey), matching: find.byType(AnimatedContainer)).first); - expect(box.size.height, equals(48.0)); // _kPanelHeaderCollapsedHeight + expect(box.size.height, equals(56.0)); // _kPanelHeaderCollapsedHeight expect(box.size.width, equals(736.0)); // Now, expand the child panel. @@ -1298,7 +1298,7 @@ void main() { // Padding is added to expanded header box = tester.renderObject(find.ancestor(of: find.byKey(firstPanelKey), matching: find.byType(AnimatedContainer)).first); - expect(box.size.height, equals(80.0)); // _kPanelHeaderCollapsedHeight + 32.0 (double default padding) + expect(box.size.height, equals(80.0)); // _kPanelHeaderCollapsedHeight + 24.0 (double default padding) expect(box.size.width, equals(736.0)); }); @@ -1323,7 +1323,7 @@ void main() { // No padding applied to closed header RenderBox box = tester.renderObject(find.ancestor(of: find.byKey(firstPanelKey), matching: find.byType(AnimatedContainer)).first); - expect(box.size.height, equals(48.0)); // _kPanelHeaderCollapsedHeight + expect(box.size.height, equals(56.0)); // _kPanelHeaderCollapsedHeight expect(box.size.width, equals(736.0)); // Now, expand the child panel. @@ -1336,7 +1336,7 @@ void main() { // Padding is added to expanded header box = tester.renderObject(find.ancestor(of: find.byKey(firstPanelKey), matching: find.byType(AnimatedContainer)).first); - expect(box.size.height, equals(128.0)); // _kPanelHeaderCollapsedHeight + 80.0 (double padding) + expect(box.size.height, equals(136.0)); // _kPanelHeaderCollapsedHeight + 80.0 (double padding) expect(box.size.width, equals(736.0)); }); From e9bad48acf7016bf57c7ecff81221cdb714b8ba0 Mon Sep 17 00:00:00 2001 From: Morris Kurz Date: Tue, 31 May 2022 14:11:09 +0200 Subject: [PATCH 2/8] Add regression tests that check for equal height of header elements in ExpansionPanel. Signed-off-by: Morris Kurz --- .../test/material/expansion_panel_test.dart | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/packages/flutter/test/material/expansion_panel_test.dart b/packages/flutter/test/material/expansion_panel_test.dart index 7f5e9b2add46c..1f48bcc3c6529 100644 --- a/packages/flutter/test/material/expansion_panel_test.dart +++ b/packages/flutter/test/material/expansion_panel_test.dart @@ -1302,6 +1302,54 @@ void main() { expect(box.size.width, equals(736.0)); }); + // Regression test for #5848 + testWidgets('The AnimatedContainer and IconButton have the same height so that animations are smooth', (WidgetTester tester) async { + const Key firstPanelKey = Key('firstPanelKey'); + + await tester.pumpWidget( + const MaterialApp( + home: SingleChildScrollView( + child: SimpleExpansionPanelListTestWidget( + firstPanelKey: firstPanelKey, + canTapOnHeader: true, + ), + ), + ), + ); + + // The panel is closed + expect(find.text('A'), findsOneWidget); + expect(find.text('B'), findsNothing); + + // No padding applied to closed header + final RenderBox boxOfContainer = tester.renderObject(find.ancestor(of: find.byKey(firstPanelKey), matching: find.byType(AnimatedContainer)).first); + final RenderBox boxOfIconButton = tester.renderObject(find.byType(IconButton).first); + expect(boxOfContainer.size.height, equals(boxOfIconButton.size.height)); + }); + + testWidgets("The AnimatedContainer's height is at least kMinInteractiveDimension", (WidgetTester tester) async { + const Key firstPanelKey = Key('firstPanelKey'); + + await tester.pumpWidget( + const MaterialApp( + home: SingleChildScrollView( + child: SimpleExpansionPanelListTestWidget( + firstPanelKey: firstPanelKey, + canTapOnHeader: true, + ), + ), + ), + ); + + // The panel is closed + expect(find.text('A'), findsOneWidget); + expect(find.text('B'), findsNothing); + + // No padding applied to closed header + final RenderBox box = tester.renderObject(find.ancestor(of: find.byKey(firstPanelKey), matching: find.byType(AnimatedContainer)).first); + expect(box.size.height, greaterThanOrEqualTo(kMinInteractiveDimension)); + }); + testWidgets('Correct custom header padding', (WidgetTester tester) async { const Key firstPanelKey = Key('firstPanelKey'); From ab57bae273ecd8ef05656d9c46795ee6988358e6 Mon Sep 17 00:00:00 2001 From: Morris Kurz Date: Tue, 31 May 2022 14:11:23 +0200 Subject: [PATCH 3/8] Clarify comment. Signed-off-by: Morris Kurz --- packages/flutter/lib/src/material/expansion_panel.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/flutter/lib/src/material/expansion_panel.dart b/packages/flutter/lib/src/material/expansion_panel.dart index cfe416745b502..ab6bb6013224c 100644 --- a/packages/flutter/lib/src/material/expansion_panel.dart +++ b/packages/flutter/lib/src/material/expansion_panel.dart @@ -11,7 +11,8 @@ import 'mergeable_material.dart'; import 'shadows.dart'; import 'theme.dart'; -// Fix for #5848. The size is the same as the Icon Button used in the header. +// Fix for #5848. The height of the collasped header should be the same as the +// height (24px icon size + 2 * 16px padding) Icon Button used in the header. const double _kPanelHeaderCollapsedHeight = 24 + 16 * 2; const EdgeInsets _kPanelHeaderExpandedDefaultPadding = EdgeInsets.symmetric( vertical: 12, From 2d2fdc45cb9e9a2dbd12a1c1cc8143c9b4e88b42 Mon Sep 17 00:00:00 2001 From: Morris Kurz Date: Mon, 6 Jun 2022 16:49:24 +0200 Subject: [PATCH 4/8] Reduce padding in ExpandIcon to 12px s.t. header height is 48px. Signed-off-by: Morris Kurz --- packages/flutter/lib/src/material/expansion_panel.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/flutter/lib/src/material/expansion_panel.dart b/packages/flutter/lib/src/material/expansion_panel.dart index ab6bb6013224c..fb54159d5f53a 100644 --- a/packages/flutter/lib/src/material/expansion_panel.dart +++ b/packages/flutter/lib/src/material/expansion_panel.dart @@ -4,6 +4,7 @@ import 'package:flutter/widgets.dart'; +import 'constants.dart'; import 'expand_icon.dart'; import 'ink_well.dart'; import 'material_localizations.dart'; @@ -11,12 +12,11 @@ import 'mergeable_material.dart'; import 'shadows.dart'; import 'theme.dart'; -// Fix for #5848. The height of the collasped header should be the same as the -// height (24px icon size + 2 * 16px padding) Icon Button used in the header. -const double _kPanelHeaderCollapsedHeight = 24 + 16 * 2; +const double _kPanelHeaderCollapsedHeight = kMinInteractiveDimension; const EdgeInsets _kPanelHeaderExpandedDefaultPadding = EdgeInsets.symmetric( - vertical: 12, + vertical: 64.0 - _kPanelHeaderCollapsedHeight, ); +const EdgeInsets _kExpandIconPadding = EdgeInsets.all(12.0); class _SaltedKey extends LocalKey { const _SaltedKey(this.salt, this.value); @@ -358,7 +358,7 @@ class _ExpansionPanelListState extends State { margin: const EdgeInsetsDirectional.only(end: 8.0), child: ExpandIcon( isExpanded: _isChildExpanded(index), - padding: const EdgeInsets.all(16.0), + padding: _kExpandIconPadding, onPressed: !child.canTapOnHeader ? (bool isExpanded) => _handlePressed(isExpanded, index) : null, From 716ed6c190ba3fd060738db44cdeb55cd9e1d382 Mon Sep 17 00:00:00 2001 From: Morris Kurz Date: Mon, 6 Jun 2022 16:50:26 +0200 Subject: [PATCH 5/8] Update testcases to new header height (56px -> 48px). Signed-off-by: Morris Kurz --- .../test/material/expansion_panel_test.dart | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/flutter/test/material/expansion_panel_test.dart b/packages/flutter/test/material/expansion_panel_test.dart index 1f48bcc3c6529..efd20fef4983b 100644 --- a/packages/flutter/test/material/expansion_panel_test.dart +++ b/packages/flutter/test/material/expansion_panel_test.dart @@ -310,37 +310,37 @@ void main() { await tester.pumpWidget(build(false, false, false)); expect(tester.renderObjectList(find.byType(AnimatedSize)), hasLength(3)); - expect(tester.getRect(find.byType(AnimatedSize).at(0)), const Rect.fromLTWH(0.0, 56.0, 800.0, 0.0)); - expect(tester.getRect(find.byType(AnimatedSize).at(1)), const Rect.fromLTWH(0.0, 113.0, 800.0, 0.0)); - expect(tester.getRect(find.byType(AnimatedSize).at(2)), const Rect.fromLTWH(0.0, 170.0, 800.0, 0.0)); + expect(tester.getRect(find.byType(AnimatedSize).at(0)), const Rect.fromLTWH(0.0, 48.0, 800.0, 0.0)); + expect(tester.getRect(find.byType(AnimatedSize).at(1)), const Rect.fromLTWH(0.0, 97.0, 800.0, 0.0)); + expect(tester.getRect(find.byType(AnimatedSize).at(2)), const Rect.fromLTWH(0.0, 146.0, 800.0, 0.0)); await tester.pump(const Duration(milliseconds: 200)); - expect(tester.getRect(find.byType(AnimatedSize).at(0)), const Rect.fromLTWH(0.0, 56.0, 800.0, 0.0)); - expect(tester.getRect(find.byType(AnimatedSize).at(1)), const Rect.fromLTWH(0.0, 113.0, 800.0, 0.0)); - expect(tester.getRect(find.byType(AnimatedSize).at(2)), const Rect.fromLTWH(0.0, 170.0, 800.0, 0.0)); + expect(tester.getRect(find.byType(AnimatedSize).at(0)), const Rect.fromLTWH(0.0, 48.0, 800.0, 0.0)); + expect(tester.getRect(find.byType(AnimatedSize).at(1)), const Rect.fromLTWH(0.0, 97.0, 800.0, 0.0)); + expect(tester.getRect(find.byType(AnimatedSize).at(2)), const Rect.fromLTWH(0.0, 146.0, 800.0, 0.0)); await tester.pumpWidget(build(false, true, false)); - expect(tester.getRect(find.byType(AnimatedSize).at(0)), const Rect.fromLTWH(0.0, 56.0, 800.0, 0.0)); - expect(tester.getRect(find.byType(AnimatedSize).at(1)), const Rect.fromLTWH(0.0, 113.0, 800.0, 0.0)); - expect(tester.getRect(find.byType(AnimatedSize).at(2)), const Rect.fromLTWH(0.0, 170.0, 800.0, 0.0)); + expect(tester.getRect(find.byType(AnimatedSize).at(0)), const Rect.fromLTWH(0.0, 48.0, 800.0, 0.0)); + expect(tester.getRect(find.byType(AnimatedSize).at(1)), const Rect.fromLTWH(0.0, 97.0, 800.0, 0.0)); + expect(tester.getRect(find.byType(AnimatedSize).at(2)), const Rect.fromLTWH(0.0, 146.0, 800.0, 0.0)); await tester.pump(kSizeAnimationDuration ~/ 2); - expect(tester.getRect(find.byType(AnimatedSize).at(0)), const Rect.fromLTWH(0.0, 56.0, 800.0, 0.0)); + expect(tester.getRect(find.byType(AnimatedSize).at(0)), const Rect.fromLTWH(0.0, 48.0, 800.0, 0.0)); final Rect rect1 = tester.getRect(find.byType(AnimatedSize).at(1)); expect(rect1.left, 0.0); - expect(rect1.top, inExclusiveRange(113.0, 113.0 + 16.0 + 32.0)); // 16.0 material gap, plus 16.0 top and bottom margins added to the header + expect(rect1.top, inExclusiveRange(113.0, 113.0 + 16.0 + 24.0)); // 16.0 material gap, plus 12.0 top and bottom margins added to the header expect(rect1.width, 800.0); expect(rect1.height, inExclusiveRange(0.0, 100.0)); final Rect rect2 = tester.getRect(find.byType(AnimatedSize).at(2)); - expect(rect2, Rect.fromLTWH(0.0, rect1.bottom + 16.0 + 56.0, 800.0, 0.0)); // the 16.0 comes from the MaterialGap being introduced, the 56.0 is the header height. + expect(rect2, Rect.fromLTWH(0.0, rect1.bottom + 16.0 + 48.0, 800.0, 0.0)); // the 16.0 comes from the MaterialGap being introduced, the 48.0 is the header height. await tester.pumpWidget(build(false, false, false)); - expect(tester.getRect(find.byType(AnimatedSize).at(0)), const Rect.fromLTWH(0.0, 56.0, 800.0, 0.0)); + expect(tester.getRect(find.byType(AnimatedSize).at(0)), const Rect.fromLTWH(0.0, 48.0, 800.0, 0.0)); expect(tester.getRect(find.byType(AnimatedSize).at(1)), rect1); expect(tester.getRect(find.byType(AnimatedSize).at(2)), rect2); await tester.pumpWidget(build(false, false, true)); - expect(tester.getRect(find.byType(AnimatedSize).at(0)), const Rect.fromLTWH(0.0, 56.0, 800.0, 0.0)); + expect(tester.getRect(find.byType(AnimatedSize).at(0)), const Rect.fromLTWH(0.0, 48.0, 800.0, 0.0)); expect(tester.getRect(find.byType(AnimatedSize).at(1)), rect1); expect(tester.getRect(find.byType(AnimatedSize).at(2)), rect2); @@ -348,14 +348,14 @@ void main() { await tester.pump(); await tester.pump(); await tester.pump(); - expect(tester.getRect(find.byType(AnimatedSize).at(0)), const Rect.fromLTWH(0.0, 56.0, 800.0, 0.0)); + expect(tester.getRect(find.byType(AnimatedSize).at(0)), const Rect.fromLTWH(0.0, 48.0, 800.0, 0.0)); expect(tester.getRect(find.byType(AnimatedSize).at(1)), rect1); expect(tester.getRect(find.byType(AnimatedSize).at(2)), rect2); await tester.pumpAndSettle(); - expect(tester.getRect(find.byType(AnimatedSize).at(0)), const Rect.fromLTWH(0.0, 56.0, 800.0, 0.0)); - expect(tester.getRect(find.byType(AnimatedSize).at(1)), const Rect.fromLTWH(0.0, 56.0 + 1.0 + 56.0, 800.0, 0.0)); - expect(tester.getRect(find.byType(AnimatedSize).at(2)), const Rect.fromLTWH(0.0, 56.0 + 1.0 + 56.0 + 16.0 + 16.0 + 48.0 + 16.0, 800.0, 100.0)); + expect(tester.getRect(find.byType(AnimatedSize).at(0)), const Rect.fromLTWH(0.0, 48.0, 800.0, 0.0)); + expect(tester.getRect(find.byType(AnimatedSize).at(1)), const Rect.fromLTWH(0.0, 48.0 + 1.0 + 48.0, 800.0, 0.0)); + expect(tester.getRect(find.byType(AnimatedSize).at(2)), const Rect.fromLTWH(0.0, 48.0 + 1.0 + 48.0 + 16.0 + 16.0 + 48.0 + 16.0, 800.0, 100.0)); }); testWidgets('Radio mode has max of one panel open at a time', (WidgetTester tester) async { @@ -1285,8 +1285,8 @@ void main() { // No padding applied to closed header RenderBox box = tester.renderObject(find.ancestor(of: find.byKey(firstPanelKey), matching: find.byType(AnimatedContainer)).first); - expect(box.size.height, equals(56.0)); // _kPanelHeaderCollapsedHeight - expect(box.size.width, equals(736.0)); + expect(box.size.height, equals(48.0)); // _kPanelHeaderCollapsedHeight + expect(box.size.width, equals(744.0)); // Now, expand the child panel. await tester.tap(find.byKey(firstPanelKey)); @@ -1299,11 +1299,11 @@ void main() { // Padding is added to expanded header box = tester.renderObject(find.ancestor(of: find.byKey(firstPanelKey), matching: find.byType(AnimatedContainer)).first); expect(box.size.height, equals(80.0)); // _kPanelHeaderCollapsedHeight + 24.0 (double default padding) - expect(box.size.width, equals(736.0)); + expect(box.size.width, equals(744.0)); }); // Regression test for #5848 - testWidgets('The AnimatedContainer and IconButton have the same height so that animations are smooth', (WidgetTester tester) async { + testWidgets('The AnimatedContainer and IconButton have the same height of 48px', (WidgetTester tester) async { const Key firstPanelKey = Key('firstPanelKey'); await tester.pumpWidget( @@ -1371,8 +1371,8 @@ void main() { // No padding applied to closed header RenderBox box = tester.renderObject(find.ancestor(of: find.byKey(firstPanelKey), matching: find.byType(AnimatedContainer)).first); - expect(box.size.height, equals(56.0)); // _kPanelHeaderCollapsedHeight - expect(box.size.width, equals(736.0)); + expect(box.size.height, equals(48.0)); // _kPanelHeaderCollapsedHeight + expect(box.size.width, equals(744.0)); // Now, expand the child panel. await tester.tap(find.byKey(firstPanelKey)); @@ -1384,8 +1384,8 @@ void main() { // Padding is added to expanded header box = tester.renderObject(find.ancestor(of: find.byKey(firstPanelKey), matching: find.byType(AnimatedContainer)).first); - expect(box.size.height, equals(136.0)); // _kPanelHeaderCollapsedHeight + 80.0 (double padding) - expect(box.size.width, equals(736.0)); + expect(box.size.height, equals(128.0)); // _kPanelHeaderCollapsedHeight + 80.0 (double padding) + expect(box.size.width, equals(744.0)); }); testWidgets('ExpansionPanelList respects dividerColor', (WidgetTester tester) async { From b102fb9332bdedb56bf91c5dff47a42ca15cef3f Mon Sep 17 00:00:00 2001 From: Morris Kurz Date: Mon, 6 Jun 2022 16:52:14 +0200 Subject: [PATCH 6/8] Test for header height equal to 48px. Signed-off-by: Morris Kurz --- packages/flutter/test/material/expansion_panel_test.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/flutter/test/material/expansion_panel_test.dart b/packages/flutter/test/material/expansion_panel_test.dart index efd20fef4983b..9458df3c79bbf 100644 --- a/packages/flutter/test/material/expansion_panel_test.dart +++ b/packages/flutter/test/material/expansion_panel_test.dart @@ -1325,6 +1325,7 @@ void main() { final RenderBox boxOfContainer = tester.renderObject(find.ancestor(of: find.byKey(firstPanelKey), matching: find.byType(AnimatedContainer)).first); final RenderBox boxOfIconButton = tester.renderObject(find.byType(IconButton).first); expect(boxOfContainer.size.height, equals(boxOfIconButton.size.height)); + expect(boxOfContainer.size.height, equals(48.0)); // Header should have 48px height according to Material 2 Design spec. }); testWidgets("The AnimatedContainer's height is at least kMinInteractiveDimension", (WidgetTester tester) async { From 29a561eb8b93ffb22242ee6624693d3d7fb592dd Mon Sep 17 00:00:00 2001 From: Morris Kurz Date: Fri, 6 Jan 2023 16:09:28 +0100 Subject: [PATCH 7/8] Change issue number to link in comment --- packages/flutter/test/material/expansion_panel_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter/test/material/expansion_panel_test.dart b/packages/flutter/test/material/expansion_panel_test.dart index 50e751fe7407e..106a2e3673c69 100644 --- a/packages/flutter/test/material/expansion_panel_test.dart +++ b/packages/flutter/test/material/expansion_panel_test.dart @@ -1302,7 +1302,7 @@ void main() { expect(box.size.width, equals(744.0)); }); - // Regression test for #5848 + // Regression test for https://github.com/flutter/flutter/issues/5848 testWidgets('The AnimatedContainer and IconButton have the same height of 48px', (WidgetTester tester) async { const Key firstPanelKey = Key('firstPanelKey'); From 6775f1b1bf914d6291a1a597c081e9503d3bcea9 Mon Sep 17 00:00:00 2001 From: Morris Kurz Date: Fri, 6 Jan 2023 16:14:25 +0100 Subject: [PATCH 8/8] Add periods to comments --- packages/flutter/test/material/expansion_panel_test.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/flutter/test/material/expansion_panel_test.dart b/packages/flutter/test/material/expansion_panel_test.dart index 106a2e3673c69..b80a3c45bce36 100644 --- a/packages/flutter/test/material/expansion_panel_test.dart +++ b/packages/flutter/test/material/expansion_panel_test.dart @@ -1302,7 +1302,7 @@ void main() { expect(box.size.width, equals(744.0)); }); - // Regression test for https://github.com/flutter/flutter/issues/5848 + // Regression test for https://github.com/flutter/flutter/issues/5848. testWidgets('The AnimatedContainer and IconButton have the same height of 48px', (WidgetTester tester) async { const Key firstPanelKey = Key('firstPanelKey'); @@ -1317,11 +1317,11 @@ void main() { ), ); - // The panel is closed + // The panel is closed. expect(find.text('A'), findsOneWidget); expect(find.text('B'), findsNothing); - // No padding applied to closed header + // No padding applied to closed header. final RenderBox boxOfContainer = tester.renderObject(find.ancestor(of: find.byKey(firstPanelKey), matching: find.byType(AnimatedContainer)).first); final RenderBox boxOfIconButton = tester.renderObject(find.byType(IconButton).first); expect(boxOfContainer.size.height, equals(boxOfIconButton.size.height));