Related
I've been trying to add SingleChildScrollView to my code so that the whole page including Tabs and TabBarViews are scrolled together.
RenderFlex children have non-zero flex but incoming height constraints are unbounded
Horizontal viewport was given unbounded height.
These are the errors I encountered to make the whole page scrollable.
import 'package:buttons_tabbar/buttons_tabbar.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import '../widgets/style_grid.dart';
class StyleScreen extends StatefulWidget {
const StyleScreen({super.key});
#override
State<StyleScreen> createState() => _StyleScreenState();
}
class _StyleScreenState extends State<StyleScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Screen')),
body: SafeArea(
child: DefaultTabController(
length: 5,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ButtonsTabBar(
backgroundColor: Colors.grey[300],
radius: 16,
unselectedLabelStyle: TextStyle(color: Colors.black),
labelStyle: TextStyle(
color: Colors.orange, fontWeight: FontWeight.bold),
tabs: [
Tab(
icon: Icon(Icons.directions_car),
text: "All",
),
Tab(
icon: Icon(Icons.directions_transit),
text: "Cat1",
),
Tab(
icon: Icon(Icons.directions_transit),
text: "Cat2",
),
Tab(
icon: Icon(Icons.directions_transit),
text: "Cat3",
),
Tab(
icon: Icon(Icons.directions_transit),
text: "Cat4",
),
],
),
Expanded(
child: TabBarView(
physics: NeverScrollableScrollPhysics(),
children: <Widget>[
StyleGrid(),
StyleGrid(),
StyleGrid(),
StyleGrid(),
StyleGrid(),
],
),
),
],
),
),
),
);
}
}
I want to make the red area scrollable.
You can use NestedScrollView. It is better to use ListView.builder or CustomScrollView instead of using SingleChildScrollView on StyleGrid
class StyleScreen extends StatefulWidget {
const StyleScreen({super.key});
#override
State<StyleScreen> createState() => _StyleScreenState();
}
class _StyleScreenState extends State<StyleScreen>
with SingleTickerProviderStateMixin {
late final TabController controller = TabController(length: 5, vsync: this);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Screen')),
body: NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) => [
SliverToBoxAdapter(
child: tabBar(),
)
],
body: TabBarView(
controller: controller,
children: List.generate(
5, (index) => SingleChildScrollView(child: StyleGrid())),
),
),
);
}
ButtonsTabBar tabBar() {
return ButtonsTabBar(
controller: controller,
backgroundColor: Colors.grey[300],
radius: 16,
unselectedLabelStyle: TextStyle(color: Colors.black),
labelStyle: TextStyle(color: Colors.orange, fontWeight: FontWeight.bold),
tabs: [
Tab(
icon: Icon(Icons.directions_car),
text: "All",
),
Tab(
icon: Icon(Icons.directions_transit),
text: "Cat1",
),
Tab(
icon: Icon(Icons.directions_transit),
text: "Cat2",
),
Tab(
icon: Icon(Icons.directions_transit),
text: "Cat3",
),
Tab(
icon: Icon(Icons.directions_transit),
text: "Cat4",
),
],
);
}
}
Try wrapping it in the following class. This provides both vertical and horizontal scrolling.
class ScrollableWidget extends StatelessWidget {
final Widget child;
const ScrollableWidget({Key? key, required this.child}) : super(key: key);
#override
Widget build(BuildContext context) => SingleChildScrollView(
physics: const BouncingScrollPhysics(),
scrollDirection: Axis.horizontal,
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
scrollDirection: Axis.vertical,
child: child,
),
);
}
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'm learning flutter and dart by creating one of our wpf apps.
However, my DataTable throws exception:
======== Exception caught by rendering library =====================================================
A RenderFlex overflowed by 10.0 pixels on the right.
Did some reasearch and there were options to use Expanded, SingleChildScrollView etc. but still nothing helps.
If I make my data row strings smaller, everything is fine.
Widget code
class WorkList extends StatefulWidget {
WorkListState createState() => new WorkListState();
}
class WorkListState extends State<WorkList> with SingleTickerProviderStateMixin {
List<DataRow> _activities = [
DataRow(
cells: <DataCell>[
DataCell(Text('William')),
DataCell(Text('27')),
DataCell(Text('Associate Professor')),
DataCell(Text('Associate Professor1')),
DataCell(Text('Associate Professor1')),
]
)
];
void initState() {
super.initState();
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeRight,
DeviceOrientation.landscapeLeft,
]);
_controller = TabController(length: 2, vsync: this);
_controller.addListener(() {
_filterActivities();
});
}
#override
dispose(){
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
super.dispose();
}
TabBar get _tabBar => TabBar(
labelColor: Colors.white,
controller: _controller,
indicator: BoxDecoration(
color: orangeBackground,
),
tabs: [
Tab(text: 'SHOW CURRENT'),
Tab(text: 'SHOW ALL')
],
);
TabController _controller;
DataTable get _dataTable => DataTable(
columns: const <DataColumn>[
DataColumn(
label: Text(
'First Name',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Last Name',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Id',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Time',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Operator',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
],
rows: _activities,
);
void _filterActivities() {
setState(() {
_dataTable.rows.clear();
_activities.add(
DataRow(
cells: <DataCell>[
DataCell(Text('Some random')),
DataCell(Text('Some random1')),
DataCell(Text('Associate Professor1111111111dasdasds')),
DataCell(Text('Associate Professor1111111adssaa')),
DataCell(Text('TEEEEEEEEEEESTdsdasds')),
]
)
);
});
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
bottom: PreferredSize(
preferredSize: _tabBar.preferredSize,
child: ColoredBox(
color: midDarkBackground,
child: _tabBar,
),
),
title: Text('Worklist'),
),
body: Column(
children: <Widget>[
Expanded(
child: TabBarView(
controller: _controller,
physics: NeverScrollableScrollPhysics(), // disable swipe
children: [
_dataTable,
_dataTable
],
),
)
],
),
floatingActionButton: FloatingActionButton.extended(
label: Text('CREATE NEW PROCEDURE'),
backgroundColor: darkBackground,
foregroundColor: Colors.white,
),
),
);
}
}
In case you want to have a horizontal scroll for your table, just wrap the DataTable widget with SingleChildScrollView with scrollDirection: Axis.horizontal.
So your get _dataTable would be the following:
get _dataTable => SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
columns: const <DataColumn>[...],
rows: ...,
),
);
Result:
If you want to have a bidirectional scroll, wrap it with the second SingleChildScrollView like this:
get _dataTable => SingleChildScrollView(
scrollDirection: Axis.vertical,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
columns: const <DataColumn>[...],
rows: ...,
),
),
);
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: ...,
),