[two_dimensional_scrollables] Fix TableSpanDecoration rects when axes are reversed (#5916)

Fixes https://github.com/flutter/flutter/issues/141704

This fixes the rect computation for TableSpanDecorations when one or both axes are reversed.
We previously fixed similar issues when the clipRects applied to areas of the table did not account for reversed. This is another similar case.
- https://github.com/flutter/packages/pull/5187
- https://github.com/flutter/packages/pull/5038

![image](https://github.com/flutter/packages/assets/16964204/ae7c6872-c740-4617-95a4-8302a3085d59)
diff --git a/packages/two_dimensional_scrollables/CHANGELOG.md b/packages/two_dimensional_scrollables/CHANGELOG.md
index e2a6474..6b04d1b 100644
--- a/packages/two_dimensional_scrollables/CHANGELOG.md
+++ b/packages/two_dimensional_scrollables/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.0.6
+
+* Fixes an error in TableSpanDecoration when one or both axes are reversed.
+
 ## 0.0.5+2
 
 * Fixes must_call_super lint warning from pending framework change.
diff --git a/packages/two_dimensional_scrollables/lib/src/table_view/table.dart b/packages/two_dimensional_scrollables/lib/src/table_view/table.dart
index a2452c7..a8d9a0c 100644
--- a/packages/two_dimensional_scrollables/lib/src/table_view/table.dart
+++ b/packages/two_dimensional_scrollables/lib/src/table_view/table.dart
@@ -861,31 +861,41 @@
         )!;
 
         Rect getColumnRect(bool consumePadding) {
+          final ({double leading, double trailing}) offsetCorrection =
+              axisDirectionIsReversed(verticalAxisDirection)
+                  ? (
+                      leading: leadingCell.size.height,
+                      trailing: trailingCell.size.height,
+                    )
+                  : (leading: 0.0, trailing: 0.0);
+
           return Rect.fromPoints(
             parentDataOf(leadingCell).paintOffset! +
                 offset -
                 Offset(
                   consumePadding ? columnSpan.padding.leading : 0.0,
-                  rowSpan.padding.leading,
+                  rowSpan.padding.leading - offsetCorrection.leading,
                 ),
             parentDataOf(trailingCell).paintOffset! +
                 offset +
                 Offset(trailingCell.size.width, trailingCell.size.height) +
                 Offset(
                   consumePadding ? columnSpan.padding.trailing : 0.0,
-                  rowSpan.padding.trailing,
+                  rowSpan.padding.trailing - offsetCorrection.trailing,
                 ),
           );
         }
 
         if (columnSpan.backgroundDecoration != null) {
           final Rect rect = getColumnRect(
-              columnSpan.backgroundDecoration!.consumeSpanPadding);
+            columnSpan.backgroundDecoration!.consumeSpanPadding,
+          );
           backgroundColumns[rect] = columnSpan.backgroundDecoration!;
         }
         if (columnSpan.foregroundDecoration != null) {
           final Rect rect = getColumnRect(
-              columnSpan.foregroundDecoration!.consumeSpanPadding);
+            columnSpan.foregroundDecoration!.consumeSpanPadding,
+          );
           foregroundColumns[rect] = columnSpan.foregroundDecoration!;
         }
       }
