Added checkboxHorizontalMargin to DataTable and PaginatedDataTable (#71217)

This commit is contained in:
Chinmoy 2021-02-18 04:41:52 +05:30 committed by GitHub
parent 19990c2d42
commit bb43d2c632
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 219 additions and 6 deletions

View file

@ -469,6 +469,7 @@ class DataTable extends StatelessWidget {
this.showBottomBorder = false,
this.dividerThickness,
required this.rows,
this.checkboxHorizontalMargin,
}) : assert(columns != null),
assert(columns.isNotEmpty),
assert(sortColumnIndex == null || (sortColumnIndex >= 0 && sortColumnIndex < columns.length)),
@ -636,6 +637,10 @@ class DataTable extends StatelessWidget {
///
/// If null, [DataTableThemeData.horizontalMargin] is used. This value
/// defaults to 24.0 to adhere to the Material Design specifications.
///
/// If [checkboxHorizontalMargin] is null, then [horizontalMargin] is also the
/// margin between the edge of the table and the checkbox, as well as the
/// margin between the checkbox and the content in the first data column.
final double? horizontalMargin;
/// {@template flutter.material.dataTable.columnSpacing}
@ -679,6 +684,16 @@ class DataTable extends StatelessWidget {
/// around the table defined by [decoration].
final bool showBottomBorder;
/// {@template flutter.material.dataTable.checkboxHorizontalMargin}
/// Horizontal margin around the checkbox, if it is displayed.
/// {@endtemplate}
///
/// If null, [DataTableThemeData.checkboxHorizontalMargin] is used. If that is
/// also null, then [horizontalMargin] is used as the margin between the edge
/// of the table and the checkbox, as well as the margin between the checkbox
/// and the content in the first data column. This value defaults to 24.0.
final double? checkboxHorizontalMargin;
// Set by the constructor to the index of the only Column that is
// non-numeric, if there is exactly one, otherwise null.
final int? _onlyTextColumn;
@ -746,12 +761,18 @@ class DataTable extends StatelessWidget {
final double effectiveHorizontalMargin = horizontalMargin
?? themeData.dataTableTheme.horizontalMargin
?? _horizontalMargin;
final double effectiveCheckboxHorizontalMarginStart = checkboxHorizontalMargin
?? themeData.dataTableTheme.checkboxHorizontalMargin
?? effectiveHorizontalMargin;
final double effectiveCheckboxHorizontalMarginEnd = checkboxHorizontalMargin
?? themeData.dataTableTheme.checkboxHorizontalMargin
?? effectiveHorizontalMargin / 2.0;
Widget contents = Semantics(
container: true,
child: Padding(
padding: EdgeInsetsDirectional.only(
start: effectiveHorizontalMargin,
end: effectiveHorizontalMargin / 2.0,
start: effectiveCheckboxHorizontalMarginStart,
end: effectiveCheckboxHorizontalMarginEnd,
),
child: Center(
child: Checkbox(
@ -933,6 +954,12 @@ class DataTable extends StatelessWidget {
final double effectiveHorizontalMargin = horizontalMargin
?? theme.dataTableTheme.horizontalMargin
?? _horizontalMargin;
final double effectiveCheckboxHorizontalMarginStart = checkboxHorizontalMargin
?? theme.dataTableTheme.checkboxHorizontalMargin
?? effectiveHorizontalMargin;
final double effectiveCheckboxHorizontalMarginEnd = checkboxHorizontalMargin
?? theme.dataTableTheme.checkboxHorizontalMargin
?? effectiveHorizontalMargin / 2.0;
final double effectiveColumnSpacing = columnSpacing
?? theme.dataTableTheme.columnSpacing
?? _columnSpacing;
@ -976,7 +1003,7 @@ class DataTable extends StatelessWidget {
int displayColumnIndex = 0;
if (displayCheckboxColumn) {
tableColumns[0] = FixedColumnWidth(effectiveHorizontalMargin + Checkbox.width + effectiveHorizontalMargin / 2.0);
tableColumns[0] = FixedColumnWidth(effectiveCheckboxHorizontalMarginStart + Checkbox.width + effectiveCheckboxHorizontalMarginEnd);
tableRows[0].children![0] = _buildCheckbox(
context: context,
checked: someChecked ? null : allChecked,
@ -1004,7 +1031,9 @@ class DataTable extends StatelessWidget {
final DataColumn column = columns[dataColumnIndex];
final double paddingStart;
if (dataColumnIndex == 0 && displayCheckboxColumn) {
if (dataColumnIndex == 0 && displayCheckboxColumn && checkboxHorizontalMargin != null) {
paddingStart = effectiveHorizontalMargin;
} else if (dataColumnIndex == 0 && displayCheckboxColumn) {
paddingStart = effectiveHorizontalMargin / 2.0;
} else if (dataColumnIndex == 0 && !displayCheckboxColumn) {
paddingStart = effectiveHorizontalMargin;

View file

@ -45,6 +45,7 @@ class DataTableThemeData with Diagnosticable {
this.horizontalMargin,
this.columnSpacing,
this.dividerThickness,
this.checkboxHorizontalMargin,
});
/// {@macro flutter.material.dataTable.decoration}
@ -79,6 +80,9 @@ class DataTableThemeData with Diagnosticable {
/// {@macro flutter.material.dataTable.dividerThickness}
final double? dividerThickness;
/// {@macro flutter.material.dataTable.checkboxHorizontalMargin}
final double? checkboxHorizontalMargin;
/// Creates a copy of this object but with the given fields replaced with the
/// new values.
DataTableThemeData copyWith({
@ -92,6 +96,7 @@ class DataTableThemeData with Diagnosticable {
double? horizontalMargin,
double? columnSpacing,
double? dividerThickness,
double? checkboxHorizontalMargin,
}) {
return DataTableThemeData(
decoration: decoration ?? this.decoration,
@ -104,6 +109,7 @@ class DataTableThemeData with Diagnosticable {
horizontalMargin: horizontalMargin ?? this.horizontalMargin,
columnSpacing: columnSpacing ?? this.columnSpacing,
dividerThickness: dividerThickness ?? this.dividerThickness,
checkboxHorizontalMargin: checkboxHorizontalMargin ?? this.checkboxHorizontalMargin,
);
}
@ -124,7 +130,8 @@ class DataTableThemeData with Diagnosticable {
headingTextStyle: TextStyle.lerp(a.headingTextStyle, b.headingTextStyle, t),
horizontalMargin: lerpDouble(a.horizontalMargin, b.horizontalMargin, t),
columnSpacing: lerpDouble(a.columnSpacing, b.columnSpacing, t),
dividerThickness: lerpDouble(a.dividerThickness, b.dividerThickness, t)
dividerThickness: lerpDouble(a.dividerThickness, b.dividerThickness, t),
checkboxHorizontalMargin: lerpDouble(a.checkboxHorizontalMargin, b.checkboxHorizontalMargin, t)
);
}
@ -141,6 +148,7 @@ class DataTableThemeData with Diagnosticable {
horizontalMargin,
columnSpacing,
dividerThickness,
checkboxHorizontalMargin,
);
}
@ -160,7 +168,8 @@ class DataTableThemeData with Diagnosticable {
&& other.headingTextStyle == headingTextStyle
&& other.horizontalMargin == horizontalMargin
&& other.columnSpacing == columnSpacing
&& other.dividerThickness == dividerThickness;
&& other.dividerThickness == dividerThickness
&& other.checkboxHorizontalMargin == checkboxHorizontalMargin;
}
@override
@ -176,6 +185,7 @@ class DataTableThemeData with Diagnosticable {
properties.add(DoubleProperty('horizontalMargin', horizontalMargin, defaultValue: null));
properties.add(DoubleProperty('columnSpacing', columnSpacing, defaultValue: null));
properties.add(DoubleProperty('dividerThickness', dividerThickness, defaultValue: null));
properties.add(DoubleProperty('checkboxHorizontalMargin', checkboxHorizontalMargin, defaultValue: null));
}
static MaterialStateProperty<T>? _lerpProperties<T>(MaterialStateProperty<T>? a, MaterialStateProperty<T>? b, double t, T Function(T?, T?, double) lerpFunction ) {

View file

@ -86,6 +86,7 @@ class PaginatedDataTable extends StatefulWidget {
this.onRowsPerPageChanged,
this.dragStartBehavior = DragStartBehavior.start,
required this.source,
this.checkboxHorizontalMargin,
}) : assert(actions == null || (actions != null && header != null)),
assert(columns != null),
assert(dragStartBehavior != null),
@ -166,6 +167,10 @@ class PaginatedDataTable extends StatefulWidget {
/// the content in the first data column.
///
/// This value defaults to 24.0 to adhere to the Material Design specifications.
///
/// If [checkboxHorizontalMargin] is null, then [horizontalMargin] is also the
/// margin between the edge of the table and the checkbox, as well as the
/// margin between the checkbox and the content in the first data column.
final double horizontalMargin;
/// The horizontal margin between the contents of each data column.
@ -224,6 +229,13 @@ class PaginatedDataTable extends StatefulWidget {
/// {@macro flutter.widgets.scrollable.dragStartBehavior}
final DragStartBehavior dragStartBehavior;
/// Horizontal margin around the checkbox, if it is displayed.
///
/// If null, then [horizontalMargin] is used as the margin between the edge
/// of the table and the checkbox, as well as the margin between the checkbox
/// and the content in the first data column. This value defaults to 24.0.
final double? checkboxHorizontalMargin;
@override
PaginatedDataTableState createState() => PaginatedDataTableState();
}
@ -513,6 +525,7 @@ class PaginatedDataTableState extends State<PaginatedDataTable> {
dataRowHeight: widget.dataRowHeight,
headingRowHeight: widget.headingRowHeight,
horizontalMargin: widget.horizontalMargin,
checkboxHorizontalMargin: widget.checkboxHorizontalMargin,
columnSpacing: widget.columnSpacing,
showCheckboxColumn: widget.showCheckboxColumn,
showBottomBorder: true,

View file

@ -1540,4 +1540,93 @@ void main() {
const Offset(width - borderVertical, height - borderHorizontal),
);
});
testWidgets('checkboxHorizontalMargin properly applied', (WidgetTester tester) async {
const double _customCheckboxHorizontalMargin = 15.0;
const double _customHorizontalMargin = 10.0;
Finder cellContent;
Finder checkbox;
Finder padding;
Widget buildCustomTable({
int? sortColumnIndex,
bool sortAscending = true,
double? horizontalMargin,
double? checkboxHorizontalMargin,
}) {
return DataTable(
sortColumnIndex: sortColumnIndex,
sortAscending: sortAscending,
onSelectAll: (bool? value) {},
horizontalMargin: horizontalMargin,
checkboxHorizontalMargin: checkboxHorizontalMargin,
columns: <DataColumn>[
const DataColumn(
label: Text('Name'),
tooltip: 'Name',
),
DataColumn(
label: const Text('Calories'),
tooltip: 'Calories',
numeric: true,
onSort: (int columnIndex, bool ascending) {},
),
DataColumn(
label: const Text('Fat'),
tooltip: 'Fat',
numeric: true,
onSort: (int columnIndex, bool ascending) {},
),
],
rows: kDesserts.map<DataRow>((Dessert dessert) {
return DataRow(
key: ValueKey<String>(dessert.name),
onSelectChanged: (bool? selected) {},
cells: <DataCell>[
DataCell(
Text(dessert.name),
),
DataCell(
Text('${dessert.calories}'),
showEditIcon: true,
onTap: () {},
),
DataCell(
Text('${dessert.fat}'),
showEditIcon: true,
onTap: () {},
),
],
);
}).toList(),
);
}
await tester.pumpWidget(MaterialApp(
home: Material(child: buildCustomTable(
checkboxHorizontalMargin: _customCheckboxHorizontalMargin,
horizontalMargin: _customHorizontalMargin,
)),
));
// Custom checkbox padding.
checkbox = find.byType(Checkbox).first;
padding = find.ancestor(of: checkbox, matching: find.byType(Padding));
expect(
tester.getRect(checkbox).left - tester.getRect(padding).left,
_customCheckboxHorizontalMargin,
);
expect(
tester.getRect(padding).right - tester.getRect(checkbox).right,
_customCheckboxHorizontalMargin,
);
// First column padding.
padding = find.widgetWithText(Padding, 'Frozen yogurt').first;
cellContent = find.widgetWithText(Align, 'Frozen yogurt'); // DataTable wraps its DataCells in an Align widget.
expect(
tester.getRect(cellContent).left - tester.getRect(padding).left,
_customHorizontalMargin,
);
});
}

View file

@ -24,6 +24,7 @@ void main() {
expect(themeData.horizontalMargin, null);
expect(themeData.columnSpacing, null);
expect(themeData.dividerThickness, null);
expect(themeData.checkboxHorizontalMargin, null);
const DataTableTheme theme = DataTableTheme(data: DataTableThemeData(), child: SizedBox());
expect(theme.data.decoration, null);
@ -36,6 +37,7 @@ void main() {
expect(theme.data.horizontalMargin, null);
expect(theme.data.columnSpacing, null);
expect(theme.data.dividerThickness, null);
expect(theme.data.checkboxHorizontalMargin, null);
});
testWidgets('Default DataTableThemeData debugFillProperties', (WidgetTester tester) async {
@ -67,6 +69,7 @@ void main() {
horizontalMargin: 3.0,
columnSpacing: 4.0,
dividerThickness: 5.0,
checkboxHorizontalMargin: 6.0,
).debugFillProperties(builder);
final List<String> description = builder.properties
@ -84,6 +87,7 @@ void main() {
expect(description[7], 'horizontalMargin: 3.0');
expect(description[8], 'columnSpacing: 4.0');
expect(description[9], 'dividerThickness: 5.0');
expect(description[10], 'checkboxHorizontalMargin: 6.0');
});
testWidgets('DataTable is themeable', (WidgetTester tester) async {

View file

@ -855,4 +855,72 @@ void main() {
// Reset the surface size.
await binding.setSurfaceSize(originalSize);
});
testWidgets('PaginatedDataTable custom checkboxHorizontalMargin properly applied', (WidgetTester tester) async {
const double _customCheckboxHorizontalMargin = 15.0;
const double _customHorizontalMargin = 10.0;
const double _width = 400;
const double _height = 400;
final Size originalSize = binding.renderView.size;
// Ensure the containing Card is small enough that we don't expand too
// much, resulting in our custom margin being ignored.
await binding.setSurfaceSize(const Size(_width, _height));
final TestDataSource source = TestDataSource(
onSelectChanged: (bool? value) {},
);
Finder cellContent;
Finder checkbox;
Finder padding;
// CUSTOM VALUES
await tester.pumpWidget(MaterialApp(
home: Material(
child: PaginatedDataTable(
header: const Text('Test table'),
source: source,
rowsPerPage: 2,
availableRowsPerPage: const <int>[
2, 4,
],
onRowsPerPageChanged: (int? rowsPerPage) {},
onPageChanged: (int rowIndex) {},
onSelectAll: (bool? value) {},
columns: const <DataColumn>[
DataColumn(label: Text('Name')),
DataColumn(label: Text('Calories'), numeric: true),
DataColumn(label: Text('Generation')),
],
horizontalMargin: _customHorizontalMargin,
checkboxHorizontalMargin: _customCheckboxHorizontalMargin,
),
),
));
// Custom checkbox padding.
checkbox = find.byType(Checkbox).first;
padding = find.ancestor(of: checkbox, matching: find.byType(Padding)).first;
expect(
tester.getRect(checkbox).left - tester.getRect(padding).left,
_customCheckboxHorizontalMargin,
);
expect(
tester.getRect(padding).right - tester.getRect(checkbox).right,
_customCheckboxHorizontalMargin,
);
// Custom first column padding.
padding = find.widgetWithText(Padding, 'Frozen yogurt (0)').first;
cellContent = find.widgetWithText(Align, 'Frozen yogurt (0)'); // DataTable wraps its DataCells in an Align widget.
expect(
tester.getRect(cellContent).left - tester.getRect(padding).left,
_customHorizontalMargin,
);
// Reset the surface size.
await binding.setSurfaceSize(originalSize);
});
}