Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// #region body
import 'package:material_ui/material_ui.dart';

/// Flutter code sample for a flat [ExpansionPanelList] with divider separators.

void main() => runApp(const FlatExpansionPanelListExampleApp());

class FlatExpansionPanelListExampleApp extends StatelessWidget {
const FlatExpansionPanelListExampleApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Flat ExpansionPanelList Sample')),
body: const Center(child: FlatExpansionPanelListExample()),
),
);
}
}

class FlatExpansionPanelListExample extends StatefulWidget {
const FlatExpansionPanelListExample({super.key});

@override
State<FlatExpansionPanelListExample> createState() =>
_FlatExpansionPanelListExampleState();
}

class _FlatExpansionPanelListExampleState
extends State<FlatExpansionPanelListExample> {
final List<bool> _isExpanded = <bool>[false, false, false];

@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Padding(
padding: const .all(16.0),
Comment thread
benji-farquhar marked this conversation as resolved.
child: Column(
children: <Widget>[
const Padding(
padding: .only(bottom: 16.0),
Comment thread
benji-farquhar marked this conversation as resolved.
child: Text(
'With materialGapSize set to 0, dividers between expanded panels are '
'preserved, creating a flat list appearance.',
textAlign: .center,
),
),
Card(
child: ExpansionPanelList(
materialGapSize: 0,
expandedHeaderPadding: .zero,
Comment thread
benji-farquhar marked this conversation as resolved.
expansionCallback: (int index, bool isExpanded) {
setState(() {
_isExpanded[index] = isExpanded;
});
},
children: <ExpansionPanel>[
ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
return const ListTile(title: Text('Panel A'));
},
body: const ListTile(title: Text('Content for Panel A')),
isExpanded: _isExpanded[0],
canTapOnHeader: true,
),
ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
return const ListTile(title: Text('Panel B'));
},
body: const ListTile(title: Text('Content for Panel B')),
isExpanded: _isExpanded[1],
canTapOnHeader: true,
),
ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
return const ListTile(title: Text('Panel C'));
},
body: const ListTile(title: Text('Content for Panel C')),
isExpanded: _isExpanded[2],
canTapOnHeader: true,
),
],
),
),
],
),
),
);
}
}
// #endregion body
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter_test/flutter_test.dart';
import 'package:material_ui/material_ui.dart';
import 'package:material_ui_examples/expansion_panel/expansion_panel_list.2.dart'
as example;

