Repository navigation
[two_dimensional_scrollables] TableSpan vertical borders are flipped when ScrollableDetails.horizontal is reversed #177117
Copy link
Copy link
Closed
flutter/packages
#11334Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work listf: scrollingViewports, list views, slivers, etc.Viewports, list views, slivers, etc.found in release: 3.35Found to occur in 3.35Found to occur in 3.35found in release: 3.37Found to occur in 3.37Found to occur in 3.37frameworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work onp: two_dimensional_scrollablesIssues pertaining to the two_dimensional_scrollables packageIssues pertaining to the two_dimensional_scrollables packagepackageflutter/packages repository. See also p: labels.flutter/packages repository. See also p: labels.team-frameworkOwned by Framework teamOwned by Framework teamtriaged-frameworkTriaged by Framework teamTriaged by Framework teamwaiting for PR to land (fixed)A fix is in flightA fix is in flight
Description
What package does this bug report belong to?
two_dimensional_scrollables
What target platforms are you seeing this bug on?
Windows
Have you already upgraded your packages?
Yes
Dependency versions
pubspec.lock
Steps to reproduce
Using the package example app
- Set
horizontalDetailsin theTableView.builderto:
horizontalDetails: ScrollableDetails.horizontal(
reverse: true,
),Expected results
The TableSpan (row) border should be at the bottom since it's defined as trailing border in
TableSpan _buildRowSpan(int index) {
final TableSpanDecoration decoration = TableSpanDecoration(
color: index.isEven ? Colors.purple[100] : null,
border: const TableSpanBorder(trailing: BorderSide(width: 3)),
);
// rest of code
}Actual results
The border placement is switched. From leading to trailing and vice-versa.
Code sample
Code sample
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:two_dimensional_scrollables/two_dimensional_scrollables.dart';
void main() {
runApp(MaterialApp(home: const TableExample()));
}
class TableExample extends StatefulWidget {
/// Creates a screen that demonstrates the TableView widget.
const TableExample({super.key});
@override
State<TableExample> createState() => _TableExampleState();
}
class _TableExampleState extends State<TableExample> {
late final ScrollController _verticalController = ScrollController();
final int _rowCount = 20;
@override
void dispose() {
_verticalController.dispose();
super.dispose();
}
bool reversed = false;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: TableView.builder(
verticalDetails: ScrollableDetails.vertical(
controller: _verticalController,
),
horizontalDetails: ScrollableDetails.horizontal(
reverse: reversed,
),
cellBuilder: _buildCell,
columnCount: 20,
columnBuilder: _buildColumnSpan,
rowCount: _rowCount,
rowBuilder: _buildRowSpan,
),
),
persistentFooterButtons: <Widget>[
OverflowBar(
alignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SegmentedButton<bool>(
segments: const <ButtonSegment<bool>>[
ButtonSegment<bool>(
value: true,
label: Text('Reversed'),
icon: Icon(Icons.keyboard_double_arrow_right),
),
ButtonSegment<bool>(
value: false,
label: Text('Normal'),
icon: Icon(Icons.keyboard_double_arrow_left),
),
],
selected: <bool>{reversed},
onSelectionChanged: (Set<bool> newValue) {
print(newValue.toList());
setState(() {
// By default there is only a single segment that can be
// selected at one time, so its value is always the first
// item in the selected set.
reversed = newValue.first;
});
},
),
],
),
],
),
],
);
}
TableViewCell _buildCell(BuildContext context, TableVicinity vicinity) {
Widget result = Center(
child: Text('Tile c: ${vicinity.column}, r: ${vicinity.row}'),
);
return TableViewCell(child: result);
}
TableSpan _buildColumnSpan(int index) {
const TableSpanDecoration decoration = TableSpanDecoration(
border: TableSpanBorder(trailing: BorderSide()),
);
switch (index % 5) {
case 0:
return TableSpan(
foregroundDecoration: decoration,
extent: const FixedTableSpanExtent(100),
onEnter: (_) => print('Entered column $index'),
recognizerFactories: <Type, GestureRecognizerFactory>{
TapGestureRecognizer:
GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
() => TapGestureRecognizer(),
(TapGestureRecognizer t) =>
t.onTap = () => print('Tap column $index'),
),
},
);
case 1:
return TableSpan(
foregroundDecoration: decoration,
extent: const FractionalTableSpanExtent(0.5),
onEnter: (_) => print('Entered column $index'),
cursor: SystemMouseCursors.contextMenu,
);
case 2:
return TableSpan(
foregroundDecoration: decoration,
extent: const FixedTableSpanExtent(120),
onEnter: (_) => print('Entered column $index'),
);
case 3:
return TableSpan(
foregroundDecoration: decoration,
extent: const FixedTableSpanExtent(145),
onEnter: (_) => print('Entered column $index'),
);
case 4:
return TableSpan(
foregroundDecoration: decoration,
extent: const FixedTableSpanExtent(200),
onEnter: (_) => print('Entered column $index'),
);
}
throw AssertionError(
'This should be unreachable, as every index is accounted for in the '
'switch clauses.',
);
}
TableSpan _buildRowSpan(int index) {
final TableSpanDecoration decoration = TableSpanDecoration(
color: index.isEven ? Colors.purple[100] : null,
border: const TableSpanBorder(trailing: BorderSide(width: 3)),
);
switch (index % 3) {
case 0:
return TableSpan(
backgroundDecoration: decoration,
extent: const FixedTableSpanExtent(50),
recognizerFactories: <Type, GestureRecognizerFactory>{
TapGestureRecognizer:
GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
() => TapGestureRecognizer(),
(TapGestureRecognizer t) =>
t.onTap = () => print('Tap row $index'),
),
},
);
case 1:
return TableSpan(
backgroundDecoration: decoration,
extent: const FixedTableSpanExtent(65),
cursor: SystemMouseCursors.click,
);
case 2:
return TableSpan(
backgroundDecoration: decoration,
extent: const FractionalTableSpanExtent(0.15),
);
}
throw AssertionError(
'This should be unreachable, as every index is accounted for in the '
'switch clauses.',
);
}
}
Screenshots or Videos
Screenshots / Video demonstration
bandicam.2025-10-16.21-21-28-612.mp4
Logs
Logs
[Paste your logs here]Flutter Doctor output
Doctor output
Flutter (Channel stable, 3.35.3, on Microsoft Windows [Version 10.0.22631.2070], locale en-US) [522ms]
• Flutter version 3.35.3 on channel stable at C:\Users\Mouayad\dev\flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision a402d9a437 (6 weeks ago), 2025-09-03 14:54:31 -0700
• Engine revision ddf47dd3ff
• Dart version 3.9.2
• DevTools version 2.48.0
• Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations,
enable-lldb-debuggingReactions are currently unavailable
Metadata
Metadata
Assignees
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work listf: scrollingViewports, list views, slivers, etc.Viewports, list views, slivers, etc.found in release: 3.35Found to occur in 3.35Found to occur in 3.35found in release: 3.37Found to occur in 3.37Found to occur in 3.37frameworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work onp: two_dimensional_scrollablesIssues pertaining to the two_dimensional_scrollables packageIssues pertaining to the two_dimensional_scrollables packagepackageflutter/packages repository. See also p: labels.flutter/packages repository. See also p: labels.team-frameworkOwned by Framework teamOwned by Framework teamtriaged-frameworkTriaged by Framework teamTriaged by Framework teamwaiting for PR to land (fixed)A fix is in flightA fix is in flight
Activity
[-]TableSpan vertical borders are flipped when ScrollableDetails.horizontal is reversed[/-][+][two_dimensional_scrollables] TableSpan vertical borders are flipped when ScrollableDetails.horizontal is reversed[/+]tirth-patel-nc commentedon Oct 17, 2025
@DMouayad Thanks for the report. Seeing the same behaviour with latest SDK versions.
[two_dimensional_scrollables] Fix span border decorations in flipped …
[two_dimensional_scrollables] Fix span border decorations in flipped …
github-actions commentedon Apr 8, 2026
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.[two_dimensional_scrollables] Fix span border decorations in flipped …