You don’t need to install any package to resize the column size of DataTable. However, DataTable
and DataColumn
don’t have such a property to update column size.
How can we implement it without any extra package?
This is the sample video.
You can find the completed code in my GitHub repo.
Set a fixed width to DataTable widget
A scrollbar is necessary if the column width is bigger than the window width. So, let’s add the DataTable
with Scrollbar
.
I wanted to create an app for a Desktop, so the window size is changeable. To make the DataTable width match the window size, MediaQuery.of(context).size.width
is set to the Container width.
import 'package:flutter/material.dart';
class TableColumnResize extends StatefulWidget {
@override
State<StatefulWidget> createState() => _TableColumnResize();
}
class _TableColumnResize extends State<TableColumnResize> {
final verticalScrollController = ScrollController();
final horizontalScrollController = ScrollController();
@override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(
title: Text("Table Column Resize"),
),
body: Center(
child: Container(
width: MediaQuery.of(context).size.width, // match the window width
height: 200,
decoration: BoxDecoration(
border: Border.all(
color: Colors.black,
width: 3,
),
),
margin: const EdgeInsets.all(15.0),
child: Scrollbar(
thumbVisibility: true,
trackVisibility: true, // make the scrollbar easy to see
controller: verticalScrollController,
child: Scrollbar(
thumbVisibility: true,
trackVisibility: true,
controller: horizontalScrollController,
notificationPredicate: (notif) => notif.depth == 1,
child: SingleChildScrollView(
controller: verticalScrollController,
scrollDirection: Axis.vertical,
child: SingleChildScrollView(
controller: horizontalScrollController,
scrollDirection: Axis.horizontal,
child: _resizableColumnWidth(), // DataTable creation
),
),
),
),
),
),
);
}
}
I recorded two videos with/without the setting so that you can easily choose your preference according to your needs.
Without width: MediaQuery.of(context).size.width,
With width: MediaQuery.of(context).size.width,
Check the following post too if you want to know about cross axis scroll deeper.
Make the column resizable
Add Positioned and GestureDetector
As mentioned, DataTable
and DataColumn
don’t provide a property to hook column drag event. We somehow need to add GestureDetector
to the DataColumn
. To achieve this, we use Stack
and Positioned
. Multiple widgets can be used in the same area if we use Stack
.
Let’s see the code.
Widget _resizableColumnWidth() {
return DataTable(
columns: [
DataColumn(
label: Stack(
children: [
Container(
child: Text('Column 1'), // Header text
width: columnWidth,
constraints: BoxConstraints(minWidth: 100),
),
Positioned(
right: 0,
child: GestureDetector(
child: Container(
width: 10,
height: 10,
decoration: BoxDecoration(
color: Colors.blue.withOpacity(1),
shape: BoxShape.circle,
),
),
),
)
],
),
),
DataColumn(label: Text("Column 2")),
DataColumn(label: Text("Column 3")),
],
rows: ...,
}
DataTable.columns
requires DataColumn
. We can’t set another widget. So, we use Stack
in it. Then, we need to pile up multiple widgets there. Container
is the main widget for the column. On the Container, put an additional widget that supports dragging. I’ve put a circle there.
The appearance looks like this.
Define drag start and dragging behavior
The next step is to add the behavior to onPanStart
and onPanUpdate
to GestureDetector
.
double columnWidth = 200;
double initX = 0;
Widget _resizableColumnWidth() {
return DataTable(
columns: [
DataColumn(
label: Stack(
children: [
Container(...),
Positioned(
right: 0,
child: GestureDetector(
onPanStart: (details) {
setState(() {
initX = details.globalPosition.dx;
});
},
onPanUpdate: (details) {
final increment = details.globalPosition.dx - initX;
final newWidth = columnWidth + increment;
setState(() {
initX = details.globalPosition.dx;
columnWidth = newWidth;
});
},
child: ...
),
)
],
),
),
DataColumn(label: Text("Column 2")),
DataColumn(label: Text("Column 3")),
],
rows: List.generate(
20,
(index) => DataRow(
cells: [
DataCell(
ConstrainedBox(
constraints: BoxConstraints(maxWidth: columnWidth),
child: Text(...),
),
),
DataCell(Text("Column2: Row index $index")),
DataCell(Text("Column3: Row index $index")),
],
),
));
}
We need to calculate the column width when the circle is being dragged. When it’s tapped/clicked, we need to know the global position of X. Then, we can calculate how far the circle widget is moved from the base point X.
setState
is called in onPanUpdate
but if you want to update the column width only when the action ends, move setState
to onPanEnd
columnWidth
is used not only for DataColumn
but also for DataCell
in DataRow
. If the DataCell
doesn’t use ConstrainedBox
, only header row width changes but not the data rows.
Set the minimum column width
You might not want to make the column invisible. Set your desired column size to columnWidth
when the result becomes smaller than it.
final minimumColumnWidth = 50.0;
onPanUpdate: (details) {
final increment = details.globalPosition.dx - initX;
final newWidth = columnWidth + increment;
setState(() {
initX = details.globalPosition.dx;
columnWidth = newWidth > minimumColumnWidth
? newWidth
: minimumColumnWidth;
});
},
Clipping DataRow text
Likewise, you might want to clip the data row text when the column width is smaller than the text length.
rows: List.generate(
20,
(index) => DataRow(
cells: [
DataCell(
ConstrainedBox(
constraints: BoxConstraints(maxWidth: columnWidth),
child: Text(
"Column1: Row index $index: long text 1234567890 1234567890 1234567890 1234567890",
overflow: TextOverflow.ellipsis,
maxLines: 1,
softWrap: false,
),
),
),
DataCell(Text("Column2: Row index $index")),
DataCell(Text("Column3: Row index $index")),
],
),
));
Without clipping
With clipping
Comments