Skip to content

Commit 2af6954

Browse files
authored
[two_dimensional_scrollables] Add TableSpanPadding (flutter#5039)
--- Fixes flutter#134453 Also related: flutter#134655 This adds padding to TableSpans with TableSpanPadding. This affords folks a leading and trailing offset to pad rows and columns by.
1 parent d7dc0a0 commit 2af6954

File tree

6 files changed

+501
-39
lines changed

6 files changed

+501
-39
lines changed

packages/two_dimensional_scrollables/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.0.4
2+
3+
* Adds TableSpanPadding, TableSpan.padding, and TableSpanDecoration.consumeSpanPadding.
4+
15
## 0.0.3
26

37
* Fixes paint issue when axes are reversed and TableView has pinned rows and columns.

packages/two_dimensional_scrollables/lib/src/table_view/table.dart

Lines changed: 83 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,13 @@ class RenderTableViewport extends RenderTwoDimensionalViewport {
308308
);
309309
}
310310

311+
// TODO(Piinks): Pinned rows/cols do not account for what is visible on the
312+
// screen. Ostensibly, we would not want to have pinned rows/columns that
313+
// extend beyond the viewport, we would never see them as they would never
314+
// scroll into view. So this currently implementation is fairly assuming
315+
// we will never have rows/cols that are outside of the viewport. We should
316+
// maybe add an assertion for this during layout.
317+
// https://github.com/flutter/flutter/issues/136833
311318
int? get _lastPinnedRow =>
312319
delegate.pinnedRowCount > 0 ? delegate.pinnedRowCount - 1 : null;
313320
int? get _lastPinnedColumn =>
@@ -667,12 +674,17 @@ class RenderTableViewport extends RenderTwoDimensionalViewport {
667674
}) {
668675
// TODO(Piinks): Assert here or somewhere else merged cells cannot span
669676
// pinned and unpinned cells (for merged cell follow-up), https://github.com/flutter/flutter/issues/131224
677+
_Span colSpan, rowSpan;
670678
double yPaintOffset = -offset.dy;
671679
for (int row = start.row; row <= end.row; row += 1) {
672680
double xPaintOffset = -offset.dx;
673-
final double rowHeight = _rowMetrics[row]!.extent;
681+
rowSpan = _rowMetrics[row]!;
682+
final double rowHeight = rowSpan.extent;
683+
yPaintOffset += rowSpan.configuration.padding.leading;
674684
for (int column = start.column; column <= end.column; column += 1) {
675-
final double columnWidth = _columnMetrics[column]!.extent;
685+
colSpan = _columnMetrics[column]!;
686+
final double columnWidth = colSpan.extent;
687+
xPaintOffset += colSpan.configuration.padding.leading;
676688

677689
final TableVicinity vicinity = TableVicinity(column: column, row: row);
678690
// TODO(Piinks): Add back merged cells, https://github.com/flutter/flutter/issues/131224
@@ -689,9 +701,11 @@ class RenderTableViewport extends RenderTwoDimensionalViewport {
689701
cell.layout(cellConstraints);
690702
cellParentData.layoutOffset = Offset(xPaintOffset, yPaintOffset);
691703
}
692-
xPaintOffset += columnWidth;
704+
xPaintOffset += columnWidth +
705+
_columnMetrics[column]!.configuration.padding.trailing;
693706
}
694-
yPaintOffset += rowHeight;
707+
yPaintOffset +=
708+
rowHeight + _rowMetrics[row]!.configuration.padding.trailing;
695709
}
696710
}
697711

