DataTable with Paginate Firestore - flutter

How we can create DataTable from Firestore collection and keep only one column on the top of ListView ?, with below code column is present for each row, also if the collection contain 1000 docs the column should not be hidden when user scoll
Below is my code:
#override
Widget build(BuildContext context) {
return FirestoreQueryBuilder<Map<String, dynamic>>(
pageSize: 5,
query: FirebaseFirestore.instance.collection('users'),
builder: (context, snapshot, _) {
return ListView.builder(
scrollDirection: Axis.vertical,
itemCount: snapshot.docs.length,
itemBuilder: (context, index) {
if (snapshot.hasMore && index + 1 == snapshot.docs.length) {
snapshot.fetchMore();
}
final user = snapshot.docs[index].data();
return DataTable(
columns: const <DataColumn>[
DataColumn(
label: Text('User Name'),
),
DataColumn(
label: Text('User email'),
),
DataColumn(
label: Text('User Id'),
),
],
rows: [
DataRow(
cells: [
DataCell(
Text(user['name']),
),
DataCell(
Text(user['email']),
),
DataCell(
Text(user['id']),
),
],
),
],
);
},
);
},
);
}
NOTE : I'm using flutterfire_ui to paginate collection
Thanks

You are not required to use ListView while using DataTable class because it also required List<Widget>.
You can use PaginatedDataTable class for this purpose. Instead of using DataTable just use PaginatedDataTable
#override
Widget build(BuildContext context) {
return FirestoreQueryBuilder<Map<String, dynamic>>(
pageSize: 5,
query: FirebaseFirestore.instance.collection('users'),
builder: (context, snapshot, _) {
return PaginatedDataTable(
columns: const <DataColumn>[
DataColumn(
label: Text('User Name'),
),
DataColumn(
label: Text('User email'),
),
DataColumn(
label: Text('User Id'),
),
],
source: MyData(snapshot.docs),
onPageChanged: snapshot.fetchMore,
);
},
);
}
}
// The "soruce" of the table
class MyData extends DataTableSource {
MyData(this._data);
final List<dynamic> _data;
#override
bool get isRowCountApproximate => false;
#override
int get rowCount => _data.length;
#override
int get selectedRowCount => 0;
#override
DataRow getRow(int index) {
return DataRow(cells: [
DataCell(Text(_data[index].data()['name'])),
DataCell(Text(_data[index].data()["email"])),
DataCell(Text(_data[index].data()["id"])),
]);
}
}

I'm able to achieve this using data_table_2 package
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
IconButton(
iconSize: 30.0,
icon: const Icon(CupertinoIcons.search),
onPressed: () {},
)
],
title: const Text('DataTable2 Firebase'),
elevation: 0,
centerTitle: true,
),
body: Column(
children: [
SizedBox(
height: 60,
child: DataTable2(
columns: const <DataColumn>[
DataColumn(
label: Text('name'),
),
DataColumn(label: Text('age'), numeric: true),
DataColumn(label: Text('score'), numeric: true),
],
rows: const [],
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(bottom: 50),
child: FirestoreQueryBuilder<Map<String, dynamic>>(
pageSize: 10,
query: FirebaseFirestore.instance.collection('users'),
builder: (context, snapshot, _) {
if (snapshot.hasMore) {
snapshot.fetchMore();
}
return DataTable2(
columnSpacing: 0,
horizontalMargin: 12,
bottomMargin: 10,
minWidth: 50,
headingRowHeight: 0,
columns: const <DataColumn>[
DataColumn(
label: Text(''),
),
DataColumn(label: Text(''), numeric: true),
DataColumn(label: Text(''), numeric: true),
],
rows: snapshot.docs
.map((e) => DataRow(cells: [
DataCell(
Text(
e.data()['name'],
),
),
DataCell(
Text(
e.data()['age'].toString(),
),
),
DataCell(
Text(
e.data()['score'].toString(),
),
),
]))
.toList(),
);
},
),
),
),
const SizedBox(
height: 50,
)
],
),
);
}

Related

How to make data column clickable in Flutter?