void main() {
testWidgets(
'Flat ExpansionPanelList preserves dividers with no MaterialGap',
(WidgetTester tester) async {
await tester.pumpWidget(const example.FlatExpansionPanelListExampleApp());

await tester.tap(find.text('Panel A'));
await tester.pumpAndSettle();

final MergeableMaterial mergeableMaterial = tester.widget(
find.byType(MergeableMaterial),
);
expect(mergeableMaterial.children.whereType<MaterialGap>().length, 0);
expect(mergeableMaterial.hasDividers, true);
},
);
}
23 changes: 21 additions & 2 deletions packages/material_ui/lib/src/expansion_panel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,19 @@ class ExpansionPanelList extends StatefulWidget {
/// Defines the [MaterialGap.size] of the [MaterialGap] which is placed
/// between the [ExpansionPanelList.children] when they're expanded.
///
/// When set to zero, no [MaterialGap] is inserted between expanded panels
/// and dividers are preserved, allowing a flat, divider-separated appearance.
///
/// <callout-box>
///
/// Here is an example with [materialGapSize] set to zero.
///
/// {@macro material_ui.dartpad_guide}
///
/// {@example /example/lib/expansion_panel/expansion_panel_list.2.dart#body}
///
/// </callout-box>
///
/// Defaults to `16.0`.
final double materialGapSize;

Expand Down Expand Up @@ -392,9 +405,13 @@ class _ExpansionPanelListState extends State<ExpansionPanelList> {
);

final items = <MergeableMaterialItem>[];
final bool shouldInsertMaterialGap = widget.materialGapSize > 0;

for (var index = 0; index < widget.children.length; index += 1) {
if (_isChildExpanded(index) && index != 0 && !_isChildExpanded(index - 1)) {
if (_isChildExpanded(index) &&
index != 0 &&
!_isChildExpanded(index - 1) &&
shouldInsertMaterialGap) {
items.add(
MaterialGap(
key: _SaltedKey<BuildContext, int>(context, index * 2 - 1),
Expand Down Expand Up @@ -483,7 +500,9 @@ class _ExpansionPanelListState extends State<ExpansionPanelList> {
),
);

if (_isChildExpanded(index) && index != widget.children.length - 1) {
if (_isChildExpanded(index) &&
index != widget.children.length - 1 &&
shouldInsertMaterialGap) {
items.add(
MaterialGap(
key: _SaltedKey<BuildContext, int>(context, index * 2 + 1),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
changelog: |
- Keeps `ExpansionPanelList` dividers when expanded if `materialGapSize` is 0.
version: minor

59 changes: 52 additions & 7 deletions packages/material_ui/test/expansion_panel_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1938,14 +1938,9 @@ void main() {
await tester.pumpWidget(buildWidgetForTest(materialGapSize: 0));
await tester.pumpAndSettle();
final MergeableMaterial mergeableMaterial = tester.widget(find.byType(MergeableMaterial));
expect(mergeableMaterial.children.length, 3);
expect(mergeableMaterial.children.whereType<MaterialGap>().length, 1);
expect(mergeableMaterial.children.length, 2);
expect(mergeableMaterial.children.whereType<MaterialGap>().length, 0);
expect(mergeableMaterial.children.whereType<MaterialSlice>().length, 2);
for (final MergeableMaterialItem e in mergeableMaterial.children) {
if (e is MaterialGap) {
expect(e.size, 0);
}
}

await tester.pumpWidget(buildWidgetForTest(materialGapSize: 20));
await tester.pumpAndSettle();
Expand All @@ -1972,6 +1967,56 @@ void main() {
}
});

Future<void> pumpExpansionPanelListAndVerifyGaps(
WidgetTester tester, {
required double materialGapSize,
required int expectedGapCount,
}) async {
await tester.pumpWidget(
MaterialApp(
home: SingleChildScrollView(
child: ExpansionPanelList(
materialGapSize: materialGapSize,
children: <ExpansionPanel>[
ExpansionPanel(
isExpanded: true,
canTapOnHeader: true,
body: const SizedBox.shrink(),
headerBuilder: (BuildContext context, bool isExpanded) {
return const SizedBox.shrink();
},
),
ExpansionPanel(
canTapOnHeader: true,
body: const SizedBox.shrink(),
headerBuilder: (BuildContext context, bool isExpanded) {
return const SizedBox.shrink();
},
),
],
),
),
),
);
await tester.pumpAndSettle();

final MergeableMaterial mergeableMaterial = tester.widget(find.byType(MergeableMaterial));
expect(mergeableMaterial.children.whereType<MaterialGap>().length, expectedGapCount);
expect(mergeableMaterial.children.whereType<MaterialSlice>().length, 2);
}

testWidgets('ExpansionPanelList does not insert MaterialGap when materialGapSize is 0', (
WidgetTester tester,
) async {
await pumpExpansionPanelListAndVerifyGaps(tester, materialGapSize: 0, expectedGapCount: 0);
});

testWidgets('ExpansionPanelList inserts MaterialGap when materialGapSize is greater than 0', (
WidgetTester tester,
) async {
await pumpExpansionPanelListAndVerifyGaps(tester, materialGapSize: 16, expectedGapCount: 1);
});

testWidgets(
'Ensure IconButton splashColor and highlightColor are correctly set when canTapOnHeader is false',
(WidgetTester tester) async {
Expand Down