@@ -104,34 +104,40 @@ class DevToolsTable<T> extends StatefulWidget {
104104 final bool fillWithEmptyRows;
105105 final bool enableHoverHandling;
106106
107+ static const columnMinWidth = 50.0 ;
108+
107109 @override
108110 DevToolsTableState <T > createState () => DevToolsTableState <T >();
109111}
110112
111113@visibleForTesting
112114class DevToolsTableState <T > extends State <DevToolsTable <T >>
113115 with AutoDisposeMixin {
116+ static const _resizingDebounceDuration = Duration (milliseconds: 200 );
117+
114118 late ScrollController scrollController;
115119 late ScrollController pinnedScrollController;
116120 late ScrollController _horizontalScrollbarController;
117121
118122 late List <T > _data;
119123
120- /// An adjusted copy of `widget.columnWidths` where any variable width columns
121- /// may be increased so that the sum of all column widths equals the available
122- /// screen space.
123- ///
124- /// This must be calculated where we have access to the Flutter view
125- /// constraints (e.g. the [LayoutBuilder] below).
124+ late Debouncer _resizingDebouncer;
125+
126126 @visibleForTesting
127- late List <double > adjustedColumnWidths;
127+ List <double > get columnWidths => _columnWidths;
128+
129+ late List <double > _columnWidths;
130+
131+ double ? _previousViewWidth;
128132
129133 @override
130134 void initState () {
131135 super .initState ();
132136
133137 _initDataAndAddListeners ();
134138
139+ _resizingDebouncer = Debouncer (duration: _resizingDebounceDuration);
140+
135141 final initialScrollOffset = widget.preserveVerticalScrollPosition
136142 ? widget.tableController.tableUiState.scrollOffset
137143 : 0.0 ;
@@ -148,7 +154,7 @@ class DevToolsTableState<T> extends State<DevToolsTable<T>>
148154
149155 pinnedScrollController = ScrollController ();
150156
151- adjustedColumnWidths = List .of (widget.columnWidths);
157+ _columnWidths = List .of (widget.columnWidths);
152158 }
153159
154160 @override
@@ -166,7 +172,9 @@ class DevToolsTableState<T> extends State<DevToolsTable<T>>
166172 _initDataAndAddListeners ();
167173 }
168174
169- adjustedColumnWidths = List .of (widget.columnWidths);
175+ if (! collectionEquals (widget.columnWidths, oldWidget.columnWidths)) {
176+ _columnWidths = List .of (widget.columnWidths);
177+ }
170178 }
171179
172180 void _initDataAndAddListeners () {
@@ -252,93 +260,91 @@ class DevToolsTableState<T> extends State<DevToolsTable<T>>
252260 super .dispose ();
253261 }
254262
263+ void _handleColumnResize (int columnIndex, double newWidth) {
264+ setState (() {
265+ _columnWidths[columnIndex] = newWidth;
266+ });
267+ }
268+
255269 /// The width of all columns in the table with additional padding.
256- double get _tableWidthForOriginalColumns {
270+ double get _currentTableWidth {
257271 var tableWidth = 2 * defaultSpacing;
258272 final numColumnGroupSpacers =
259273 widget.tableController.columnGroups? .numSpacers ?? 0 ;
260274 final numColumnSpacers =
261275 widget.tableController.columns.numSpacers - numColumnGroupSpacers;
262276 tableWidth += numColumnSpacers * columnSpacing;
263277 tableWidth += numColumnGroupSpacers * columnGroupSpacingWithPadding;
264- for (final columnWidth in widget.columnWidths ) {
278+ for (final columnWidth in _columnWidths ) {
265279 tableWidth += columnWidth;
266280 }
267281 return tableWidth;
268282 }
269283
270- /// Modifies [adjustedColumnWidths] so that any available view space greater
271- /// than [_tableWidthForOriginalColumns] is distributed evenly across variable
272- /// width columns.
284+ /// Adjusts the column widths to fit the new [viewWidth] .
285+ ///
286+ /// This method will attempt to distribute any extra space (positive or
287+ /// negative) amongst the variable-width columns. If there are no
288+ /// variable-width columns, it will distribute the space amongst all columns.
273289 void _adjustColumnWidthsForViewSize (double viewWidth) {
274- final extraSpace = viewWidth - _tableWidthForOriginalColumns;
275- if (extraSpace <= 0 ) {
276- adjustedColumnWidths = List .of (widget.columnWidths);
290+ final extraSpace = _currentTableWidth - viewWidth;
291+ if (extraSpace == 0 ) {
277292 return ;
278293 }
279294
280- final adjustedColumnWidthsByIndex = < int , double > {};
281-
282- /// Helper method to evenly distribute [space] among the columns at
283- /// [columnIndices] .
284- ///
285- /// This method stores the adjusted width values in
286- /// [adjustedColumnWidthsByIndex] .
287- void evenlyDistributeColumnSizes (List <int > columnIndices, double space) {
288- final targetSize = space / columnIndices.length;
289-
290- var largestColumnIndex = - 1 ;
291- var largestColumnWidth = 0.0 ;
292- for (final index in columnIndices) {
293- final columnWidth = widget.columnWidths[index];
294- if (columnWidth >= largestColumnWidth) {
295- largestColumnIndex = index;
296- largestColumnWidth = columnWidth;
297- }
298- }
299- if (targetSize < largestColumnWidth) {
300- // We do not have enough extra space to evenly distribute to all
301- // columns. Remove the largest column and recurse.
302- adjustedColumnWidthsByIndex[largestColumnIndex] = largestColumnWidth;
303- final newColumnIndices = List .of (columnIndices)
304- ..remove (largestColumnIndex);
305- return evenlyDistributeColumnSizes (
306- newColumnIndices,
307- space - largestColumnWidth,
308- );
309- }
310-
311- for (int i = 0 ; i < columnIndices.length; i++ ) {
312- final columnIndex = columnIndices[i];
313- adjustedColumnWidthsByIndex[columnIndex] = targetSize;
314- }
315- }
316-
317- final variableWidthColumnIndices = < int > [];
318- var sumVariableWidthColumnSizes = 0.0 ;
295+ final variableWidthColumnIndices = < (int , double )> [];
319296 for (int i = 0 ; i < widget.tableController.columns.length; i++ ) {
320297 final column = widget.tableController.columns[i];
321298 if (column.fixedWidthPx == null ) {
322- variableWidthColumnIndices.add (i);
323- sumVariableWidthColumnSizes += widget.columnWidths[i];
299+ variableWidthColumnIndices.add ((i, _columnWidths[i]));
324300 }
325301 }
326- final totalVariableWidthColumnSpace =
327- sumVariableWidthColumnSizes + extraSpace;
328302
329- evenlyDistributeColumnSizes (
330- variableWidthColumnIndices,
331- totalVariableWidthColumnSpace,
303+ // If the table contains variable width columns, then distribute the extra
304+ // space between them. Otherwise, distribute the extra space between all the
305+ // columns.
306+ _distributeExtraSpace (
307+ extraSpace,
308+ indexedColumns: variableWidthColumnIndices.isNotEmpty
309+ ? variableWidthColumnIndices
310+ : _columnWidths.indexed,
332311 );
312+ }
333313
334- adjustedColumnWidths.clear ();
335- for (int i = 0 ; i < widget.columnWidths.length; i++ ) {
336- final originalWidth = widget.columnWidths[i];
337- final isVariableWidthColumn = variableWidthColumnIndices.contains (i);
338- adjustedColumnWidths.add (
339- isVariableWidthColumn ? adjustedColumnWidthsByIndex[i]! : originalWidth,
314+ /// Distributes [extraSpace] evenly between the given [indexedColumns] .
315+ ///
316+ /// The [extraSpace] will be subtracted from each column's width. The
317+ /// remainder of the division is subtracted from the last column to ensure a
318+ /// perfect fit.
319+ ///
320+ /// This method respects the `minWidthPx` of each column.
321+ void _distributeExtraSpace (
322+ double extraSpace, {
323+ required Iterable <(int , double )> indexedColumns,
324+ }) {
325+ final newWidths = List .of (_columnWidths);
326+ final delta = extraSpace / indexedColumns.length;
327+ final remainder = extraSpace % indexedColumns.length;
328+
329+ for (var i = 0 ; i < indexedColumns.length; i++ ) {
330+ final columnIndex = indexedColumns.elementAt (i).$1;
331+ var newWidth = indexedColumns.elementAt (i).$2;
332+
333+ newWidth -= delta;
334+ if (i == indexedColumns.length - 1 ) {
335+ newWidth -= remainder;
336+ }
337+
338+ final column = widget.tableController.columns[columnIndex];
339+ newWidths[columnIndex] = max (
340+ newWidth,
341+ column.minWidthPx ?? DevToolsTable .columnMinWidth,
340342 );
341343 }
344+
345+ setState (() {
346+ _columnWidths = newWidths;
347+ });
342348 }
343349
344350 double _pinnedDataHeight (BoxConstraints tableConstraints) => min (
@@ -372,7 +378,7 @@ class DevToolsTableState<T> extends State<DevToolsTable<T>>
372378 return widget.rowBuilder (
373379 context: context,
374380 index: index,
375- columnWidths: adjustedColumnWidths ,
381+ columnWidths: _columnWidths ,
376382 isPinned: isPinned,
377383 enableHoverHandling: widget.enableHoverHandling,
378384 );
@@ -406,7 +412,12 @@ class DevToolsTableState<T> extends State<DevToolsTable<T>>
406412 return LayoutBuilder (
407413 builder: (context, constraints) {
408414 final viewWidth = constraints.maxWidth;
409- _adjustColumnWidthsForViewSize (viewWidth);
415+ if (_previousViewWidth != null && viewWidth != _previousViewWidth) {
416+ _resizingDebouncer.run (
417+ () => _adjustColumnWidthsForViewSize (viewWidth),
418+ );
419+ }
420+ _previousViewWidth = viewWidth;
410421 return Scrollbar (
411422 controller: _horizontalScrollbarController,
412423 thumbVisibility: true ,
@@ -415,14 +426,15 @@ class DevToolsTableState<T> extends State<DevToolsTable<T>>
415426 controller: _horizontalScrollbarController,
416427 child: SelectionArea (
417428 child: SizedBox (
418- width: max (viewWidth, _tableWidthForOriginalColumns ),
429+ width: max (viewWidth, _currentTableWidth ),
419430 child: Column (
420431 crossAxisAlignment: CrossAxisAlignment .stretch,
421432 children: [
422433 if (showColumnGroupHeader)
423434 TableRow <T >.tableColumnGroupHeader (
424435 columnGroups: columnGroups,
425- columnWidths: adjustedColumnWidths,
436+ columnWidths: _columnWidths,
437+ onColumnResize: _handleColumnResize,
426438 sortColumn: sortColumn,
427439 sortDirection: tableUiState.sortDirection,
428440 secondarySortColumn:
@@ -436,7 +448,8 @@ class DevToolsTableState<T> extends State<DevToolsTable<T>>
436448 key: const Key ('Table header' ),
437449 columns: widget.tableController.columns,
438450 columnGroups: columnGroups,
439- columnWidths: adjustedColumnWidths,
451+ columnWidths: _columnWidths,
452+ onColumnResize: _handleColumnResize,
440453 sortColumn: sortColumn,
441454 sortDirection: tableUiState.sortDirection,
442455 secondarySortColumn:
0 commit comments