@@ -836,29 +850,45 @@ class RenderTableViewport extends RenderTwoDimensionalViewport {
836850
final LinkedHashMap<Rect, TableSpanDecoration> backgroundColumns =
837851
LinkedHashMap<Rect, TableSpanDecoration>();
838852

853+
final TableSpan rowSpan = _rowMetrics[leading.row]!.configuration;
839854
for (int column = leading.column; column <= trailing.column; column++) {
840-
final _Span span = _columnMetrics[column]!;
841-
if (span.configuration.backgroundDecoration != null ||
842-
span.configuration.foregroundDecoration != null) {
855+
final TableSpan columnSpan = _columnMetrics[column]!.configuration;
856+
if (columnSpan.backgroundDecoration != null ||
857+
columnSpan.foregroundDecoration != null) {
843858
final RenderBox leadingCell = getChildFor(
844859
TableVicinity(column: column, row: leading.row),
845860
)!;
846861
final RenderBox trailingCell = getChildFor(
847862
TableVicinity(column: column, row: trailing.row),
848863
)!;
849864

850-
final Rect rect = Rect.fromPoints(
851-
parentDataOf(leadingCell).paintOffset! + offset,
852-
parentDataOf(trailingCell).paintOffset! +
853-
Offset(trailingCell.size.width, trailingCell.size.height) +
854-
offset,
855-
);
865+
Rect getColumnRect(bool consumePadding) {
866+
return Rect.fromPoints(
867+
parentDataOf(leadingCell).paintOffset! +
868+
offset -
869+
Offset(
870+
consumePadding ? columnSpan.padding.leading : 0.0,
871+
rowSpan.padding.leading,
872+
),
873+
parentDataOf(trailingCell).paintOffset! +
874+
offset +
875+
Offset(trailingCell.size.width, trailingCell.size.height) +
876+
Offset(
877+
consumePadding ? columnSpan.padding.trailing : 0.0,
878+
rowSpan.padding.trailing,
879+
),
880+
);
881+
}
856882

857-
if (span.configuration.backgroundDecoration != null) {
858-
backgroundColumns[rect] = span.configuration.backgroundDecoration!;
883+
if (columnSpan.backgroundDecoration != null) {
884+
final Rect rect = getColumnRect(
885+
columnSpan.backgroundDecoration!.consumeSpanPadding);
886+
backgroundColumns[rect] = columnSpan.backgroundDecoration!;
859887
}
860-
if (span.configuration.foregroundDecoration != null) {
861-
foregroundColumns[rect] = span.configuration.foregroundDecoration!;
888+
if (columnSpan.foregroundDecoration != null) {
889+
final Rect rect = getColumnRect(
890+
columnSpan.foregroundDecoration!.consumeSpanPadding);
891+
foregroundColumns[rect] = columnSpan.foregroundDecoration!;
862892
}
863893
}
864894
}
@@ -869,28 +899,45 @@ class RenderTableViewport extends RenderTwoDimensionalViewport {
869899
final LinkedHashMap<Rect, TableSpanDecoration> backgroundRows =
870900
LinkedHashMap<Rect, TableSpanDecoration>();
871901

902+
final TableSpan columnSpan = _columnMetrics[leading.column]!.configuration;
872903
for (int row = leading.row; row <= trailing.row; row++) {
873-
final _Span span = _rowMetrics[row]!;
874-
if (span.configuration.backgroundDecoration != null ||
875-
span.configuration.foregroundDecoration != null) {
904+
final TableSpan rowSpan = _rowMetrics[row]!.configuration;
905+
if (rowSpan.backgroundDecoration != null ||
906+
rowSpan.foregroundDecoration != null) {
876907
final RenderBox leadingCell = getChildFor(
877908
TableVicinity(column: leading.column, row: row),
878909
)!;
879910
final RenderBox trailingCell = getChildFor(
880911
TableVicinity(column: trailing.column, row: row),
881912
)!;
882913

883-
final Rect rect = Rect.fromPoints(
884-
parentDataOf(leadingCell).paintOffset! + offset,
885-
parentDataOf(trailingCell).paintOffset! +
886-
Offset(trailingCell.size.width, trailingCell.size.height) +
887-
offset,
888-
);
889-
if (span.configuration.backgroundDecoration != null) {
890-
backgroundRows[rect] = span.configuration.backgroundDecoration!;
914+
Rect getRowRect(bool consumePadding) {
915+
return Rect.fromPoints(
916+
parentDataOf(leadingCell).paintOffset! +
917+
offset -
918+
Offset(
919+
columnSpan.padding.leading,
920+
consumePadding ? rowSpan.padding.leading : 0.0,
921+
),
922+
parentDataOf(trailingCell).paintOffset! +
923+
offset +
924+
Offset(trailingCell.size.width, trailingCell.size.height) +
925+
Offset(
926+
columnSpan.padding.leading,
927+
consumePadding ? rowSpan.padding.trailing : 0.0,
928+
),
929+
);
930+
}
931+
932+
if (rowSpan.backgroundDecoration != null) {
933+
final Rect rect =
934+
getRowRect(rowSpan.backgroundDecoration!.consumeSpanPadding);
935+
backgroundRows[rect] = rowSpan.backgroundDecoration!;
891936
}
892-
if (span.configuration.foregroundDecoration != null) {
893-
foregroundRows[rect] = span.configuration.foregroundDecoration!;
937+
if (rowSpan.foregroundDecoration != null) {
938+
final Rect rect =
939+
getRowRect(rowSpan.foregroundDecoration!.consumeSpanPadding);
940+
foregroundRows[rect] = rowSpan.foregroundDecoration!;
894941
}
895942
}
896943
}
@@ -1028,7 +1075,12 @@ class _Span
10281075
bool get isPinned => _isPinned;
10291076
late bool _isPinned;
10301077

1031-
double get trailingOffset => leadingOffset + extent;
1078+
double get trailingOffset {
1079+
return leadingOffset +
1080+
extent +
1081+
configuration.padding.leading +
1082+
configuration.padding.trailing;
1083+
}
10321084

10331085
// ---- Span Management ----
10341086

packages/two_dimensional_scrollables/lib/src/table_view/table_span.dart

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,42 @@ import 'package:flutter/widgets.dart';
1010

1111
import 'table.dart';
1212

13+
/// Defines the leading and trailing padding values of a [TableSpan].
14+
class TableSpanPadding {
15+
/// Creates a padding configuration for a [TableSpan].
16+
const TableSpanPadding({
17+
this.leading = 0.0,
18+
this.trailing = 0.0,
19+
});
20+
21+
/// Creates padding where both the [leading] and [trailing] are `value`.
22+
const TableSpanPadding.all(double value)
23+
: leading = value,
24+
trailing = value;
25+
26+
/// The leading amount of pixels to pad a [TableSpan] by.
27+
///
28+
/// If the [TableSpan] is a row and the vertical [Axis] is not reversed, this
29+
/// offset will be applied above the row. If the vertical [Axis] is reversed,
30+
/// this will be applied below the row.
31+
///
32+
/// If the [TableSpan] is a column and the horizontal [Axis] is not reversed,
33+
/// this offset will be applied to the left the column. If the horizontal
34+
/// [Axis] is reversed, this will be applied to the right of the column.
35+
final double leading;
36+
37+
/// The trailing amount of pixels to pad a [TableSpan] by.
38+
///
39+
/// If the [TableSpan] is a row and the vertical [Axis] is not reversed, this
40+
/// offset will be applied below the row. If the vertical [Axis] is reversed,
41+
/// this will be applied above the row.
42+
///
43+
/// If the [TableSpan] is a column and the horizontal [Axis] is not reversed,
44+
/// this offset will be applied to the right the column. If the horizontal
45+
/// [Axis] is reversed, this will be applied to the left of the column.
46+
final double trailing;
47+
}
48+
1349
/// Defines the extent, visual appearance, and gesture handling of a row or
1450
/// column in a [TableView].
1551
///
@@ -20,20 +56,26 @@ class TableSpan {
2056
/// The [extent] argument must be provided.
2157
const TableSpan({
2258
required this.extent,
59+
TableSpanPadding? padding,
2360
this.recognizerFactories = const <Type, GestureRecognizerFactory>{},
2461
this.onEnter,
2562
this.onExit,
2663
this.cursor = MouseCursor.defer,
2764
this.backgroundDecoration,
2865
this.foregroundDecoration,
29-
});
66+
}) : padding = padding ?? const TableSpanPadding();
3067

3168
/// Defines the extent of the span.
3269
///
3370
/// If the span represents a row, this is the height of the row. If it
3471
/// represents a column, this is the width of the column.
3572
final TableSpanExtent extent;
3673

74+
/// Defines the leading and or trailing extent to pad the row or column by.
75+
///
76+
/// Defaults to no padding.
77+
final TableSpanPadding padding;
78+
3779
/// Factory for creating [GestureRecognizer]s that want to compete for
3880
/// gestures within the [extent] of the span.
3981
///
@@ -251,14 +293,63 @@ class MinTableSpanExtent extends CombiningTableSpanExtent {
251293
/// A decoration for a [TableSpan].
252294
class TableSpanDecoration {
253295
/// Creates a [TableSpanDecoration].
254-
const TableSpanDecoration({this.border, this.color});
296+
const TableSpanDecoration({
297+
this.border,
298+
this.color,
299+
this.consumeSpanPadding = true,
300+
});
255301

256302
/// The border drawn around the span.
257303
final TableSpanBorder? border;
258304

259305
/// The color to fill the bounds of the span with.
260306
final Color? color;
261307

308+
/// Whether or not the decoration should extend to fill the space created by
309+
/// the [TableSpanPadding].
310+
///
311+
/// Defaults to true, meaning if a [TableSpan] is a row, the decoration will
312+
/// apply to the full [TableSpanExtent], including the
313+
/// [TableSpanPadding.leading] and [TableSpanPadding.trailing] for the row.
314+
/// This same row decoration will consume any padding from the column spans so
315+
/// as to decorate the row as one continuous span.
316+
///
317+
/// {@tool snippet}
318+
/// This example illustrates how [consumeSpanPadding] affects
319+
/// [TableSpanDecoration.color]. By default, the color of the decoration
320+
/// consumes the padding, coloring the row fully by including the padding
321+
/// around the row. When [consumeSpanPadding] is false, the padded area of
322+
/// the row is not decorated.
323+
///
324+
/// ```dart
325+
/// TableView.builder(
326+
/// rowCount: 4,
327+
/// columnCount: 4,
328+
/// columnBuilder: (int index) => TableSpan(
329+
/// extent: const FixedTableSpanExtent(150.0),
330+
/// padding: const TableSpanPadding(trailing: 10),
331+
/// ),
332+
/// rowBuilder: (int index) => TableSpan(
333+
/// extent: const FixedTableSpanExtent(150.0),
334+
/// padding: TableSpanPadding(leading: 10, trailing: 10),
335+
/// backgroundDecoration: TableSpanDecoration(
336+
/// color: index.isOdd ? Colors.blue : Colors.green,
337+
/// // The background color will not be applied to the padded area.
338+
/// consumeSpanPadding: false,
339+
/// ),
340+
/// ),
341+
/// cellBuilder: (_, TableVicinity vicinity) {
342+
/// return Container(
343+
/// height: 150,
344+
/// width: 150,
345+
/// child: const Center(child: FlutterLogo()),
346+
/// );
347+
/// },
348+
/// );
349+
/// ```
350+
/// {@end-tool}
351+
final bool consumeSpanPadding;
352+
262353
/// Called to draw the decoration around a span.
263354
///
264355
/// The provided [TableSpanDecorationPaintDetails] describes the bounds and

packages/two_dimensional_scrollables/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: two_dimensional_scrollables
22
description: Widgets that scroll using the two dimensional scrolling foundation.
3-
version: 0.0.3
3+
version: 0.0.4
44
repository: https://github.com/flutter/packages/tree/main/packages/two_dimensional_scrollables
55
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+two_dimensional_scrollables%22+
66

0 commit comments

Comments
 (0)