I would like to make a datacolumn clickable, such that when the user clicks on it, an overlay opens up. I've tried to add a IconButton, but i doesn't work. Does anyone has an idea how to do that properly in flutter?
Code:
SizedBox(
height: 500,
width: double.infinity,
child: DataTable2(
minWidth: 600,
columnSpacing: defaultPadding,
columns: const [
DataColumn(
IconButton( <------------- This doesn't work
icon: Icons.abs,
onPressed: () {},
),
label: Text("Car ID"),
),
DataColumn(label: Text("Date")),
DataColumn(label: Text("Avg. Speed")),
DataColumn(label: Text("Video File")),
],
rows: List.generate(demoRecentFiles.length,
(index) => recentFileDataRow(demoRecentFiles[index])),
)),
To make data column clickable, just wrap your label with GestureDetector() widget.
DataColumn(
label: Expanded(
child: GestureDetector(onTap: (){
print('Hey');
}, child: Text(
'Name',
style: TextStyle(fontStyle: FontStyle.italic),
)),
)
,
),
label attribute of DataColumn as well as positional parameter of DataCell in DataRow can be any widget. So feel free to use any clickable widget there. Here's working snippet with IconButton like you wanted it to be. You can try it in Dartpad.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: MyStatelessWidget(),
),
);
}
}
class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget({super.key});
#override
Widget build(BuildContext context) {
return DataTable(
columns: <DataColumn>[
DataColumn(
label: Expanded(
child: IconButton(
icon: const Icon(Icons.favorite),
onPressed: () {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('It works'),
actions: <Widget>[
TextButton(
child: const Text('Ok'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
},
),
),
),
],
rows: const <DataRow>[
DataRow(
cells: <DataCell>[
DataCell(Text('1')),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('2')),
],
),
],
);
}
}

The argument type 'Object?' can't be assigned to the parameter type 'QuerySnapshot<Object?>'. Facing while developing a Flutter Website

Trying to develop an Admin Grocery Website, but faced an error and can't fix it as I am new to flutter. I am getting an error here rows: vendorsDetailsRow(snapshot.data) and the error is The argument type 'Object?' can't be assigned to the parameter type 'QuerySnapshot<Object?>'.
So here is my code
import 'package:admin_app_grocery/services/firebase_services.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class VendorDataTable extends StatelessWidget {
const VendorDataTable({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
FirebaseServices services = FirebaseServices();
return StreamBuilder(
stream:
services.vendors.orderBy('shopName', descending: true).snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text("Something Went Wrong");
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
showBottomBorder: true,
dataRowHeight: 60,
headingRowColor: MaterialStateProperty.all(Colors.grey[200]),
columns: const [
DataColumn(
label: Text("Active/Inactive"),
),
DataColumn(
label: Text("Top Picked"),
),
DataColumn(
label: Text("Shop Name"),
),
DataColumn(
label: Text("Rating"),
),
DataColumn(
label: Text("Total Sales"),
),
DataColumn(
label: Text("Mobile"),
),
DataColumn(
label: Text("Email"),
),
DataColumn(
label: Text("View Details"),
),
],
rows: vendorsDetailsRow(snapshot.data),
),
);
},
);
}
List<DataRow> vendorsDetailsRow(QuerySnapshot snapshot) {
List<DataRow> newList =
snapshot.docs.map((DocumentSnapshot documentSnapshot) {
return DataRow(cells: [
DataCell(IconButton(
onPressed: () {},
icon: documentSnapshot['accVerified']
? const Icon(
Icons.check_circle,
color: Colors.blue,
)
: const Icon(
Icons.remove_circle,
color: Colors.red,
),
)),
DataCell(IconButton(
onPressed: () {},
icon: documentSnapshot['isTopPicked']
? const Icon(
Icons.check_circle,
color: Colors.blue,
)
: const Icon(null),
)),
DataCell(documentSnapshot['shopName']),
DataCell(Row(
children: const [Icon(Icons.star), Text("3.5")],
)),
const DataCell(Text("20,000")),
DataCell(Text(documentSnapshot['mobile'])),
DataCell(Text(documentSnapshot['email'])),
DataCell(IconButton(
icon: const Icon(Icons.remove_red_eye_outlined),
onPressed: () {},
))
]);
}).toList();
return newList;
}
}
Specify the type in the StreamBuilder like so
return StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(
stream:
services.vendors.orderBy('shopName', descending: true).snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text("Something Went Wrong");
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
showBottomBorder: true,
dataRowHeight: 60,
headingRowColor: MaterialStateProperty.all(Colors.grey[200]),
columns: const [
DataColumn(
label: Text("Active/Inactive"),
),
DataColumn(
label: Text("Top Picked"),
),
DataColumn(
label: Text("Shop Name"),
),
DataColumn(
label: Text("Rating"),
),
DataColumn(
label: Text("Total Sales"),
),
DataColumn(
label: Text("Mobile"),
),
DataColumn(
label: Text("Email"),
),
DataColumn(
label: Text("View Details"),
),
],
rows: vendorsDetailsRow(snapshot.data),
),
);
},
);
}
And update vendorsDetailsRow to:
List<DataRow> vendorsDetailsRow(QuerySnapshot<Map<String, dynamic>> snapshot) {
List<DataRow> newList =
snapshot.docs.map((DocumentSnapshot x) {
final documentSnapshot = x.data();
return DataRow(cells: [
DataCell(IconButton(
onPressed: () {},
icon: documentSnapshot['accVerified']
? const Icon(
Icons.check_circle,
color: Colors.blue,
)
: const Icon(
Icons.remove_circle,
color: Colors.red,
),
)),
DataCell(IconButton(
onPressed: () {},
icon: documentSnapshot['isTopPicked']
? const Icon(
Icons.check_circle,
color: Colors.blue,
)
: const Icon(null),
)),
DataCell(documentSnapshot['shopName']),
DataCell(Row(
children: const [Icon(Icons.star), Text("3.5")],
)),
const DataCell(Text("20,000")),
DataCell(Text(documentSnapshot['mobile'])),
DataCell(Text(documentSnapshot['email'])),
DataCell(IconButton(
icon: const Icon(Icons.remove_red_eye_outlined),
onPressed: () {},
))
]);
}).toList();
return newList;
}
Fixed the code it was just a very minor mistake just changed this line of code i.e DataCell(documentSnapshot['shopName']), to DataCell(Text(documentSnapshot['shopName'])), . Earlier it was returning a String now its returning a Text Widget , this fixed my issue.
Modified Code is :
import 'package:admin_app_grocery/services/firebase_services.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class VendorDataTable extends StatelessWidget {
const VendorDataTable({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
FirebaseServices services = FirebaseServices();
return StreamBuilder<QuerySnapshot>(
stream:
services.vendors.orderBy('shopName', descending: true).snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text("Something Went Wrong");
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
showBottomBorder: true,
dataRowHeight: 60,
headingRowColor: MaterialStateProperty.all(Colors.grey[200]),
columns: const [
DataColumn(
label: Text("Active/Inactive"),
),
DataColumn(
label: Text("Top Picked"),
),
DataColumn(
label: Text("Shop Name"),
),
DataColumn(
label: Text("Rating"),
),
DataColumn(
label: Text("Total Sales"),
),
DataColumn(
label: Text("Mobile"),
),
DataColumn(
label: Text("Email"),
),
DataColumn(
label: Text("View Details"),
),
],
rows: vendorsDetailsRow(snapshot.data),
),
);
},
);
}
List<DataRow> vendorsDetailsRow(QuerySnapshot? snapshot) {
List<DataRow> newList =
snapshot!.docs.map((DocumentSnapshot documentSnapshot) {
return DataRow(cells: [
DataCell(IconButton(
onPressed: () {},
icon: documentSnapshot['accVerified']
? const Icon(
Icons.check_circle,
color: Colors.blue,
)
: const Icon(
Icons.remove_circle,
color: Colors.red,
),
)),
DataCell(IconButton(
onPressed: () {},
icon: documentSnapshot['isTopPicked']
? const Icon(
Icons.check_circle,
color: Colors.blue,
)
: const Icon(null),
)),
DataCell(Text(documentSnapshot['shopName'])),
DataCell(Row(
children: const [Icon(Icons.star), Text("3.5")],
),),
const DataCell(Text("20,000")),
DataCell(Text(documentSnapshot['mobile'])),
DataCell(Text(documentSnapshot['email'])),
DataCell(IconButton(
icon: const Icon(Icons.remove_red_eye_outlined),
onPressed: () {},
))
]);
}).toList();
return newList;
}
}

