Make DataTable Scroll Bidirectional in Flutter
Asked Answered
D

7

49

How to make DataTable Scroll Bidirectional. I made the datatable scroll Horizontally but my list is large and unable to scroll down.

@override 
Widget build(BuildContext context) { 
    return Scaffold(
      appBar: AppBar(title: Text("Bills Receivable"),),
      body:SingleChildScrollView( 
        scrollDirection: Axis.horizontal, 
        child:
           DataTable(
          columns: <DataColumn>[
            DataColumn(label:Text("BNm",style: TextStyle(fontWeight: FontWeight.bold),)),
            DataColumn(label:Text("BDt",style: TextStyle(fontWeight: FontWeight.bold),)),
            DataColumn(label:Text("BPrty",style: TextStyle(fontWeight: FontWeight.bold),)),
            DataColumn(label:Text("BdueDt",style: TextStyle(fontWeight: FontWeight.bold),)),
            DataColumn(label:Text("Dys",style: TextStyle(fontWeight: FontWeight.bold),)),
            DataColumn(label:Text("BAmt",style: TextStyle(fontWeight: FontWeight.bold),)),
            DataColumn(label:Text("BPAmt",style: TextStyle(fontWeight: FontWeight.bold),)),
            DataColumn(label:Text("BBAmt",style: TextStyle(fontWeight: FontWeight.bold),))
          ], rows:  widget.rdata.bRecDtl.map((e)=>
            DataRow(
              cells:<DataCell>[
                DataCell(Text(e.bNm.toString())),
                DataCell(Text(e.bDt.toString())),
                DataCell(Text(e.bPrty.toString())),
                DataCell(Text(e.bdueDt.toString())),
                DataCell(Text(e.dys.toString())), 
                DataCell(Text(e.bAmt.toString())),
                DataCell(Text(e.bPAmt.toString())),
                DataCell(Text(e.bBAmt.toString())),
          ])).toList()
        ),
      ),
    );
  }
Durazzo answered 22/3, 2019 at 12:10 Comment(0)
I
99

Just add two SingleChildScrollViews:

child: SingleChildScrollView(
  scrollDirection: Axis.vertical,
    child: SingleChildScrollView(
      scrollDirection: Axis.horizontal,
      child: DataTable()
Interlining answered 17/8, 2019 at 19:41 Comment(3)
The scroll bar on the horizontal scrolling is not showing in futter web, how can I make it visible?Magnetron
Add a Scrollbar widget to each of the SingleChildScrollView, and set their individual scrollbarOrientation property to ScrollbarOrientation.right (for vertical) and ScrollbarOrientation.top respectively on the Scrollbar widgets.Faenza
While this works, see my solution below for a more modern solution :)Wennerholn
W
36

Newer and more efficient way after Flutter update:

As of the most recent Flutter update, you can also wrap your DataTable in an InteractiveViewer widget with the constrained property set to false. Unlike earlier solutions, this will allow you to scroll both horizontally and vertically at the same time. It is also less code and lets you zoom in/out of your DataTable.

Good luck!

Wennerholn answered 23/8, 2020 at 10:43 Comment(5)
For some reason InteractiveViewer affects performace in a big DataTable as compared to SingleChildScrollView. Yet InteractiveViewer has more and better usability. Hopefully the situation will change after a couple of versionsPresto
@Presto I assume the fast zooming in and out functionality affects adds some heavy load to the rendering process. But as long as the table is small and one wants some added functionality this widget is very nice!Integrant
@ExoticSquid Can you achieve this with flutter web? cuz it didn't work with me...Magnetron
@user10033434, I tested it on Flutter Web. It is working just fine.Clave
Tested on Flutter Web. The main issue is that scrolling is bind to the scale/zoom of the InteractiveViewer . Event with scaleEnable = false in the option, you won't be able to scroll vertically on your table. You will have to pan, which is not ideal on the web.Spooky
S
5

If anyone here is using a PaginatedDataTable or PaginatedDataTable2, you need to set the minWidth property in order to get the horizontal scroll working.

Saideman answered 3/1, 2022 at 7:17 Comment(0)
S
3

You can use two SingleChildScrollView or one InteractiveViewer.

But if you want two fixed scrollbars like this.

Bidirectional scrolling with fixed scrollbar

I used two SingleChildScrollView and adaptive_scrollbar v2.1.0 to get this result.

Source Code

import 'package:flutter/material.dart';
import 'package:adaptive_scrollbar/adaptive_scrollbar.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Bidirectional Scrollbars',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    final _verticalScrollController = ScrollController();
    final _horizontalScrollController = ScrollController();

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Container(
        height: 300,
        width: 700,
        child: AdaptiveScrollbar(
          underColor: Colors.blueGrey.withOpacity(0.3),
          sliderDefaultColor: Colors.grey.withOpacity(0.7),
          sliderActiveColor: Colors.grey,
          controller: _verticalScrollController,
          child: AdaptiveScrollbar(
            controller: _horizontalScrollController,
            position: ScrollbarPosition.bottom,
            underColor: Colors.blueGrey.withOpacity(0.3),
            sliderDefaultColor: Colors.grey.withOpacity(0.7),
            sliderActiveColor: Colors.grey,
            child: SingleChildScrollView(
              controller: _verticalScrollController,
              scrollDirection: Axis.vertical,
              child: SingleChildScrollView(
                controller: _horizontalScrollController,
                scrollDirection: Axis.horizontal,
                child: Padding(
                  padding: const EdgeInsets.only(right: 8.0, bottom: 16.0),
                  child: DataTable(
                    showCheckboxColumn: true,
                    columns: [
                      DataColumn(
                        label: Text('Name'),
                      ),
                      DataColumn(
                        label: Text('Year'),
                      ),
                      DataColumn(
                        label: Text('Year'),
                      ),
                      DataColumn(
                        label: Text('Year'),
                      ),
                      DataColumn(
                        label: Text('Year'),
                      ),
                      DataColumn(
                        label: Text('Name'),
                      ),
                      DataColumn(
                        label: Text('Year'),
                      ),
                      DataColumn(
                        label: Text('Year'),
                      ),
                      DataColumn(
                        label: Text('Year'),
                      ),
                      DataColumn(
                        label: Text('Year'),
                      ),
                    ],
                    rows: List<DataRow>.generate(
                      20,
                      (int index) => DataRow(
                        cells: <DataCell>[
                          DataCell(
                            Text('Row $index'),
                          ),
                          DataCell(
                            Text('Row $index'),
                          ),
                          DataCell(
                            Text('Row $index'),
                          ),
                          DataCell(
                            Text('Row $index'),
                          ),
                          DataCell(
                            Text('Row $index'),
                          ),
                          DataCell(
                            Text('Row $index'),
                          ),
                          DataCell(
                            Text('Row $index'),
                          ),
                          DataCell(
                            Text('Row $index'),
                          ),
                          DataCell(
                            Text('Row $index'),
                          ),
                          DataCell(
                            Text('Row $index'),
                          ),
                        ],
                        onSelectChanged: (bool? value) {},
                      ),
                    ),
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Stodder answered 12/1, 2022 at 22:23 Comment(1)
The flutter team announced that they are developing an official solution for this problem here's the video Preview: Two-dimensional scrolling in Flutter, and Flutter 3.13 is out and it has added TwoDimensional scrolling foundation, more info in What’s new in Flutter 3.13, and it is also updating data tables to use this new 2D Scrolling, with some other improvements, the progress can be seen in GitHub 2D Scrolling • flutter.Stodder
E
2

There is a new package maintained by the Flutter team that a 2 dimensional scrollable. It supports diagonal, vertical, and horizontal.

2D Scrollables

Eventful answered 29/12, 2023 at 13:19 Comment(0)
I
1

Try this:

SingleChildScrollView(
  physics: const BouncingScrollPhysics(),
  scrollDirection: Axis.vertical,
  child: ...,
),
Isologous answered 26/2, 2022 at 7:16 Comment(0)
S
0

I used to code this way, and it works fine:

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:followmeifucan/constants/style_constants.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';


class RankScreen extends StatefulWidget {

  static String id = 'ranks_screen';

  @override
  _RankScreenState createState() => _RankScreenState();
}

class _RankScreenState extends State<RankScreen> {
  final _firestore = Firestore.instance;

  @override
  void initState() {
    super.initState();
  }

  List<DataCell> _createCellsForElement(DocumentSnapshot document){
    Timestamp timestamp = document.data['when'];
    return <DataCell>[
      DataCell(Text(document.data['name'].toString())),
      DataCell(Text(document.data['level'].toString())),
      DataCell(Text(document.data['round'].toString())),
      DataCell(Text(document.data['hits_ok'].toString())),
      DataCell(Text(timestamp.toDate().toString().substring(0, 16))),
      DataCell(Icon(document.data['plataform'].toString() == 'Android' ? Icons.android : FontAwesomeIcons.apple))
    ];
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        backgroundColor: Colors.black,
        title: Row(
          children: <Widget>[
            Flexible(
              child: Hero(
                tag: 'logo',
                child: Container(
                  height: 30.0,
                  child: Image.asset('images/default_icon.png'),
                ),
              ),
            ),
            SizedBox(width: 10.0,),
            Text('HIGHSOCRES'),
          ],
        ),
      ),
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: SingleChildScrollView(
            scrollDirection: Axis.vertical,
            child: Column(
              children: <Widget>[
                StreamBuilder<QuerySnapshot>(
                stream:  _firestore.collection('ranks').snapshots(),
                builder: (context, snapshot){
                  List<DataRow> rankLines = new List<DataRow>();
                  if(snapshot.hasData){
                    final ranks = snapshot.data.documents;
                   for(var rankData in ranks){

                     rankLines.add(
                         DataRow(
                             cells: _createCellsForElement(rankData)
                         )
                     );
                   }
                  }
                  return Container(
                    child: SingleChildScrollView(
                      scrollDirection: Axis.horizontal,
                      child: DataTable(
                        sortAscending: true,
                        sortColumnIndex: 3,

                        columns: <DataColumn>[
                          DataColumn(label: Text('NAME', style: kRankLabelStyle,)),
                          DataColumn(label:Text('LEVEL', style: kRankLabelStyle,), numeric: true,),
                          DataColumn(label:Text('ROUND', style: kRankLabelStyle,), numeric: true,),
                          DataColumn(label:Text('HITS OK', style: kRankLabelStyle,), numeric: true,),
                          DataColumn(label:Text('WHEN', style: kRankLabelStyle,),),
                          DataColumn(label:Icon(Icons.phone_iphone)),
                        ],
                        rows: rankLines,
                      ),
                    ),
                  );
                },
              ),
              ]
            ),
          ),
        ),
      ),
    );
  }
}
Syzygy answered 25/5, 2020 at 22:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.