Related
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,
)
],
),
);
}
I'm having a bit of a problem with Flutter's widget placement. Here's the code:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
final _scrollController = ScrollController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
controller: _scrollController,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
controller: _scrollController,
child: DataTable(
//Rows and columns filled with random data
columns: const <DataColumn>[
DataColumn(
label: Text(
'Name',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Age',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Role',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Name',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Age',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Role',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
],
rows: const <DataRow>[
DataRow(
cells: <DataCell>[
DataCell(Text('Sarah')),
DataCell(Text('19')),
DataCell(Text('Student')),
DataCell(Text('Sarah')),
DataCell(Text('19')),
DataCell(Text('Student')),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('Janine')),
DataCell(Text('43')),
DataCell(Text('Professor')),
DataCell(Text('Sarah')),
DataCell(Text('19')),
DataCell(Text('Student')),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('William')),
DataCell(Text('27')),
DataCell(Text('Associate Professor')),
DataCell(Text('Sarah')),
DataCell(Text('19')),
DataCell(Text('Student')),
],
),
],
),
),
//How to make this widget be at the bottom?
const Text('This widget should be at the bottom'),
],
),
),
);
}
}
I have a DataTable and a Text widget, wrapped with a Column and two SingleChilScrollViews in order to have bidirectional scrolling (the table is larger than the screen). I want to place the Text widget on the bottom of the screen. Tinkering with the code, I can do it pretty easily by giving the Column widget a parent Container, and fixing its size to my screen. However, how would I go about sizing it dynamically? I've tried using MediaQuery but the result was not that accurate. This "fix" can be found below:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
final _scrollController = ScrollController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
controller: _scrollController,
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
controller: _scrollController,
child: DataTable(
//Rows and columns filled with random data
columns: const <DataColumn>[
DataColumn(
label: Text(
'Name',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Age',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Role',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Name',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Age',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Role',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
],
rows: const <DataRow>[
DataRow(
cells: <DataCell>[
DataCell(Text('Sarah')),
DataCell(Text('19')),
DataCell(Text('Student')),
DataCell(Text('Sarah')),
DataCell(Text('19')),
DataCell(Text('Student')),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('Janine')),
DataCell(Text('43')),
DataCell(Text('Professor')),
DataCell(Text('Sarah')),
DataCell(Text('19')),
DataCell(Text('Student')),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('William')),
DataCell(Text('27')),
DataCell(Text('Associate Professor')),
DataCell(Text('Sarah')),
DataCell(Text('19')),
DataCell(Text('Student')),
],
),
],
),
),
//How to make this widget be at the bottom?
const Text('This widget should be at the bottom'),
],
),
),
),
);
}
}
Any ideas or suggestions on how to tackle this issue? Thanks.
EDIT
Using the text in the bottomNavigationBar of the Scaffold works as well, however it doens't seem an appropriate solution.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
final _scrollController = ScrollController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: ListView(
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
//Rows and columns filled with random data
columns: const <DataColumn>[
DataColumn(
label: Text(
'Name',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Age',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Role',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Name',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Age',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Role',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
],
rows: const <DataRow>[
DataRow(
cells: <DataCell>[
DataCell(Text('Sarah')),
DataCell(Text('19')),
DataCell(Text('Student')),
DataCell(Text('Sarah')),
DataCell(Text('19')),
DataCell(Text('Student')),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('Janine')),
DataCell(Text('43')),
DataCell(Text('Professor')),
DataCell(Text('Sarah')),
DataCell(Text('19')),
DataCell(Text('Student')),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('William')),
DataCell(Text('27')),
DataCell(Text('Associate Professor')),
DataCell(Text('Sarah')),
DataCell(Text('19')),
DataCell(Text('Student')),
],
),
],
),
),
],
),
bottomNavigationBar: const Text('Is at the bottom - doesnt seem right'),
);
}
}
i think This might get you resolved your Error.
Code:
Scaffold(
body:SizedBox(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Column(
children: [
SizedBox(
height: MediaQuery.of(context).size.height-100,
width: MediaQuery.of(context).size.width,
child: SingleChildScrollView(
child: Column(
children: [
///// your Scrollabel Data Table will be here
for(int i=0;i<20;i++)
ListTile(
title: Text('Tile $i'),
),
],
),
),
),
const Align(
alignment: Alignment.bottomCenter,
child: Text('Hello I am at the Bottom'),
),
],
),
) ,)
You can use a SingleChildScrollView inside a Listview to achive that.
ListView(
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Column(
children: [
//Your table goes here
],
),
),
Text(
//Your bottom text goes here,
),
],
),
I found a more elegant solution while tinkering with my code and the provided answers. I wrapped the Scaffold's body in a Column and used 2 expanded widgets: one for my bottom widget and another one for the ScrollViews and Table. Here's the final body:
body: Column(
children: [
Expanded(
flex: 9,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
controller: _scrollController,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
controller: _scrollController,
child: DataTable(
columns: _controller.columns,
rows: _controller.rows,
),
),
),
),
Expanded(
child: Total(_controller.total),
),
],
),
did you try to use BottomSheetNavigtor?
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) {},
),
),
),
),
),
),
),
),
),
);
}
}
I want to create a viewport that could be horizontally and vertically scrolled. I am able to achieve this by using two nested SingleScrollChildView. The problem is that the horizontal scrollbar is not attached to the viewport as desired but rather it is attached/present at the bottom vertically which is expected. Is there a way to achieve such a behavior? Please be gentle as I am still learning this framework. Here is the code snippet:
Scrollbar(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Scrollbar(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: CustomViewPort(),
),
),
),
),
Like this?
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) {},
),
),
),
),
),
),
),
),
),
);
}
}
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: ...,
),