@@ -910,18 +920,25 @@
         )!;
 
         Rect getRowRect(bool consumePadding) {
+          final ({double leading, double trailing}) offsetCorrection =
+              axisDirectionIsReversed(horizontalAxisDirection)
+                  ? (
+                      leading: leadingCell.size.width,
+                      trailing: trailingCell.size.width,
+                    )
+                  : (leading: 0.0, trailing: 0.0);
           return Rect.fromPoints(
             parentDataOf(leadingCell).paintOffset! +
                 offset -
                 Offset(
-                  columnSpan.padding.leading,
+                  columnSpan.padding.leading - offsetCorrection.leading,
                   consumePadding ? rowSpan.padding.leading : 0.0,
                 ),
             parentDataOf(trailingCell).paintOffset! +
                 offset +
                 Offset(trailingCell.size.width, trailingCell.size.height) +
                 Offset(
-                  columnSpan.padding.leading,
+                  columnSpan.padding.leading - offsetCorrection.trailing,
                   consumePadding ? rowSpan.padding.trailing : 0.0,
                 ),
           );
diff --git a/packages/two_dimensional_scrollables/pubspec.yaml b/packages/two_dimensional_scrollables/pubspec.yaml
index d8a9f96..5d1ede3 100644
--- a/packages/two_dimensional_scrollables/pubspec.yaml
+++ b/packages/two_dimensional_scrollables/pubspec.yaml
@@ -1,6 +1,6 @@
 name: two_dimensional_scrollables
 description: Widgets that scroll using the two dimensional scrolling foundation.
-version: 0.0.5+2
+version: 0.0.6
 repository: https://github.com/flutter/packages/tree/main/packages/two_dimensional_scrollables
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+two_dimensional_scrollables%22+
 
diff --git a/packages/two_dimensional_scrollables/test/table_view/table_span_test.dart b/packages/two_dimensional_scrollables/test/table_view/table_span_test.dart
index fea0d26..b6b0db3 100644
--- a/packages/two_dimensional_scrollables/test/table_view/table_span_test.dart
+++ b/packages/two_dimensional_scrollables/test/table_view/table_span_test.dart
@@ -177,6 +177,675 @@
     expect(border.details, details);
     expect(border.radius, radius);
   });