Flutter web - Always show horizontal and vertical scrollbar in a DataTable

I have a DataTable and I'm trying to make it always show the horizontal and vertical scrollbars.
I managed to always show the vertical scrollbar, but the horizontal scrollbar only shows when I scroll down to the bottom.
Here's my code:
final _scrollController = ScrollController();
final _scrollController2 = ScrollController();
#override
Widget build(BuildContext context) {
return Container(
height: 300,
width: 400,
child: Scrollbar(
controller: _scrollController,
isAlwaysShown: true,
child: SingleChildScrollView(
controller: _scrollController,
scrollDirection: Axis.vertical,
child: Scrollbar(
controller: _scrollController2,
isAlwaysShown: true,
child: SingleChildScrollView(
controller: _scrollController2,
scrollDirection: Axis.horizontal,
child: DataTable(
showCheckboxColumn: true,
columns: [
DataColumn(
label: Text('Name'),
),
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'),
),
],
onSelectChanged: (bool? value) {},
),
),
),
),
),
),
),
);
}
I'm using Flutter 2.2.3 (Channel stable).
I tried a lot of things, but I think I finally got it.
here's the final result: Bidirectional scrolling with fixed scrollbar
I used 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) {},
),
),
),
),
),
),
),
),
),
);
}
}

Flutter | What is the best way to designed a Data Table to fit the full screen in Vertical View?

