Commit 09e8c2ff authored by Hans Muller's avatar Hans Muller Committed by GitHub

Update ExpansionTile, added a sample app (#10019)

parent ff0aa513
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
class Entry {
Entry(this.title, [this.children = const <Entry>[]]);
final String title;
final List<Entry> children;
}
final List<Entry> data = <Entry>[
new Entry('Chapter A',
<Entry>[
new Entry('Section A0',
<Entry>[
new Entry('Item A0.1'),
new Entry('Item A0.2'),
new Entry('Item A0.3'),
],
),
new Entry('Section A1'),
new Entry('Section A2'),
],
),
new Entry('Chapter B',
<Entry>[
new Entry('Section B0'),
new Entry('Section B1'),
],
),
new Entry('Chapter C',
<Entry>[
new Entry('Section C0'),
new Entry('Section C1'),
new Entry('Section C2',
<Entry>[
new Entry('Item C2.0'),
new Entry('Item C2.1'),
new Entry('Item C2.2'),
new Entry('Item C2.3'),
],
),
],
),
];
class EntryItem extends StatelessWidget {
EntryItem(this.entry);
final Entry entry;
Widget _buildTiles(Entry root) {
if (root.children.isEmpty)
return new ListTile(title: new Text(root.title));
return new ExpansionTile(
key: new ValueKey<Entry>(root),
title: new Text(root.title),
children: root.children.map(_buildTiles).toList(),
);
}
@override
Widget build(BuildContext context) {
return _buildTiles(entry);
}
}
class ExpansionTileSample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: const Text('ExpansionTile'),
),
body: new ListView.builder(
itemBuilder: (BuildContext context, int index) => new EntryItem(data[index]),
itemCount: data.length,
),
);
}
}
void main() {
runApp(new MaterialApp(home: new ExpansionTileSample()));
}
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../lib/expansion_tile_sample.dart' as expansion_tile_sample;
import '../lib/expansion_tile_sample.dart' show Entry;
void main() {
testWidgets("expansion_tile sample smoke test", (WidgetTester tester) async {
expansion_tile_sample.main();
await tester.pump();
// Initially only the top level EntryItems (the "chapters") are present.
for (Entry chapter in expansion_tile_sample.data) {
expect(find.text(chapter.title), findsOneWidget);
for (Entry section in chapter.children) {
expect(find.text(section.title), findsNothing);
for (Entry item in section.children)
expect(find.text(item.title), findsNothing);
}
}
Future<Null> scrollUpOneEntry() async {
await tester.dragFrom(const Offset(200.0, 200.0), const Offset(0.0, -88.00));
await tester.pumpAndSettle();
}
Future<Null> tapEntry(String title) async {
await tester.tap(find.text(title));
await tester.pumpAndSettle();
}
// Expand the chapters. Now the chapter and sections, but not the
// items, should be present.
for (Entry chapter in expansion_tile_sample.data.reversed)
await tapEntry(chapter.title);
for (Entry chapter in expansion_tile_sample.data) {
expect(find.text(chapter.title), findsOneWidget);
for (Entry section in chapter.children) {
expect(find.text(section.title), findsOneWidget);
await scrollUpOneEntry();
for (Entry item in section.children)
expect(find.text(item.title), findsNothing);
}
await scrollUpOneEntry();
}
// - scroll to the top -
await tester.flingFrom(const Offset(200.0, 200.0), const Offset(0.0, 100.0), 5000.0);
await tester.pumpAndSettle();
// Expand the sections. Now Widgets for all three levels should be present.
for (Entry chapter in expansion_tile_sample.data) {
for (Entry section in chapter.children) {
await tapEntry(section.title);
await scrollUpOneEntry();
}
await scrollUpOneEntry();
}
// We're scrolled to the bottom so the very last item is visible.
// Working in reverse order, so we don't need to do anymore scrolling,
// check that everything is visible and close the sections and
// chapters as we go up.
for (Entry chapter in expansion_tile_sample.data.reversed) {
expect(find.text(chapter.title), findsOneWidget);
for (Entry section in chapter.children.reversed) {
expect(find.text(section.title), findsOneWidget);
for (Entry item in section.children.reversed)
expect(find.text(item.title), findsOneWidget);
await tapEntry(section.title); // close the section
}
await tapEntry(chapter.title); // close the chapter
}
// Finally only the top level EntryItems (the "chapters") are present.
for (Entry chapter in expansion_tile_sample.data) {
expect(find.text(chapter.title), findsOneWidget);
for (Entry section in chapter.children) {
expect(find.text(section.title), findsNothing);
for (Entry item in section.children)
expect(find.text(item.title), findsNothing);
}
}
});
}
......@@ -20,7 +20,9 @@ const Duration _kExpand = const Duration(milliseconds: 200);
/// the tile to reveal or hide the [children].
///
/// This widget is typically used with [ListView] to create an
/// "expand / collapse" list entry.
/// "expand / collapse" list entry. When used with scrolling widgets like
/// [ListView], a unique [key] must be specified to enable the [ExpansionTile] to
/// save and restore its expanded state when it is scrolled in and out of view.
///
/// See also:
///
......@@ -110,7 +112,11 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
if (_isExpanded)
_controller.forward();
else
_controller.reverse();
_controller.reverse().then((Null value) {
setState(() {
// Rebuild without widget.children.
});
});
PageStorage.of(context)?.writeState(context, _isExpanded);
});
if (widget.onExpansionChanged != null)
......@@ -172,10 +178,12 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
..begin = Colors.transparent
..end = widget.backgroundColor ?? Colors.transparent;
final bool closed = !_isExpanded && _controller.isDismissed;
return new AnimatedBuilder(
animation: _controller.view,
builder: _buildChildren,
child: new Column(children: widget.children),
child: closed ? null : new Column(children: widget.children),
);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment