Search code examples
flutterdartdatatablescrollbar

Flutter Scrollbar in vertical and horizontal axis


I have two scrollbars in my flutter project for scrolling the data table. Here are the codes.

  Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
            child: Container(
                width: MediaQuery.of(context).size.width * (1 / 2),
                height: MediaQuery.of(context).size.height * (1 / 2),
                child: Scrollbar(
                    isAlwaysShown: true,
                    controller: _controllerOne,
                    child: SingleChildScrollView(
                        scrollDirection: Axis.vertical,
                        controller: _controllerOne,
                        child: Scrollbar(
                          controller: _controllerTwo,
                          isAlwaysShown: true,
                          child: SingleChildScrollView(
                            scrollDirection: Axis.horizontal,
                            controller: _controllerTwo,
                            child: DataTable(
                             ...
                            ),
                          ),
                        ))))));
  }

These two scrollbars are working correctly. But I can not see both of them at the same time. I mean if I write the Scrollbar with scrollDirection in vertical first it's like;

enter image description here

Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
            child: Container(
                width: MediaQuery.of(context).size.width * (1 / 2),
                height: MediaQuery.of(context).size.height * (1 / 2),
                child: Scrollbar(
                    isAlwaysShown: true,
                    controller: _controllerOne,
                    child: SingleChildScrollView(
                        **scrollDirection: Axis.vertical,**
                        controller: _controllerOne,
                        child: Scrollbar(
                          controller: _controllerTwo,
                          isAlwaysShown: true,
                          child: SingleChildScrollView(
                            **scrollDirection: Axis.horizontal,**
                            controller: _controllerTwo,
                            child: DataTable(
                             ...
                            ),
                          ),
                        ))))));
  }

if I write the Scrollbar with scrollDirection in horizontal first it's like;

enter image description here

Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
            child: Container(
                width: MediaQuery.of(context).size.width * (1 / 2),
                height: MediaQuery.of(context).size.height * (1 / 2),
                child: Scrollbar(
                    isAlwaysShown: true,
                    controller: _controllerOne,
                    child: SingleChildScrollView(
                        **scrollDirection: Axis.horizontal,**
                        controller: _controllerOne,
                        child: Scrollbar(
                          controller: _controllerTwo,
                          isAlwaysShown: true,
                          child: SingleChildScrollView(
                            **scrollDirection: Axis.vertical,**
                            controller: _controllerTwo,
                            child: DataTable(
                             ...
                            ),
                          ),
                        ))))));
  }

When I scroll till the end I can see the other scrollbar for both directions. But when the table is shown I need to see them both at the same time.

Is there any solution for this?


Solution

  • Old suggestion:

    I suggest you to use InteractiveViewer.builder for its easier navigation when presenting large data on a table

    New suggestion: (in regard to latest flutter version)

    Flutter has recently released the widget TableView.builder from their official package two_dimensional_scrollables which has built in scrollbar support for both axis (enabled by default for desktop build) and has a similar structure when creating DataTable so you'll have no trouble migrating. Here's a great example on how to use it.

    However, to address your question you can actually do it without relying on any packages and please note that those I mentioned before are more performant as they support lazy loading.

    What you'll have to do is nest the vertical and horizontal SingleChildScrollView and wrap it with two ScrollBar then attach a ScrollController respectively and use the notificationPredicate property on the inner ScrollBar.

    enter image description here

    class MyWidget extends StatelessWidget {
      final List<int> shades = [100, 200, 300, 400, 500, 600, 700, 800, 900];
      final ScrollController _horizontal = ScrollController(),
          _vertical = ScrollController();
    
      @override
      Widget build(BuildContext context) {
        return SizedBox(
          height: 500,
          width: 400,
          child: Scrollbar(
            controller: _vertical,
            thumbVisibility: true,
            trackVisibility: true,
            child: Scrollbar(
              controller: _horizontal,
              thumbVisibility: true,
              trackVisibility: true,
              notificationPredicate: (notif) => notif.depth == 1,
              child: SingleChildScrollView(
                controller: _vertical,
                child: SingleChildScrollView(
                  controller: _horizontal,
                  scrollDirection: Axis.horizontal,
                  child: DataTable(
                    columns: const <DataColumn>[
                      DataColumn(label: Text('Preview')),
                      DataColumn(label: Text('Color')),
                      DataColumn(label: Text('Shade')),
                    ],
                    rows: [
                      for (var color in Colors.primaries)
                        for (var shade in shades)
                          DataRow(
                            cells: [
                              DataCell(Container(
                                  height: 20, width: 50, color: color[shade])),
                              DataCell(Text(color.toString())),
                              DataCell(Text('$shade')),
                            ],
                          ),
                    ],
                  ),
                ),
              ),
            ),
          ),
        );
      }
    }