Unverified Commit 2f611f14 authored by Per Classon's avatar Per Classon Committed by GitHub

Make header optional in PaginatedDataTable (#69610)

parent 96a78c08
......@@ -37,8 +37,7 @@ import 'theme.dart';
class PaginatedDataTable extends StatefulWidget {
/// Creates a widget describing a paginated [DataTable] on a [Card].
///
/// The [header] should give the card's header, typically a [Text] widget. It
/// must not be null.
/// The [header] should give the card's header, typically a [Text] widget.
///
/// The [columns] argument must be a list of as many [DataColumn] objects as
/// the table is to have columns, ignoring the leading checkbox column if any.
......@@ -64,7 +63,7 @@ class PaginatedDataTable extends StatefulWidget {
/// both have defaults, though, so don't have to be specified).
PaginatedDataTable({
Key? key,
required this.header,
this.header,
this.actions,
required this.columns,
this.sortColumnIndex,
......@@ -82,7 +81,7 @@ class PaginatedDataTable extends StatefulWidget {
this.onRowsPerPageChanged,
this.dragStartBehavior = DragStartBehavior.start,
required this.source,
}) : assert(header != null),
}) : assert(actions == null || (actions != null && header != null)),
assert(columns != null),
assert(dragStartBehavior != null),
assert(columns.isNotEmpty),
......@@ -103,17 +102,19 @@ class PaginatedDataTable extends StatefulWidget {
assert(source != null),
super(key: key);
/// The table card's header.
/// The table card's optional header.
///
/// This is typically a [Text] widget, but can also be a [ButtonBar] with
/// [TextButton]s. Suitable defaults are automatically provided for the font,
/// button color, button padding, and so forth.
/// This is typically a [Text] widget, but can also be a [Row] of
/// [TextButton]s. To show icon buttons at the top end side of the table with
/// a header, set the [actions] property.
///
/// If items in the table are selectable, then, when the selection is not
/// empty, the header is replaced by a count of the selected items.
final Widget header;
/// empty, the header is replaced by a count of the selected items. The
/// [actions] are still visible when items are selected.
final Widget? header;
/// Icon buttons to show at the top right of the table.
/// Icon buttons to show at the top end side of the table. The [header] must
/// not be null to show the actions.
///
/// Typically, the exact actions included in this list will vary based on
/// whether any rows are selected or not.
......@@ -337,8 +338,8 @@ class PaginatedDataTableState extends State<PaginatedDataTable> {
// HEADER
final List<Widget> headerWidgets = <Widget>[];
double startPadding = 24.0;
if (_selectedRowCount == 0) {
headerWidgets.add(Expanded(child: widget.header));
if (_selectedRowCount == 0 && widget.header != null) {
headerWidgets.add(Expanded(child: widget.header!));
if (widget.header is ButtonBar) {
// We adjust the padding when a button bar is present, because the
// ButtonBar introduces 2 pixels of outside padding, plus 2 pixels
......@@ -347,7 +348,7 @@ class PaginatedDataTableState extends State<PaginatedDataTable> {
// inside of the button to line up with the 24.0 left inset.
startPadding = 12.0;
}
} else {
} else if (widget.header != null) {
headerWidgets.add(Expanded(
child: Text(localizations.selectedRowCountTitle(_selectedRowCount)),
));
......@@ -432,32 +433,33 @@ class PaginatedDataTableState extends State<PaginatedDataTable> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Semantics(
container: true,
child: DefaultTextStyle(
// These typographic styles aren't quite the regular ones. We pick the closest ones from the regular
// list and then tweak them appropriately.
// See https://material.io/design/components/data-tables.html#tables-within-cards
style: _selectedRowCount > 0 ? themeData.textTheme.subtitle1!.copyWith(color: themeData.accentColor)
: themeData.textTheme.headline6!.copyWith(fontWeight: FontWeight.w400),
child: IconTheme.merge(
data: const IconThemeData(
opacity: 0.54
),
child: Ink(
height: 64.0,
color: _selectedRowCount > 0 ? themeData.secondaryHeaderColor : null,
child: Padding(
padding: EdgeInsetsDirectional.only(start: startPadding, end: 14.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: headerWidgets,
if (headerWidgets.isNotEmpty)
Semantics(
container: true,
child: DefaultTextStyle(
// These typographic styles aren't quite the regular ones. We pick the closest ones from the regular
// list and then tweak them appropriately.
// See https://material.io/design/components/data-tables.html#tables-within-cards
style: _selectedRowCount > 0 ? themeData.textTheme.subtitle1!.copyWith(color: themeData.accentColor)
: themeData.textTheme.headline6!.copyWith(fontWeight: FontWeight.w400),
child: IconTheme.merge(
data: const IconThemeData(
opacity: 0.54
),
child: Ink(
height: 64.0,
color: _selectedRowCount > 0 ? themeData.secondaryHeaderColor : null,
child: Padding(
padding: EdgeInsetsDirectional.only(start: startPadding, end: 14.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: headerWidgets,
),
),
),
),
),
),
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
dragStartBehavior: widget.dragStartBehavior,
......
......@@ -229,6 +229,43 @@ void main() {
expect(tester.getTopRight(find.text('8')).dx, tester.getTopRight(find.text('Rows per page:')).dx + 40.0); // per spec
});
testWidgets('PaginatedDataTable with and without header and actions', (WidgetTester tester) async {
await binding.setSurfaceSize(const Size(800, 800));
const String headerText = 'HEADER';
final List<Widget> actions = <Widget>[
IconButton(onPressed: () {}, icon: const Icon(Icons.add)),
];
Widget buildTable({String? header, List<Widget>? actions}) => MaterialApp(
home: PaginatedDataTable(
header: header != null ? Text(header) : null,
actions: actions,
source: TestDataSource(onSelectChanged: (bool? value) {}),
showCheckboxColumn: true,
columns: const <DataColumn>[
DataColumn(label: Text('Name')),
DataColumn(label: Text('Calories'), numeric: true),
DataColumn(label: Text('Generation')),
],
),
);
await tester.pumpWidget(buildTable(header: headerText));
expect(find.text(headerText), findsOneWidget);
expect(find.byIcon(Icons.add), findsNothing);
await tester.pumpWidget(buildTable(header: headerText, actions: actions));
expect(find.text(headerText), findsOneWidget);
expect(find.byIcon(Icons.add), findsOneWidget);
await tester.pumpWidget(buildTable());
expect(find.text(headerText), findsNothing);
expect(find.byIcon(Icons.add), findsNothing);
expect(() => buildTable(actions: actions), throwsAssertionError);
await binding.setSurfaceSize(null);
});
testWidgets('PaginatedDataTable with large text', (WidgetTester tester) async {
final TestDataSource source = TestDataSource();
await tester.pumpWidget(MaterialApp(
......
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