What are the best views in Flutter or best practice to implement a better UX?
The Vertical text View looks too small:
SingleChildScrollView bodyData() =>
SingleChildScrollView(
scrollDirection: Axis.vertical,
padding: EdgeInsets.all(1.2),
child: FittedBox(fit:BoxFit.fill,
child:
DataTable(
sortColumnIndex: 1,
sortAscending: true,
columns: <DataColumn>[
DataColumn(
label: Text("Company"),
onSort: (_, __) {
setState(() {
widget.photos.sort((a, b) =>
a.data["quote"]["companyName"]
.compareTo(b.data["quote"]["companyName"]));
});
},
),
DataColumn(
label: Text("ttmDivRate"),
numeric: true,
onSort: (_,__) {
setState(() {
widget.photos.sort((a, b) =>
a.data["stats"]["ttmDividendRate"]
.compareTo(b.data["stats"]["ttmDividendRate"]));
View code:
The Horizontal view is fine:
Try wrapping the DataTable with another SingleChildScrollView and set the scrollDirection to Axis.horizontal. That way you can increase the text size, and still be able to scroll horizontally to see everything.
Here is an example:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.black54,
appBar: AppBar(),
body: Center(
child: Container(
color: Colors.white,
height: 130,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
columns: <DataColumn>[
DataColumn(
label: Text('Column 1'),
),
DataColumn(
label: Text('Column 2'),
),
DataColumn(
label: Text('Column 3'),
),
DataColumn(
label: Text('Column 4'),
),
DataColumn(
label: Text('Column 5'),
),
DataColumn(
label: Text('Column 6'),
),
],
rows: <DataRow>[
DataRow(
cells: <DataCell>[
DataCell(Text('Data')),
DataCell(Text('Data')),
DataCell(Text('Data')),
DataCell(Text('Data')),
DataCell(Text('Data')),
DataCell(Text('Data')),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('Data')),
DataCell(Text('Data')),
DataCell(Text('Data')),
DataCell(Text('Data')),
DataCell(Text('Data')),
DataCell(Text('Data')),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('Data')),
DataCell(Text('Data')),
DataCell(Text('Data')),
DataCell(Text('Data')),
DataCell(Text('Data')),
DataCell(Text('Data')),
],
),
],
),
),
),
),
),
),
);
}
}
We can use a FittedBox to fit height of the data table and let it scroll horizontally.
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child:
FittedBox(
fit: BoxFit.fitHeight,
child: DataTable(
//sortAscending: false,
//sortColumnIndex: 2,
columns: <DataColumn>[
DataColumn(
label: Text("No",textScaleFactor: 1,),
numeric: false,
),
DataColumn(
label: Text("Name",textScaleFactor: 1,),
numeric: false,
),
DataColumn(
label: Text("Delay\nMinutes",textScaleFactor: 1,),
numeric: false,
),
],
rows: TrainDelayedList.map((t) =>
DataRow(cells: [
DataCell(
Text(t.trainno,textScaleFactor: 1,),
),
DataCell(
Text(t.trainname,maxLines: 3,textScaleFactor: 1,)
),
DataCell(
Text(t.delayatstn.toString(),textScaleFactor: 1,)
),
])).toList()
),
))

Make DataTable Scroll Bidirectional in Flutter

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()
),
),
);
}
Just add two SingleChildScrollViews:
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable()
Newer and easier way.
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!
If anyone here is using a PaginatedDataTable or PaginatedDataTable2, you need to set the minWidth property in order to get the horizontal scroll working.
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) {},
),
),
),
),
),
),
),
),
),
);
}
}
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,
),
),
);
},
),
]
),
),
),
),
);
}
}
I published this package for bidirectional tables: easy_table
Try this:
SingleChildScrollView(
physics: const BouncingScrollPhysics(),
scrollDirection: Axis.vertical,
child: ...,
),