Flutter DataTable Cross axis scroll

eye-catchDart and Flutter

When lots of data need to be shown on a screen, it requires a large view area that definitely doesn’t fit the restricted screen size, especially for mobile. It might be a design issue but there are some cases that we really want to place a data table there to show them. How can we make the view scrollable for both vertical and horizontal directions?

Go to this repository if you need the complete code.

flutter_samples/cross_axis_scroll.dart at main · yuto-yuto/flutter_samples
Contribute to yuto-yuto/flutter_samples development by creating an account on GitHub.
Sponsored links

Conclusion

It is actually simpler than I expected. The following two are the solutions.

  • Use SingleChildScrollView with Column or Row widget
  • Use two SingleChildScrollView

Let’s check the actual code.

Impossible to make a ListView cross axis scroll?

We sometimes need to handle long texts like this below.

final _data = const [
  "1 long long word assdaffadfadafdafdsfasfdafdsaffdazfdasfdafdfafafdfadfafds",
  "2 long long word assdaffadfadafdafdsfasfdafdsaffdazfdasfdafdfafafdfadfafds",
  "3 long long word assdaffadfadafdafdsfasfdafdsaffdazfdasfdafdfafafdfadfafds",
  "4 long long word assdaffadfadafdafdsfasfdafdsaffdazfdasfdafdfafafdfadfafds",
  "5 long long word assdaffadfadafdafdsfasfdafdsaffdazfdasfdafdfafafdfadfafds",
  "6 long long word assdaffadfadafdafdsfasfdafdsaffdazfdasfdafdfafafdfadfafds",
  "7 long long word assdaffadfadafdafdsfasfdafdsaffdazfdasfdafdfafafdfadfafds",
  "8 long long word assdaffadfadafdafdsfasfdafdsaffdazfdasfdafdfafafdfadfafds",
  "9 long long word assdaffadfadafdafdsfasfdafdsaffdazfdasfdafdfafafdfadfafds",
];

URL is I think a good example in the real world. It can easily be a long text. If we need to show them with ListView it looks like this.

listview-vertical

The text is wrapped and it looks not nice.

If Axishorizontal is set to scrollDirection, it looks like this.

listview-horizontal

This is also not nice.

The first attempt to solve this problem is wrapping ListView by SingleChildScrollView. ListView is scrollable in the vertical direction and SingleChildScrollView in the horizontal direction.

Widget _createCrossAxis2() {
  return SingleChildScrollView(
    scrollDirection: Axis.horizontal,
    child: _createListView(Axis.vertical),
  );
}

Widget _createListView(Axis direction) {
  return ListView.builder(
    scrollDirection: direction,
    itemCount: _data.length,
    itemBuilder: (context, index) => Card(child: Text(_data[index])),
  );
}

It doesn’t go well. The following error happens.

════════ Exception caught by rendering library ═════════════════════════════════
The following assertion was thrown during performResize():
Vertical viewport was given unbounded width.

Viewports expand in the cross axis to fill their container and constrain their children to match their extent in the cross axis. In this case, a vertical viewport was given an unlimited amount of horizontal space in which to expand.
The relevant error-causing widget was
ListView
lib\cross_axis_scroll.dart:61
When the exception was thrown, this was the stack

The ListView widget is wrapped in the following container in the example.

Widget _wrap(BuildContext context, Widget widget) {
  return Container(
    child: widget,
    height: 150,
    width: MediaQuery.of(context).size.width,
    padding: EdgeInsets.all(5),
    margin: EdgeInsets.all(5),
    decoration: BoxDecoration(
      border: Border.all(color: Colors.black, width: 1),
    ),
  );
}

Use two SingleChildScrollView with Column or Row

ListView couldn’t be used in SingleChildScrollView. The next try is use to SingleChildScrollView with Column or Row. Set a different direction to scrollDirection property for each widget.

Widget _createCrossAxis() {
  return SingleChildScrollView(
    scrollDirection: Axis.horizontal,
    child: SingleChildScrollView(
      scrollDirection: Axis.vertical,
      child: Column(
        children: _data.map((e) => Card(child: Text(e))).toList(),
      ),
    ),
  );
}

Cross axis scrollable DataTable

If the data contains column info, we might want to show it on the view. In this case, DataTable is a proper widget.

Widget _createDataTable() {
  final columns = List.generate(
    20,
    (index) => DataColumn(
      label: Text("column $index"),
    ),
  );

  final rows = List.generate(
    20,
    (rowIndex) => DataRow(
      cells: List.generate(
        20,
        (cellIndex) => DataCell(
          Text("data $cellIndex-$rowIndex"),
        ),
      ),
    ),
  );

  final dataTable = DataTable(
    columns: columns,
    rows: rows,
    border: TableBorder.all(color: Colors.grey),
  );

  return SingleChildScrollView(
    scrollDirection: Axis.horizontal,
    child: SingleChildScrollView(
      scrollDirection: Axis.vertical,
      child: dataTable,
    ),
  );
}

For web

To make it scrollable for the web version, the following code needs to be added.

class MyCustomScrollBehavior extends MaterialScrollBehavior {
  @override
  Set<PointerDeviceKind> get dragDevices => {
        PointerDeviceKind.touch,
        PointerDeviceKind.mouse,
      };
}

MaterialApp(
  scrollBehavior: MyCustomScrollBehavior(),
  ...

The widget is scrollable when dragging it by mouse. However, when I added a scroll bar it wasn’t scrollable by it. If I find the solution I will update.

If the scroll doesn’t work, try to add a controller respectively.

final _verticalScrollController = ScrollController();
final _horizontalScrollController = ScrollController();
...
SingleChildScrollView(
  controller: _horizontalScrollController,
  scrollDirection: Axis.horizontal,
  child: SingleChildScrollView(
    controller: _verticalScrollController,
    scrollDirection: Axis.vertical,
    child: dataTable,
  ),
);

Comments

  1. Biruk says:

    Have you found any solution to the web horizontal scrolling issue?

    • YutoYuto says:

      Hmm… I don’t remember it well but a controller is set in my private project. I added the code in the article to the bottom.
      Can you try it?

Copied title and URL