Is it possible to only color a part of a row in a DataTable like 55% of the space?
I am open to alternatives to the DataTable.
I tried it with DataRow color but that colors the complete row.
I found a solution by making a custom DataTable class with two new parameters and some other code changes. Please let me know if someone finds a better solution.
Copy the code from original DataTable and add/replace the following code parts
Here are the relevant code parts:
class CustomDataTable extends StatelessWidget {
CustomDataTable({
Key? key,
required this.columns,
this.sortColumnIndex,
this.sortAscending = true,
this.onSelectAll,
this.decoration,
this.dataRowColor,
this.dataRowHeight,
this.dataTextStyle,
this.headingRowColor,
this.headingRowHeight,
this.headingTextStyle,
this.horizontalMargin,
this.columnSpacing,
this.showCheckboxColumn = true,
this.showBottomBorder = false,
this.dividerThickness,
required this.rows,
this.checkboxHorizontalMargin,
this.border,
this.dataRowPartIndicatorList,
this.dataRowPartColorGradientList,
}) : assert(columns.isNotEmpty),
assert(sortColumnIndex == null || (sortColumnIndex >= 0 && sortColumnIndex < columns.length)),
assert(!rows.any((DataRow row) => row.cells.length != columns.length)),
assert(dividerThickness == null || dividerThickness >= 0),
_onlyTextColumn = _initOnlyTextColumn(columns),
super(key: key);
/// List of rows. Indicates how much of the row should be colored.
/// Values from 1 to 100.
final List<int?>? dataRowPartIndicatorList;
/// List of rows. The color is used to color part of the row.
final List<Color?>? dataRowPartColorGradientList;
@override
Widget build(BuildContext context) {
assert(!_debugInteractive || debugCheckHasMaterial(context));
final ThemeData theme = Theme.of(context);
final DataTableThemeData dataTableTheme = DataTableTheme.of(context);
final MaterialStateProperty<Color?>? effectiveHeadingRowColor = headingRowColor
?? dataTableTheme.headingRowColor
?? theme.dataTableTheme.headingRowColor;
final MaterialStateProperty<Color?>? effectiveDataRowColor = dataRowColor
?? dataTableTheme.dataRowColor
?? theme.dataTableTheme.dataRowColor;
final MaterialStateProperty<Color?> defaultRowColor = MaterialStateProperty.resolveWith(
(Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
return theme.colorScheme.primary.withOpacity(0.08);
}
return null;
},
);
final bool anyRowSelectable = rows.any((DataRow row) => row.onSelectChanged != null);
final bool displayCheckboxColumn = showCheckboxColumn && anyRowSelectable;
final Iterable<DataRow> rowsWithCheckbox = displayCheckboxColumn ?
rows.where((DataRow row) => row.onSelectChanged != null) : <DataRow>[];
final Iterable<DataRow> rowsChecked = rowsWithCheckbox.where((DataRow row) => row.selected);
final bool allChecked = displayCheckboxColumn && rowsChecked.length == rowsWithCheckbox.length;
final bool anyChecked = displayCheckboxColumn && rowsChecked.isNotEmpty;
final bool someChecked = anyChecked && !allChecked;
final double effectiveHorizontalMargin = horizontalMargin
?? dataTableTheme.horizontalMargin
?? theme.dataTableTheme.horizontalMargin
?? _horizontalMargin;
final double effectiveCheckboxHorizontalMarginStart = checkboxHorizontalMargin
?? dataTableTheme.checkboxHorizontalMargin
?? theme.dataTableTheme.checkboxHorizontalMargin
?? effectiveHorizontalMargin;
final double effectiveCheckboxHorizontalMarginEnd = checkboxHorizontalMargin
?? dataTableTheme.checkboxHorizontalMargin
?? theme.dataTableTheme.checkboxHorizontalMargin
?? effectiveHorizontalMargin / 2.0;
final double effectiveColumnSpacing = columnSpacing
?? dataTableTheme.columnSpacing
?? theme.dataTableTheme.columnSpacing
?? _columnSpacing;
final List<TableColumnWidth> tableColumns = List<TableColumnWidth>.filled(columns.length + (displayCheckboxColumn ? 1 : 0), const _NullTableColumnWidth());
final List<TableRow> tableRows = List<TableRow>.generate(
rows.length + 1, // the +1 is for the header row
(int index) {
final bool isSelected = index > 0 && rows[index - 1].selected;
final bool isDisabled = index > 0 && anyRowSelectable && rows[index - 1].onSelectChanged == null;
final Set<MaterialState> states = <MaterialState>{
if (isSelected)
MaterialState.selected,
if (isDisabled)
MaterialState.disabled,
};
final Color? resolvedDataRowColor = index > 0 ? (rows[index - 1].color ?? effectiveDataRowColor)?.resolve(states) : null;
final Color? resolvedHeadingRowColor = effectiveHeadingRowColor?.resolve(<MaterialState>{});
final Color? rowColor = index > 0 ? resolvedDataRowColor : resolvedHeadingRowColor;
final BorderSide borderSide = Divider.createBorderSide(
context,
width: dividerThickness
?? dataTableTheme.dividerThickness
?? theme.dataTableTheme.dividerThickness
?? _dividerThickness,
);
final Border? border = showBottomBorder
? Border(bottom: borderSide)
: index == 0 ? null : Border(top: borderSide);
return TableRow(
key: index == 0 ? _headingRowKey : rows[index - 1].key,
decoration: BoxDecoration(
border: border,
color: rowColor ?? defaultRowColor.resolve(states),
gradient: index != 0 && dataRowPartIndicatorList != null && dataRowPartColorGradientList != null ? LinearGradient(
colors: List.generate(100, (indexList) => indexList < dataRowPartIndicatorList![index - 1]! ? dataRowPartColorGradientList![index - 1]!.withOpacity(0.4) : Colors.transparent),
begin: Alignment.centerLeft,
stops: List.generate(100, (indexList) => indexList.toDouble() * 0.01),
) : null,
),
children: List<Widget>.filled(tableColumns.length, const _NullWidget()),
);
},
);
int rowIndex;
int displayColumnIndex = 0;
if (displayCheckboxColumn) {
tableColumns[0] = FixedColumnWidth(effectiveCheckboxHorizontalMarginStart + Checkbox.width + effectiveCheckboxHorizontalMarginEnd);
tableRows[0].children![0] = _buildCheckbox(
context: context,
checked: someChecked ? null : allChecked,
onRowTap: null,
onCheckboxChanged: (bool? checked) => _handleSelectAll(checked, someChecked),
overlayColor: null,
tristate: true,
);
rowIndex = 1;
for (final DataRow row in rows) {
tableRows[rowIndex].children![0] = _buildCheckbox(
context: context,
checked: row.selected,
onRowTap: row.onSelectChanged == null ? null : () => row.onSelectChanged?.call(!row.selected),
onCheckboxChanged: row.onSelectChanged,
overlayColor: row.color ?? effectiveDataRowColor,
tristate: false,
);
rowIndex += 1;
}
displayColumnIndex += 1;
}
for (int dataColumnIndex = 0; dataColumnIndex < columns.length; dataColumnIndex += 1) {
final DataColumn column = columns[dataColumnIndex];
final double paddingStart;
if (dataColumnIndex == 0 && displayCheckboxColumn && checkboxHorizontalMargin != null) {
paddingStart = effectiveHorizontalMargin;
} else if (dataColumnIndex == 0 && displayCheckboxColumn) {
paddingStart = effectiveHorizontalMargin / 2.0;
} else if (dataColumnIndex == 0 && !displayCheckboxColumn) {
paddingStart = effectiveHorizontalMargin;
} else {
paddingStart = effectiveColumnSpacing / 2.0;
}
final double paddingEnd;
if (dataColumnIndex == columns.length - 1) {
paddingEnd = effectiveHorizontalMargin;
} else {
paddingEnd = effectiveColumnSpacing / 2.0;
}
final EdgeInsetsDirectional padding = EdgeInsetsDirectional.only(
start: paddingStart,
end: paddingEnd,
);
if (dataColumnIndex == _onlyTextColumn) {
tableColumns[displayColumnIndex] = const IntrinsicColumnWidth(flex: 1.0);
} else {
tableColumns[displayColumnIndex] = const IntrinsicColumnWidth();
}
tableRows[0].children![displayColumnIndex] = _buildHeadingCell(
context: context,
padding: padding,
label: column.label,
tooltip: column.tooltip,
numeric: column.numeric,
onSort: column.onSort != null ? () => column.onSort!(dataColumnIndex, sortColumnIndex != dataColumnIndex || !sortAscending) : null,
sorted: dataColumnIndex == sortColumnIndex,
ascending: sortAscending,
overlayColor: effectiveHeadingRowColor,
);
rowIndex = 1;
for (final DataRow row in rows) {
final DataCell cell = row.cells[dataColumnIndex];
tableRows[rowIndex].children![displayColumnIndex] = _buildDataCell(
context: context,
padding: padding,
label: cell.child,
numeric: column.numeric,
placeholder: cell.placeholder,
showEditIcon: cell.showEditIcon,
onTap: cell.onTap,
onDoubleTap: cell.onDoubleTap,
onLongPress: cell.onLongPress,
onTapCancel: cell.onTapCancel,
onTapDown: cell.onTapDown,
onSelectChanged: row.onSelectChanged == null ? null : () => row.onSelectChanged?.call(!row.selected),
overlayColor: row.color ?? effectiveDataRowColor,
onRowLongPress: row.onLongPress,
);
rowIndex += 1;
}
displayColumnIndex += 1;
}
return Container(
decoration: decoration ?? dataTableTheme.decoration ?? theme.dataTableTheme.decoration,
child: Material(
type: MaterialType.transparency,
child: Table(
columnWidths: tableColumns.asMap(),
children: tableRows,
border: border,
),
),
);
}
}