Skip to content
Closed
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
18 changes: 18 additions & 0 deletions packages/material_ui/lib/src/expansion_tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ class ExpansionTile extends StatefulWidget {
this.expansionAnimationStyle,
this.internalAddSemanticForOnTap = false,
this.statesController,
this.lazyLoadChildren = false,
}) : assert(
expandedCrossAxisAlignment != CrossAxisAlignment.baseline,
'CrossAxisAlignment.baseline is not supported since the expanded children '
Expand Down Expand Up @@ -256,6 +257,13 @@ class ExpansionTile extends StatefulWidget {
/// collapsed and recreated upon expansion.
final bool maintainState;

/// If [maintainState] is true, whether to delay building the children until
/// the tile is expanded for the first time.
///
/// Defaults to false, meaning the children are built at the same time as the
/// header.
final bool lazyLoadChildren;

/// Specifies padding for the [ListTile].
///
/// Analogous to [ListTile.contentPadding], this property defines the insets for
Expand Down Expand Up @@ -521,6 +529,15 @@ class ExpansionTile extends StatefulWidget {

@override
State<ExpansionTile> createState() => _ExpansionTileState();

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(FlagProperty('maintainState', value: maintainState, ifTrue: 'maintainState'));
properties.add(
FlagProperty('lazyLoadChildren', value: lazyLoadChildren, ifTrue: 'lazyLoadChildren'),
);
}
}

class _ExpansionTileState extends State<ExpansionTile> {
Expand Down Expand Up @@ -893,6 +910,7 @@ class _ExpansionTileState extends State<ExpansionTile> {
duration: _duration,
reverseCurve: _reverseCurve,
maintainState: widget.maintainState,
lazyLoadChildren: widget.lazyLoadChildren,
headerBuilder: _buildHeader,
bodyBuilder: _buildBody,
expansibleBuilder: _buildExpansible,
Expand Down
86 changes: 86 additions & 0 deletions packages/material_ui/test/expansion_tile_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,92 @@ void main() {
expect(find.text('Discarding State'), findsNothing);
});

testWidgets('ExpansionTile lazyLoadChildren defers child build until first expansion', (
WidgetTester tester,
) async {
await tester.pumpWidget(
const MaterialApp(
home: Material(
child: SingleChildScrollView(
child: ExpansionTile(
title: Text('Title'),
lazyLoadChildren: true,
maintainState: true,
children: <Widget>[Text('Lazy Child')],
),
),
),
),
);

// Child is not in the tree at all before first expansion, even with
// maintainState: true.
expect(find.text('Lazy Child', skipOffstage: false), findsNothing);

await tester.tap(find.text('Title'));
await tester.pumpAndSettle();
expect(find.text('Lazy Child'), findsOneWidget);

// After collapsing, the child remains in the tree (offstage) because
// maintainState is true and the tile has been expanded once.
await tester.tap(find.text('Title'));
await tester.pumpAndSettle();
expect(find.text('Lazy Child'), findsNothing);
expect(find.text('Lazy Child', skipOffstage: false), findsOneWidget);
});

testWidgets('ExpansionTile lazyLoadChildren builds children when initiallyExpanded is true', (
WidgetTester tester,
) async {
await tester.pumpWidget(
const MaterialApp(
home: Material(
child: SingleChildScrollView(
child: ExpansionTile(
title: Text('Title'),
lazyLoadChildren: true,
initiallyExpanded: true,
children: <Widget>[Text('Lazy Child')],
),
),
),
),
);

// Initially expanded, so the children are built right away.
expect(find.text('Lazy Child'), findsOneWidget);
});

testWidgets(
'ExpansionTile lazyLoadChildren with maintainState false still removes children when collapsed',
(WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
home: Material(
child: SingleChildScrollView(
child: ExpansionTile(
title: Text('Title'),
lazyLoadChildren: true,
maintainState: false,
children: <Widget>[Text('Lazy Child')],
),
),
),
),
);

expect(find.text('Lazy Child', skipOffstage: false), findsNothing);

await tester.tap(find.text('Title'));
await tester.pumpAndSettle();
expect(find.text('Lazy Child'), findsOneWidget);

await tester.tap(find.text('Title'));
await tester.pumpAndSettle();
expect(find.text('Lazy Child', skipOffstage: false), findsNothing);
},
);

testWidgets('ExpansionTile padding test', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
Expand Down