+
+  group('Decoration rects account for reversed axes', () {
+    late ScrollController verticalController;
+    late ScrollController horizontalController;
+
+    setUp(() {
+      verticalController = ScrollController();
+      horizontalController = ScrollController();
+    });
+
+    tearDown(() {
+      verticalController.dispose();
+      horizontalController.dispose();
+    });
+
+    Widget buildCell(BuildContext context, TableVicinity vicinity) {
+      return const SizedBox.shrink();
+    }
+
+    TableSpan buildSpan(bool isColumn) {
+      return TableSpan(
+        extent: const FixedTableSpanExtent(100),
+        foregroundDecoration: TableSpanDecoration(
+          color: isColumn ? const Color(0xFFE1BEE7) : const Color(0xFFBBDEFB),
+        ),
+      );
+    }
+
+    testWidgets('Vertical main axis, vertical reversed',
+        (WidgetTester tester) async {
+      final TableView table = TableView.builder(
+        verticalDetails: ScrollableDetails.vertical(
+          controller: verticalController,
+          reverse: true,
+        ),
+        horizontalDetails: ScrollableDetails.horizontal(
+          controller: horizontalController,
+        ),
+        rowCount: 10,
+        columnCount: 10,
+        rowBuilder: (_) => buildSpan(false),
+        columnBuilder: (_) => buildSpan(true),
+        cellBuilder: buildCell,
+      );
+      await tester.pumpWidget(Directionality(
+        textDirection: TextDirection.ltr,
+        child: table,
+      ));
+      await tester.pumpAndSettle();
+
+      expect(
+        find.byType(TableViewport),
+        paints
+          // Rows first, bottom to top for reversed axis
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, 500.0, 1000.0, 600.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, 400.0, 1000.0, 500.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, 300.0, 1000.0, 400.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, 200.0, 1000.0, 300.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, 100.0, 1000.0, 200.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, 0.0, 1000.0, 100.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, -100.0, 1000.0, 0.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, -200.0, 1000.0, -100.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, -300.0, 1000.0, -200.0),
+            color: const Color(0xffbbdefb),
+          )
+          // Columns next
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, -300.0, 100.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(100.0, -300.0, 200.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(200.0, -300.0, 300.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(300.0, -300.0, 400.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(400.0, -300.0, 500.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(500.0, -300.0, 600.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(600.0, -300.0, 700.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(700.0, -300.0, 800.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(800.0, -300.0, 900.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(900.0, -300.0, 1000.0, 600.0),
+            color: const Color(0xffe1bee7),
+          ),
+      );
+    });
+
+    testWidgets('Vertical main axis, horizontal reversed',
+        (WidgetTester tester) async {
+      final TableView table = TableView.builder(
+        verticalDetails: ScrollableDetails.vertical(
+          controller: verticalController,
+        ),
+        horizontalDetails: ScrollableDetails.horizontal(
+          controller: horizontalController,
+          reverse: true,
+        ),
+        rowCount: 10,
+        columnCount: 10,
+        rowBuilder: (_) => buildSpan(false),
+        columnBuilder: (_) => buildSpan(true),
+        cellBuilder: buildCell,
+      );
+      await tester.pumpWidget(Directionality(
+        textDirection: TextDirection.ltr,
+        child: table,
+      ));
+      await tester.pumpAndSettle();
+
+      expect(
+        find.byType(TableViewport),
+        paints
+          // Rows first
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 0.0, 800.0, 100.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 100.0, 800.0, 200.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 200.0, 800.0, 300.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 300.0, 800.0, 400.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 400.0, 800.0, 500.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 500.0, 800.0, 600.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 600.0, 800.0, 700.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 700.0, 800.0, 800.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 800.0, 800.0, 900.0),
+            color: const Color(0xffbbdefb),
+          )
+          // Columns next, right to left for reversed axis
+          ..rect(
+            rect: const Rect.fromLTRB(700.0, 0.0, 800.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(600.0, 0.0, 700.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(500.0, 0.0, 600.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(400.0, 0.0, 500.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(300.0, 0.0, 400.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(200.0, 0.0, 300.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(100.0, 0.0, 200.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-100.0, 0.0, 0.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 0.0, -100.0, 900.0),
+            color: const Color(0xffe1bee7),
+          ),
+      );
+    });
+
+    testWidgets('Vertical main axis, both reversed',
+        (WidgetTester tester) async {
+      final TableView table = TableView.builder(
+        verticalDetails: ScrollableDetails.vertical(
+          controller: verticalController,
+          reverse: true,
+        ),
+        horizontalDetails: ScrollableDetails.horizontal(
+          controller: horizontalController,
+          reverse: true,
+        ),
+        rowCount: 10,
+        columnCount: 10,
+        rowBuilder: (_) => buildSpan(false),
+        columnBuilder: (_) => buildSpan(true),
+        cellBuilder: buildCell,
+      );
+      await tester.pumpWidget(Directionality(
+        textDirection: TextDirection.ltr,
+        child: table,
+      ));
+      await tester.pumpAndSettle();
+
+      expect(
+        find.byType(TableViewport),
+        paints
+          // Rows first, bottom to top for reversed axis
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 500.0, 800.0, 600.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 400.0, 800.0, 500.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 300.0, 800.0, 400.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 200.0, 800.0, 300.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 100.0, 800.0, 200.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 0.0, 800.0, 100.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, -100.0, 800.0, 0.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, -200.0, 800.0, -100.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, -300.0, 800.0, -200.0),
+            color: const Color(0xffbbdefb),
+          )
+          // Columns next, right to left for reversed axis
+          ..rect(
+            rect: const Rect.fromLTRB(700.0, -300.0, 800.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(600.0, -300.0, 700.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(500.0, -300.0, 600.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(400.0, -300.0, 500.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(300.0, -300.0, 400.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(200.0, -300.0, 300.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(100.0, -300.0, 200.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, -300.0, 100.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-100.0, -300.0, 0.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, -300.0, -100.0, 600.0),
+            color: const Color(0xffe1bee7),
+          ),
+      );
+    });
+
+    testWidgets('Horizontal main axis, vertical reversed',
+        (WidgetTester tester) async {
+      final TableView table = TableView.builder(
+        mainAxis: Axis.horizontal,
+        verticalDetails: ScrollableDetails.vertical(
+          controller: verticalController,
+          reverse: true,
+        ),
+        horizontalDetails: ScrollableDetails.horizontal(
+          controller: horizontalController,
+        ),
+        rowCount: 10,
+        columnCount: 10,
+        rowBuilder: (_) => buildSpan(false),
+        columnBuilder: (_) => buildSpan(true),
+        cellBuilder: buildCell,
+      );
+      await tester.pumpWidget(Directionality(
+        textDirection: TextDirection.ltr,
+        child: table,
+      ));
+      await tester.pumpAndSettle();
+
+      expect(
+        find.byType(TableViewport),
+        paints
+          // Columns first
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, -300.0, 100.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(100.0, -300.0, 200.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(200.0, -300.0, 300.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(300.0, -300.0, 400.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(400.0, -300.0, 500.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(500.0, -300.0, 600.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(600.0, -300.0, 700.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(700.0, -300.0, 800.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(800.0, -300.0, 900.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(900.0, -300.0, 1000.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          // Rows next, bottom to top for reversed axis
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, 500.0, 1000.0, 600.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, 400.0, 1000.0, 500.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, 300.0, 1000.0, 400.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, 200.0, 1000.0, 300.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, 100.0, 1000.0, 200.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, 0.0, 1000.0, 100.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, -100.0, 1000.0, 0.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, -200.0, 1000.0, -100.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, -300.0, 1000.0, -200.0),
+            color: const Color(0xffbbdefb),
+          ),
+      );
+    });
+
+    testWidgets('Horizontal main axis, horizontal reversed',
+        (WidgetTester tester) async {
+      final TableView table = TableView.builder(
+        mainAxis: Axis.horizontal,
+        verticalDetails: ScrollableDetails.vertical(
+          controller: verticalController,
+        ),
+        horizontalDetails: ScrollableDetails.horizontal(
+          controller: horizontalController,
+          reverse: true,
+        ),
+        rowCount: 10,
+        columnCount: 10,
+        rowBuilder: (_) => buildSpan(false),
+        columnBuilder: (_) => buildSpan(true),
+        cellBuilder: buildCell,
+      );
+      await tester.pumpWidget(Directionality(
+        textDirection: TextDirection.ltr,
+        child: table,
+      ));
+      await tester.pumpAndSettle();
+
+      expect(
+        find.byType(TableViewport),
+        paints
+          // Columns first, right to left for reversed axis
+          ..rect(
+            rect: const Rect.fromLTRB(700.0, 0.0, 800.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(600.0, 0.0, 700.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(500.0, 0.0, 600.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(400.0, 0.0, 500.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(300.0, 0.0, 400.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(200.0, 0.0, 300.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(100.0, 0.0, 200.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-100.0, 0.0, 0.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 0.0, -100.0, 900.0),
+            color: const Color(0xffe1bee7),
+          )
+          // Rows next
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 0.0, 800.0, 100.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 100.0, 800.0, 200.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 200.0, 800.0, 300.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 300.0, 800.0, 400.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 400.0, 800.0, 500.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 500.0, 800.0, 600.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 600.0, 800.0, 700.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 700.0, 800.0, 800.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 800.0, 800.0, 900.0),
+            color: const Color(0xffbbdefb),
+          ),
+      );
+    });
+
+    testWidgets('Horizontal main axis, both reversed',
+        (WidgetTester tester) async {
+      final TableView table = TableView.builder(
+        mainAxis: Axis.horizontal,
+        verticalDetails: ScrollableDetails.vertical(
+          controller: verticalController,
+          reverse: true,
+        ),
+        horizontalDetails: ScrollableDetails.horizontal(
+          controller: horizontalController,
+          reverse: true,
+        ),
+        rowCount: 10,
+        columnCount: 10,
+        rowBuilder: (_) => buildSpan(false),
+        columnBuilder: (_) => buildSpan(true),
+        cellBuilder: buildCell,
+      );
+      await tester.pumpWidget(Directionality(
+        textDirection: TextDirection.ltr,
+        child: table,
+      ));
+      await tester.pumpAndSettle();
+
+      expect(
+        find.byType(TableViewport),
+        paints
+          // Columns first, right to left for reversed axis
+          ..rect(
+            rect: const Rect.fromLTRB(700.0, -300.0, 800.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(600.0, -300.0, 700.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(500.0, -300.0, 600.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(400.0, -300.0, 500.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(300.0, -300.0, 400.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(200.0, -300.0, 300.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(100.0, -300.0, 200.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(0.0, -300.0, 100.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-100.0, -300.0, 0.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, -300.0, -100.0, 600.0),
+            color: const Color(0xffe1bee7),
+          )
+          // Rows next, bottom to top for reversed axis
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 500.0, 800.0, 600.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 400.0, 800.0, 500.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 300.0, 800.0, 400.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 200.0, 800.0, 300.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 100.0, 800.0, 200.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, 0.0, 800.0, 100.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, -100.0, 800.0, 0.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, -200.0, 800.0, -100.0),
+            color: const Color(0xffbbdefb),
+          )
+          ..rect(
+            rect: const Rect.fromLTRB(-200.0, -300.0, 800.0, -200.0),
+            color: const Color(0xffbbdefb),
+          ),
+      );
+    });
+  });
 }
 
 class TestCanvas implements Canvas {