Skip to content

Repository files navigation

Note

Original repository: https://github.com/kechankrisna/flutter_responsive_table

The problem with the original repository is that it has not been updated since 2021, for this reason this repository was created in order to maintain an updated code and eliminate the warning message when compiling a flutter application.

xtable

Responsive Data table is a highly flexible tool built upon the foundations of progressive enhancement, that adds all of these advanced features to any flutter table.

Example

screenshot1

screenshot2

screenshot3

screenshot4

import'dart:math';
import'package:flutter/material.dart';
import'package:xtable/xtable.dart';
voidmain() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
classMyAppextendsStatelessWidget {
@overrideWidgetbuild(BuildContext context) {
returnMaterialApp(
title:'Flutter Demo',
debugShowCheckedModeBanner:false,
theme:ThemeData(
primarySwatch:Colors.blue,
visualDensity:VisualDensity.adaptivePlatformDensity,
),
initialRoute:'/',
routes: {
'/': (_) =>DataPage(),
},
);
}
}
classDataPageextendsStatefulWidget {
DataPage({Key? key}) :super(key: key);
@override_DataPageStatecreateState() =>_DataPageState();
}
class_DataPageStateextendsState<DataPage> {
lateList<DatatableHeader> _headers;
List<int> _perPages = [10, 20, 50, 100];
int _total =100;
int? _currentPerPage =10;
List<bool>? _expanded;
String? _searchKey ="id";
int _currentPage =1;
bool _isSearch =false;
List<Map<String, dynamic>> _sourceOriginal = [];
List<Map<String, dynamic>> _sourceFiltered = [];
List<Map<String, dynamic>> _source = [];
List<Map<String, dynamic>> _selecteds = [];
// ignore: unused_fieldString _selectableKey ="id";
String? _sortColumn;
bool _sortAscending =true;
bool _isLoading =true;
bool _showSelect =true;
var random =newRandom();
List<Map<String, dynamic>> _generateData({int n:100}) {
finalList source =List.filled(n, Random.secure());
List<Map<String, dynamic>> temps = [];
var i =1;
print(i);
// ignore: unused_local_variablefor (var data in source) {
temps.add({
"id": i,
"sku":"$i\000$i",
"name":"Product $i",
"category":"Category-$i",
"price": i *10.00,
"cost":"20.00",
"margin":"${i}0.20",
"in_stock":"${i}0",
"alert":"5",
"received": [i +20, 150]
});
i++;
}
return temps;
}
_initializeData() async {
_mockPullData();
}
_mockPullData() async {
_expanded =List.generate(_currentPerPage!, (index) =>false);
setState(() => _isLoading =true);
Future.delayed(Duration(seconds:3)).then((value) {
_sourceOriginal.clear();
_sourceOriginal.addAll(_generateData(n: random.nextInt(10000)));
_sourceFiltered = _sourceOriginal;
_total = _sourceFiltered.length;
_source = _sourceFiltered.getRange(0, _currentPerPage!).toList();
setState(() => _isLoading =false);
});
}
_resetData({start:0}) async {
setState(() => _isLoading =true);
var _expandedLen = _total - start < _currentPerPage!? _total - start : _currentPerPage;
Future.delayed(Duration(seconds:0)).then((value) {
_expanded =List.generate(_expandedLen asint, (index) =>false);
_source.clear();
_source = _sourceFiltered.getRange(start, start + _expandedLen).toList();
setState(() => _isLoading =false);
});
}
_filterData(value) {
setState(() => _isLoading =true);
try {
if (value ==""|| value ==null) {
_sourceFiltered = _sourceOriginal;
} else {
_sourceFiltered = _sourceOriginal.where((data) => data[_searchKey!].toString().toLowerCase().contains(value.toString().toLowerCase())).toList();
}
_total = _sourceFiltered.length;
var _rangeTop = _total < _currentPerPage!? _total : _currentPerPage!;
_expanded =List.generate(_rangeTop, (index) =>false);
_source = _sourceFiltered.getRange(0, _rangeTop).toList();
} catch (e) {
print(e);
}
setState(() => _isLoading =false);
}
@overridevoidinitState() {
super.initState();
/// set headers _headers = [
DatatableHeader(text:"ID", value:"id", show:true, sortable:true, textAlign:TextAlign.center),
DatatableHeader(
text:"Name",
value:"name",
show:true,
flex:2,
sortable:true,
editable:true,
textAlign:TextAlign.left,
format:DataTableFormat.date,
),
DatatableHeader(
text:"SKU",
value:"sku",
show:true,
sortable:true,
textAlign:TextAlign.center,
editable:true,
),
DatatableHeader(text:"Category", value:"category", show:true, sortable:true, textAlign:TextAlign.left),
DatatableHeader(text:"Price", value:"price", show:true, sortable:true, textAlign:TextAlign.left),
DatatableHeader(text:"Margin", value:"margin", show:true, sortable:true, textAlign:TextAlign.left),
DatatableHeader(text:"In Stock", value:"in_stock", show:true, sortable:true, textAlign:TextAlign.left),
DatatableHeader(text:"Alert", value:"alert", show:true, sortable:true, textAlign:TextAlign.left),
DatatableHeader(
text:"Received",
value:"received",
show:true,
sortable:false,
sourceBuilder: (value, row) {
List list =List.from(value);
returnContainer(
child:Column(
children: [
Container(
width:85,
child:LinearProgressIndicator(
value: list.first / list.last,
),
),
Text("${list.first} of ${list.last}")
],
),
);
},
textAlign:TextAlign.center),
];
_initializeData();
}
@overridevoiddispose() {
super.dispose();
}
@overrideWidgetbuild(BuildContext context) {
returnScaffold(
appBar:AppBar(
title:Text("RESPONSIVE DATA TABLE"),
actions: [
IconButton(
onPressed: _initializeData,
icon:Icon(Icons.refresh_sharp),
),
],
),
drawer:Drawer(
child:ListView(
children: [
ListTile(
leading:Icon(Icons.home),
title:Text("home"),
onTap: () {},
),
ListTile(
leading:Icon(Icons.storage),
title:Text("data"),
onTap: () {},
)
],
),
),
body:SingleChildScrollView(
child:Column(mainAxisAlignment:MainAxisAlignment.start, mainAxisSize:MainAxisSize.max, children: [
Container(
margin:EdgeInsets.all(10),
padding:EdgeInsets.all(0),
constraints:BoxConstraints(
maxHeight:700,
),
child:Card(
elevation:1,
shadowColor:Colors.black,
clipBehavior:Clip.none,
child:XDataTable(
title:TextButton.icon(
onPressed: () => {},
icon:Icon(Icons.add),
label:Text("new item"),
),
reponseScreenSizes: [ScreenSize.xs],
actions: [
if (_isSearch)
Expanded(
child:TextField(
decoration:InputDecoration(
hintText:'Enter search term based on '+ _searchKey!.replaceAll(newRegExp('[\\W_]+'), ' ').toUpperCase(),
prefixIcon:IconButton(
icon:Icon(Icons.cancel),
onPressed: () {
setState(() {
_isSearch =false;
});
_initializeData();
}),
suffixIcon:IconButton(icon:Icon(Icons.search), onPressed: () {})),
onSubmitted: (value) {
_filterData(value);
},
)),
if (!_isSearch)
IconButton(
icon:Icon(Icons.search),
onPressed: () {
setState(() {
_isSearch =true;
});
})
],
headers: _headers,
source: _source,
selecteds: _selecteds,
showSelect: _showSelect,
autoHeight:false,
dropContainer: (data) {
if (int.tryParse(data['id'].toString())!.isEven) {
returnText("is Even");
}
return_DropDownContainer(data: data);
},
onChangedRow: (value, header) {
/// print(value); /// print(header); },
onSubmittedRow: (value, header) {
/// print(value); /// print(header); },
onTabRow: (data) {
print(data);
},
onSort: (value) {
setState(() => _isLoading =true);
setState(() {
_sortColumn = value;
_sortAscending =!_sortAscending;
if (_sortAscending) {
_sourceFiltered.sort((a, b) => b["$_sortColumn"].compareTo(a["$_sortColumn"]));
} else {
_sourceFiltered.sort((a, b) => a["$_sortColumn"].compareTo(b["$_sortColumn"]));
}
var _rangeTop = _currentPerPage!< _sourceFiltered.length ? _currentPerPage!: _sourceFiltered.length;
_source = _sourceFiltered.getRange(0, _rangeTop).toList();
_searchKey = value;
_isLoading =false;
});
},
expanded: _expanded,
sortAscending: _sortAscending,
sortColumn: _sortColumn,
isLoading: _isLoading,
onSelect: (value, item) {
print("$value $item ");
if (value!) {
setState(() => _selecteds.add(item));
} else {
setState(() => _selecteds.removeAt(_selecteds.indexOf(item)));
}
},
onSelectAll: (value) {
if (value!) {
setState(() => _selecteds = _source.map((entry) => entry).toList().cast());
} else {
setState(() => _selecteds.clear());
}
},
footers: [
Container(
padding:EdgeInsets.symmetric(horizontal:15),
child:Text("Rows per page:"),
),
if (_perPages.isNotEmpty)
Container(
padding:EdgeInsets.symmetric(horizontal:15),
child:DropdownButton<int>(
value: _currentPerPage,
items: _perPages
.map((e) =>DropdownMenuItem<int>(
child:Text("$e"),
value: e,
))
.toList(),
onChanged: (dynamic value) {
setState(() {
_currentPerPage = value;
_currentPage =1;
_resetData();
});
},
isExpanded:false,
),
),
Container(
padding:EdgeInsets.symmetric(horizontal:15),
child:Text("$_currentPage - $_currentPerPage of $_total"),
),
IconButton(
icon:Icon(
Icons.arrow_back_ios,
size:16,
),
onPressed: _currentPage ==1?null: () {
var _nextSet = _currentPage - _currentPerPage!;
setState(() {
_currentPage = _nextSet >1? _nextSet :1;
_resetData(start: _currentPage -1);
});
},
padding:EdgeInsets.symmetric(horizontal:15),
),
IconButton(
icon:Icon(Icons.arrow_forward_ios, size:16),
onPressed: _currentPage + _currentPerPage!-1> _total
?null: () {
var _nextSet = _currentPage + _currentPerPage!;
setState(() {
_currentPage = _nextSet < _total ? _nextSet : _total - _currentPerPage!;
_resetData(start: _nextSet -1);
});
},
padding:EdgeInsets.symmetric(horizontal:15),
)
],
headerDecoration:BoxDecoration(color:Colors.grey, border:Border(bottom:BorderSide(color:Colors.red, width:1))),
selectedDecoration:BoxDecoration(
border:Border(bottom:BorderSide(color:Colors.green[300]!, width:1)),
color:Colors.green,
),
headerTextStyle:TextStyle(color:Colors.white),
rowTextStyle:TextStyle(color:Colors.green),
selectedTextStyle:TextStyle(color:Colors.white),
),
),
),
])),
);
}
}
class_DropDownContainerextendsStatelessWidget {
finalMap<String, dynamic> data;
const_DropDownContainer({Key? key, requiredthis.data}) :super(key: key);
@overrideWidgetbuild(BuildContext context) {
List<Widget> _children = data.entries.map<Widget>((entry) {
Widget w =Row(
children: [
Text(entry.key.toString()),
Spacer(),
Text(entry.value.toString()),
],
);
return w;
}).toList();
returnContainer(
/// height: 100, child:Column(
/// children: [ /// Expanded( /// child: Container( /// color: Colors.red, /// height: 50, /// )), /// ], children: _children,
),
);
}
}

Methods

 onSelect( bool selected, dynamic key);
onSelectAll( bool selected );
onTabRow( dynamic row );
onSort( dynamic value );

Properties

 title: Widget
headers: List<DatatableHeader>
actions: List<Widget>
source: List<Map<String, dynamic>>
selecteds: List<Map<String, dynamic>>
showSelect: bool
footers: List<Widget>
sortColumn: String
sortAscending: bool
isLoading: bool,
autoHeight: bool,

About

Responsive Data table is a highly flexible tool built upon the foundations of progressive enhancement, that adds all of these advanced features to any flutter table.

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages